├── .env ├── .github └── workflows │ ├── awake.yml │ └── deploy.yml ├── .idea ├── modules.xml ├── whats-api.iml └── workspace.xml ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── api ├── controllers │ ├── authentication.go │ ├── message.go │ └── server.go ├── middlewares │ └── middlewares.go ├── models │ └── message_model.go ├── responses │ └── responses.go ├── router │ ├── router.go │ └── routes │ │ ├── authentication_routes.go │ │ ├── message_routes.go │ │ ├── routes.go │ │ └── server_routes.go ├── server.go └── utils │ ├── file_utility │ └── download_file.go │ ├── json_utility │ └── json_utility.go │ ├── security │ └── token.go │ └── shell_commands │ └── shell_commands.go ├── bin └── whats_api ├── build.sh ├── config └── config.go ├── deploy_to_heroku.sh ├── go.mod ├── go.sum ├── heroku.yml ├── main.go └── vendor ├── github.com ├── alexellis │ └── go-execute │ │ ├── LICENSE │ │ └── pkg │ │ └── v1 │ │ └── exec.go ├── gorilla │ └── mux │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── go.mod │ │ ├── middleware.go │ │ ├── mux.go │ │ ├── regexp.go │ │ ├── route.go │ │ └── test_helpers.go └── skip2 │ └── go-qrcode │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── bitset │ └── bitset.go │ ├── encoder.go │ ├── go.mod │ ├── qrcode.go │ ├── reedsolomon │ ├── gf2_8.go │ ├── gf_poly.go │ └── reed_solomon.go │ ├── regular_symbol.go │ ├── symbol.go │ └── version.go └── modules.txt /.env: -------------------------------------------------------------------------------- 1 | API_PORT=9000 -------------------------------------------------------------------------------- /.github/workflows/awake.yml: -------------------------------------------------------------------------------- 1 | name: Heroku Awake 2 | 3 | on: 4 | schedule: 5 | - cron: "*/30 * * * *" 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: mikeesto/heroku-awake@1.0.0 13 | with: 14 | URL: "https://whats-api-cli.herokuapp.com" -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy container to heroku. 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Build, Push and Release a Docker container to Heroku. 14 | uses: gonuit/heroku-docker-deploy@v1.3.2 15 | with: 16 | email: jose.daniell@outlook.com 17 | heroku_api_key: ${{ secrets.HEROKU_API_KEY }} 18 | heroku_app_name: whats-api-cli 19 | dockerfile_directory: ./ 20 | dockerfile_name: Dockerfile 21 | process_type: web -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/whats-api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | true 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ### Build binary from official Go image 2 | FROM golang:stretch as build 3 | COPY . /src 4 | WORKDIR /src 5 | RUN CGO_ENABLED=0 GOOS=linux go build -mod=vendor -o /whats-api . 6 | 7 | ### Put the binary onto Heroku image 8 | FROM heroku/heroku:18 9 | COPY --from=build /whats-api /whats-api 10 | CMD ["/whats-api"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 José Daniel Araújo Pacheco Filho 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bin/whats_api -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # whatsapp-api 2 | 3 | Connect and send messages as a whatsapp client sending REST requests, **this is only for experimental use**. 4 | 5 | ## Summary 6 | 7 | > [1. How to use locally](#how-to-use-locally)
8 | > [2. How to use on Heroku Instance](#how-to-use-on-heroku-instance)
9 | > [3. Testing request end-points](#testing-the-requests-end-points)
10 | > [4. Contribute](#your-contribution-is-important)
11 | 12 | ## How to use locally 13 | 14 | **Requirements:** 15 | 16 | - Have the go installed on your machine. 17 | 18 | ##### Download the repository 19 | 20 | `git clone https://github.com/jsdaniell/whats-api.git` 21 | 22 | ##### Get dependencies 23 | 24 | `go mod download || go mod vendor` 25 | 26 | ##### Running 27 | 28 | `go run main.go` 29 | 30 | ## How to use on Heroku Instance 31 | 32 | **Requirements:** 33 | 34 | - Have the [heroku cli](https://devcenter.heroku.com/articles/heroku-cli) installed on your machine and logged account. 35 | 36 | ##### Download the repository 37 | 38 | `git clone https://github.com/jsdaniell/whats-api.git` 39 | 40 | ##### Create your project on your heroku account 41 | 42 | `heroku create {name-of-your-project}` 43 | 44 | ##### Deploying to heroku 45 | 46 | `heroku container:push -a {name-of-your-project} web`
47 | 48 | `heroku container:release -a {name-of-your-project} web` 49 | 50 | ##### Testing 51 | 52 | `curl https://{name-of-your-project}.herokuapp.com/` 53 | 54 | *The response must be: Server Running...* 55 | 56 | See logs with `heroku logs --tail` on the root folder of project. 57 | 58 | ## Testing the requests end-points 59 | 60 | GET: https://localhost:9000/getQrCode
61 | returns the QRCode PNG on the response.
62 | 63 | `curl --location --request GET 'http://localhost:9000/getQrCode' \ 64 | --header 'Authorization: anyIDString'` 65 | 66 | GET: https://localhost:9000/disconnect
67 | disconnect the actually logged session on the server.
68 | 69 | `curl --location --request GET 'http://localhost:9000/disconnect' \ 70 | --header 'Authorization: anyIDString'` 71 | 72 | POST: https://localhost:9000/sendMessage
73 | send a message to some number.
74 | 75 | `curl --location --request POST 'http://localhost:9000/sendMessage' \ 76 | --header 'Authorization: anyIDString' \ 77 | --header 'Content-Type: application/json' \ 78 | --data-raw '{ 79 | "number": "558599999999", 80 | "message": "message" 81 | }'` 82 | 83 | ### Your contribution is important! 84 | 85 | Feel free to contribute and create suggestions about this tool, you can submit a PR anytime you want! 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /api/controllers/authentication.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "github.com/jsdaniell/whats_api/api/responses" 7 | "github.com/jsdaniell/whats_api/api/utils/shell_commands" 8 | "github.com/skip2/go-qrcode" 9 | "net/http" 10 | "os/exec" 11 | ) 12 | 13 | func Connect(w http.ResponseWriter, r *http.Request) { 14 | auth := r.Header.Get("Authorization") 15 | if len(auth) == 0{ 16 | responses.ERROR(w, http.StatusBadRequest, errors.New("missing 'Authorization' on header")) 17 | return 18 | } 19 | 20 | // create new WhatsApp connection 21 | cmd := exec.Command("./whats-cli", "connect", auth) 22 | 23 | stdout, _ := cmd.StdoutPipe() 24 | err := cmd.Start() 25 | if err != nil { 26 | responses.ERROR(w, http.StatusInternalServerError, err) 27 | } 28 | 29 | scanner := bufio.NewScanner(stdout) 30 | for scanner.Scan() { 31 | m := scanner.Text() 32 | 33 | png, err := qrcode.Encode(m, qrcode.Medium, 256) 34 | if err != nil { 35 | responses.ERROR(w, http.StatusBadRequest, err) 36 | } 37 | 38 | _, err = w.Write(png) 39 | if err != nil { 40 | responses.ERROR(w, http.StatusBadRequest, err) 41 | } 42 | 43 | return 44 | } 45 | err = cmd.Wait() 46 | if err != nil { 47 | responses.ERROR(w, http.StatusInternalServerError, err) 48 | } 49 | } 50 | 51 | func Disconnect(w http.ResponseWriter, r *http.Request) { 52 | auth := r.Header.Get("Authorization") 53 | if len(auth) == 0{ 54 | responses.ERROR(w, http.StatusBadRequest, errors.New("missing 'Authorization' on header")) 55 | } 56 | 57 | err := shell_commands.ExecuteShellCommand("./whats-cli", "version") 58 | if err != nil { 59 | responses.ERROR(w, http.StatusInternalServerError, err) 60 | } 61 | 62 | 63 | err = shell_commands.ExecuteShellCommand("./whats-cli", "disconnect", auth) 64 | if err != nil { 65 | responses.ERROR(w, http.StatusInternalServerError, err) 66 | } 67 | 68 | _, err = w.Write([]byte("disconnected")) 69 | if err != nil { 70 | responses.ERROR(w, http.StatusInternalServerError, err) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /api/controllers/message.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "errors" 7 | "github.com/jsdaniell/whats_api/api/models" 8 | "github.com/jsdaniell/whats_api/api/responses" 9 | "io/ioutil" 10 | "net/http" 11 | "os/exec" 12 | ) 13 | 14 | func Message(w http.ResponseWriter, r *http.Request) { 15 | auth := r.Header.Get("Authorization") 16 | if len(auth) == 0{ 17 | responses.ERROR(w, http.StatusBadRequest, errors.New("missing 'Authorization' on header")) 18 | } 19 | 20 | var messageBody models.MessageModel 21 | 22 | bytes, err := ioutil.ReadAll(r.Body) 23 | if err != nil { 24 | responses.ERROR(w, http.StatusUnprocessableEntity, err) 25 | return 26 | } 27 | 28 | err = json.Unmarshal(bytes, &messageBody) 29 | if err != nil { 30 | responses.ERROR(w, http.StatusUnprocessableEntity, err) 31 | return 32 | } 33 | 34 | cmd := exec.Command( "./whats-cli", "send", messageBody.Number, messageBody.Message, auth) 35 | 36 | stdout, _ := cmd.StdoutPipe() 37 | err = cmd.Start() 38 | if err != nil { 39 | responses.ERROR(w, http.StatusInternalServerError, err) 40 | } 41 | 42 | scanner := bufio.NewScanner(stdout) 43 | for scanner.Scan() { 44 | m := scanner.Text() 45 | 46 | _, err = w.Write([]byte(m)) 47 | if err != nil { 48 | responses.ERROR(w, http.StatusInternalServerError, err) 49 | } 50 | return 51 | } 52 | err = cmd.Wait() 53 | if err != nil { 54 | responses.ERROR(w, http.StatusInternalServerError, err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /api/controllers/server.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "net/http" 4 | 5 | // Handle the root / route to return feedback about the server to request like "Server Running..." 6 | func ServerRunning(w http.ResponseWriter, r *http.Request) { 7 | w.Write([]byte("Server Running...")) 8 | } 9 | -------------------------------------------------------------------------------- /api/middlewares/middlewares.go: -------------------------------------------------------------------------------- 1 | // Middlewares to handle generic handles of request. 2 | package middlewares 3 | 4 | import ( 5 | "fmt" 6 | "github.com/jsdaniell/whats_api/api/responses" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | // Validate Access Control to API and deal with CORS request. 12 | func SetMiddlewareLogger(next http.HandlerFunc) http.HandlerFunc { 13 | 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | 16 | // TODO: When in production setup to https://website 17 | 18 | w.Header().Set("Access-Control-Allow-Origin", "*") 19 | 20 | w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") 21 | w.Header().Set("Access-Control-Allow-Methods", "*") 22 | 23 | log.Println("%s %s%s %s", r.Method, r.Host, r.RequestURI, r.Proto) 24 | 25 | if (*r).Method == "OPTIONS" { 26 | return 27 | } 28 | 29 | next(w, r) 30 | } 31 | } 32 | 33 | // Middleware to test authorization header parameter on closed routes. 34 | func SetMiddlewareJSON(next http.HandlerFunc, openRoute bool) http.HandlerFunc { 35 | return func(w http.ResponseWriter, r *http.Request) { 36 | 37 | if !openRoute { 38 | auth := r.Header.Get("Authorization") 39 | if auth == "" { 40 | responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("missing authorization token")) 41 | return 42 | } 43 | } 44 | 45 | next(w, r) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /api/models/message_model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type MessageModel struct { 4 | Number string `json:"number"` 5 | Message string `json:"message"` 6 | } 7 | -------------------------------------------------------------------------------- /api/responses/responses.go: -------------------------------------------------------------------------------- 1 | // Response utility to returns JSON error or JSON response to the request. 2 | package responses 3 | 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | func JSON(w http.ResponseWriter, statusCode int, data interface{}) { 11 | w.WriteHeader(statusCode) 12 | 13 | w.Header().Set("Content-Type", "application/json") 14 | 15 | err := json.NewEncoder(w).Encode(data) 16 | if err != nil { 17 | fmt.Fprintf(w, "%s", err.Error()) 18 | } 19 | } 20 | 21 | func ERROR(w http.ResponseWriter, statusCode int, err error) { 22 | if err != nil { 23 | JSON(w, statusCode, struct { 24 | Error string `json:"error"` 25 | }{ 26 | Error: err.Error(), 27 | }) 28 | return 29 | } 30 | 31 | JSON(w, http.StatusBadRequest, nil) 32 | } 33 | -------------------------------------------------------------------------------- /api/router/router.go: -------------------------------------------------------------------------------- 1 | // Router setup. 2 | package router 3 | 4 | import ( 5 | "github.com/gorilla/mux" 6 | "github.com/jsdaniell/whats_api/api/router/routes" 7 | ) 8 | 9 | func New() *mux.Router { 10 | r := mux.NewRouter().StrictSlash(true) 11 | 12 | return routes.SetupRoutesWithMiddlewares(r) 13 | } 14 | -------------------------------------------------------------------------------- /api/router/routes/authentication_routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/jsdaniell/whats_api/api/controllers" 5 | "net/http" 6 | ) 7 | 8 | var authenticationRoutes = []Route{ 9 | { 10 | URI: "/getQrCode", 11 | Method: http.MethodGet, 12 | Handler: controllers.Connect, 13 | Open: true, 14 | }, 15 | { 16 | URI: "/disconnect", 17 | Method: http.MethodGet, 18 | Handler: controllers.Disconnect, 19 | Open: true, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /api/router/routes/message_routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/jsdaniell/whats_api/api/controllers" 5 | "net/http" 6 | ) 7 | 8 | var messageRoutes = []Route{ 9 | { 10 | URI: "/sendMessage", 11 | Method: http.MethodPost, 12 | Handler: controllers.Message, 13 | Open: true, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /api/router/routes/routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | "github.com/jsdaniell/whats_api/api/middlewares" 6 | "net/http" 7 | ) 8 | 9 | // Route struct provides the structure of route 10 | type Route struct { 11 | URI string 12 | Method string 13 | Handler func(http.ResponseWriter, *http.Request) 14 | Open bool 15 | } 16 | 17 | // Load all routes and setting with middlewares to receive requests. 18 | func Load() []Route { 19 | routes := [][]Route{ 20 | serverRoutes, 21 | authenticationRoutes, 22 | messageRoutes, 23 | } 24 | 25 | var joinedRoutes []Route 26 | 27 | for _, r := range routes { 28 | joinedRoutes = append(joinedRoutes, r...) 29 | } 30 | 31 | return joinedRoutes 32 | } 33 | 34 | // SetupRoutesWithMiddlewares Using middlewares on handle of route. 35 | func SetupRoutesWithMiddlewares(r *mux.Router) *mux.Router { 36 | 37 | for _, route := range Load() { 38 | r.HandleFunc(route.URI, middlewares.SetMiddlewareLogger( 39 | middlewares.SetMiddlewareJSON(route.Handler, route.Open))).Methods(route.Method, "OPTIONS") 40 | } 41 | 42 | return r 43 | } 44 | -------------------------------------------------------------------------------- /api/router/routes/server_routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/jsdaniell/whats_api/api/controllers" 5 | "net/http" 6 | ) 7 | 8 | // Generic server routes with their controller. 9 | var serverRoutes = []Route{ 10 | { 11 | URI: "/", 12 | Method: http.MethodGet, 13 | Handler: controllers.ServerRunning, 14 | Open: true, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /api/server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jsdaniell/whats_api/api/router" 6 | "github.com/jsdaniell/whats_api/config" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | // Run initialize the server. 12 | func Run() { 13 | config.Load() 14 | fmt.Printf("Listening... localhost:%d", config.PORT) 15 | listen(config.PORT) 16 | } 17 | 18 | // Configure listen port. 19 | func listen(port int) { 20 | r := router.New() 21 | log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), r)) 22 | } 23 | -------------------------------------------------------------------------------- /api/utils/file_utility/download_file.go: -------------------------------------------------------------------------------- 1 | package file_utility 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | // DownloadFile will download a url to a local file. It's efficient because it will 10 | // write as it downloads and not load the whole file into memory. 11 | func DownloadFile(filepath string, url string) error { 12 | // Get the data 13 | resp, err := http.Get(url) 14 | if err != nil { 15 | return err 16 | } 17 | defer resp.Body.Close() 18 | 19 | // Create the file 20 | out, err := os.Create(filepath) 21 | if err != nil { 22 | return err 23 | } 24 | defer out.Close() 25 | 26 | // Write the body to file 27 | _, err = io.Copy(out, resp.Body) 28 | return err 29 | } -------------------------------------------------------------------------------- /api/utils/json_utility/json_utility.go: -------------------------------------------------------------------------------- 1 | package json_utility 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // Utility function to lower case JSON pure structs, generally used on response of a request inside controllers. 8 | func StructToLowerCaseJson(data interface{}) (interface{}, error) { 9 | 10 | bytes, err := json.Marshal(data) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | var r interface{} 16 | 17 | err = json.Unmarshal(bytes, &r) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return r, nil 23 | } 24 | -------------------------------------------------------------------------------- /api/utils/security/token.go: -------------------------------------------------------------------------------- 1 | package security 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | "errors" 7 | "github.com/jsdaniell/whats_api/api/models" 8 | "time" 9 | ) 10 | 11 | const layout = "2006-01-02T15:04:05.000Z" 12 | 13 | // CreateToken Creates the token stored on each user, receives a generic string to generate token based on string. 14 | func CreateToken(s string) models.Token { 15 | h := sha1.New() 16 | h.Write([]byte(s)) 17 | 18 | hashes := hex.EncodeToString(h.Sum(nil)) 19 | actual := time.Now() 20 | date := actual.Format(time.RFC3339) 21 | 22 | var t = models.Token{ 23 | CreatedAt: date, 24 | Token: hashes, 25 | } 26 | 27 | return t 28 | } 29 | 30 | func ValidateToken(tk models.Token) error { 31 | 32 | t, err := time.Parse(time.RFC3339, tk.CreatedAt) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | dayLater := t.Add(24 * time.Hour) 38 | 39 | if !t.Before(dayLater) { 40 | return errors.New("token expired") 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /api/utils/shell_commands/shell_commands.go: -------------------------------------------------------------------------------- 1 | package shell_commands 2 | 3 | import ( 4 | "fmt" 5 | execute "github.com/alexellis/go-execute/pkg/v1" 6 | ) 7 | 8 | func ExecuteSh(file, projectName string) { 9 | cmd := execute.ExecTask{ 10 | Command: "sh", 11 | Args: []string{file}, 12 | StreamStdio: false, 13 | Cwd: projectName, 14 | } 15 | 16 | res, err := cmd.Execute() 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | fmt.Printf("output: %s", res.Stderr) 22 | } 23 | 24 | func ExecuteShellCommand(command string, args ...string) error { 25 | cmd := execute.ExecTask{ 26 | Command: command, 27 | Args: args, 28 | StreamStdio: true, 29 | } 30 | 31 | 32 | res, err := cmd.Execute() 33 | if err != nil { 34 | return err 35 | } 36 | 37 | fmt.Printf("output: %s", res.Stdout) 38 | fmt.Printf("output: %s", res.Stderr) 39 | 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /bin/whats_api: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdaniell/whats-api/ccf63342e46c59085afcd23287a46e585240a964/bin/whats_api -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | go mod vendor 2 | go build -o bin/whats_api -v . -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | var ( 10 | PORT = 9000 11 | ) 12 | 13 | func Load() { 14 | var err error 15 | 16 | PORT, err = strconv.Atoi(os.Getenv("PORT")) 17 | if err != nil { 18 | log.Println(err) 19 | PORT = 9000 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deploy_to_heroku.sh: -------------------------------------------------------------------------------- 1 | go mod vendor 2 | echo "Type the commit message: " 3 | read -r commitMessage 4 | git add . 5 | git commit -am "${commitMessage}" 6 | heroku container:push -a whats-api-cli web 7 | heroku container:release -a whats-api-cli web -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jsdaniell/whats_api 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f // indirect 7 | github.com/Rhymen/go-whatsapp v0.1.1 // indirect 8 | github.com/alexellis/go-execute v0.0.0-20201205082949-69a2cde04f4f 9 | github.com/aws/aws-sdk-go v1.38.1 // indirect 10 | github.com/davecgh/go-spew v1.1.1 // indirect 11 | github.com/golang/snappy v0.0.3 // indirect 12 | github.com/gorilla/mux v1.8.0 13 | github.com/joho/godotenv v1.3.0 // indirect 14 | github.com/klauspost/compress v1.11.12 // indirect 15 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 16 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect 17 | go.mongodb.org/mongo-driver v1.5.0 // indirect 18 | golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 // indirect 19 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 20 | golang.org/x/text v0.3.5 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= 2 | github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= 5 | github.com/Rhymen/go-whatsapp v0.1.1 h1:OK+bCugQcr2YjyYKeDzULqCtM50TPUFM6LvQtszKfcw= 6 | github.com/Rhymen/go-whatsapp v0.1.1/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk= 7 | github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= 8 | github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= 9 | github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw= 10 | github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM= 11 | github.com/alexellis/go-execute v0.0.0-20201205082949-69a2cde04f4f h1:5auqirFmPvQPu2Cq8gSKPyT6l7KYr9UniEbmvOBiWXM= 12 | github.com/alexellis/go-execute v0.0.0-20201205082949-69a2cde04f4f/go.mod h1:zfRbgnPVxXCSpiKrg1CE72hNUWInqxExiaz2D9ppTts= 13 | github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= 14 | github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= 15 | github.com/aws/aws-sdk-go v1.38.1 h1:dVtNY7+5CtiRfAbEVxm5NgL5Xol8AMNoAf6TkyhDq1I= 16 | github.com/aws/aws-sdk-go v1.38.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 17 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 19 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 21 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 22 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 23 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= 24 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= 25 | github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= 26 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 27 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 28 | github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= 29 | github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 30 | github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 31 | github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= 32 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= 33 | github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= 34 | github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= 35 | github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= 36 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= 37 | github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= 38 | github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= 39 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= 40 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 41 | github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 42 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 43 | github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 44 | github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= 45 | github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= 46 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 47 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 48 | github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= 49 | github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= 50 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 51 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 52 | github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= 53 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 54 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 55 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 56 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 57 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 58 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 59 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 60 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 61 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 62 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 63 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 64 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 65 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 66 | github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= 67 | github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 68 | github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= 69 | github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 70 | github.com/klauspost/compress v1.11.12 h1:famVnQVu7QwryBN4jNseQdUKES71ZAOnB6UQQJPZvqk= 71 | github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 72 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 73 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 74 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 75 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 76 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 77 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 78 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= 79 | github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= 80 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 81 | github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= 82 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 83 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 84 | github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 85 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 86 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 87 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 88 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 89 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 90 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 91 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 92 | github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 93 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 94 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 95 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 96 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 97 | github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= 98 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= 99 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= 100 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 101 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 102 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 103 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 104 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 105 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 106 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 107 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 108 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= 109 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 110 | github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= 111 | github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 112 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 113 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 114 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= 115 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= 116 | go.mongodb.org/mongo-driver v1.5.0 h1:REddm85e1Nl0JPXGGhgZkgJdG/yOe6xvpXUcYK5WLt0= 117 | go.mongodb.org/mongo-driver v1.5.0/go.mod h1:boiGPFqyBs5R0R5qf2ErokGRekMfwn+MqKaUyHs7wy0= 118 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 119 | golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 120 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 121 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 123 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= 124 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 125 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 126 | golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 h1:gzMM0EjIYiRmJI3+jBdFuoynZlpxa2JQZsolKu09BXo= 127 | golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 128 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 129 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 130 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 131 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 132 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 133 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 134 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 135 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 136 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 138 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 139 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= 140 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 141 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 142 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 143 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 144 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 145 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 146 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 149 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 150 | golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 151 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 152 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= 153 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 154 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 155 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 156 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 157 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 158 | golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= 159 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 160 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 161 | golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 162 | golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 163 | golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 164 | golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 165 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 166 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 167 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 168 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 169 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 170 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 171 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 172 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | # heroku.yml 2 | build: 3 | docker: 4 | web: Dockerfile -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jsdaniell/whats_api/api" 6 | "github.com/jsdaniell/whats_api/api/utils/file_utility" 7 | "github.com/jsdaniell/whats_api/api/utils/shell_commands" 8 | ) 9 | 10 | func main() { 11 | err := file_utility.DownloadFile("whats-cli.tar.gz", "https://github.com/jsdaniell/whats-cli/releases/download/v1.2.1/whats-cli_1.2.1_linux_amd64.tar.gz") 12 | if err != nil { 13 | fmt.Println(err) 14 | } 15 | 16 | err = shell_commands.ExecuteShellCommand("tar", "-zxvf", "whats-cli.tar.gz") 17 | if err != nil { 18 | fmt.Println(err) 19 | } 20 | 21 | api.Run() 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/alexellis/go-execute/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Inlets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/alexellis/go-execute/pkg/v1/exec.go: -------------------------------------------------------------------------------- 1 | package execute 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | ) 11 | 12 | type ExecTask struct { 13 | Command string 14 | Args []string 15 | Shell bool 16 | Env []string 17 | Cwd string 18 | 19 | // Stdin connect a reader to stdin for the command 20 | // being executed. 21 | Stdin io.Reader 22 | 23 | // StreamStdio prints stdout and stderr directly to os.Stdout/err as 24 | // the command runs. 25 | StreamStdio bool 26 | 27 | // PrintCommand prints the command before executing 28 | PrintCommand bool 29 | } 30 | 31 | type ExecResult struct { 32 | Stdout string 33 | Stderr string 34 | ExitCode int 35 | } 36 | 37 | func (et ExecTask) Execute() (ExecResult, error) { 38 | argsSt := "" 39 | if len(et.Args) > 0 { 40 | argsSt = strings.Join(et.Args, " ") 41 | } 42 | 43 | if et.PrintCommand { 44 | fmt.Println("exec: ", et.Command, argsSt) 45 | } 46 | 47 | var cmd *exec.Cmd 48 | 49 | if et.Shell { 50 | var args []string 51 | if len(et.Args) == 0 { 52 | startArgs := strings.Split(et.Command, " ") 53 | script := strings.Join(startArgs, " ") 54 | args = append([]string{"-c"}, fmt.Sprintf("%s", script)) 55 | 56 | } else { 57 | script := strings.Join(et.Args, " ") 58 | args = append([]string{"-c"}, fmt.Sprintf("%s %s", et.Command, script)) 59 | 60 | } 61 | 62 | cmd = exec.Command("/bin/bash", args...) 63 | } else { 64 | if strings.Index(et.Command, " ") > 0 { 65 | parts := strings.Split(et.Command, " ") 66 | command := parts[0] 67 | args := parts[1:] 68 | cmd = exec.Command(command, args...) 69 | 70 | } else { 71 | cmd = exec.Command(et.Command, et.Args...) 72 | } 73 | } 74 | 75 | cmd.Dir = et.Cwd 76 | 77 | if len(et.Env) > 0 { 78 | overrides := map[string]bool{} 79 | for _, env := range et.Env { 80 | key := strings.Split(env, "=")[0] 81 | overrides[key] = true 82 | cmd.Env = append(cmd.Env, env) 83 | } 84 | 85 | for _, env := range os.Environ() { 86 | key := strings.Split(env, "=")[0] 87 | 88 | if _, ok := overrides[key]; !ok { 89 | cmd.Env = append(cmd.Env, env) 90 | } 91 | } 92 | } 93 | if et.Stdin != nil { 94 | cmd.Stdin = et.Stdin 95 | } 96 | 97 | stdoutBuff := bytes.Buffer{} 98 | stderrBuff := bytes.Buffer{} 99 | 100 | var stdoutWriters io.Writer 101 | var stderrWriters io.Writer 102 | 103 | if et.StreamStdio { 104 | stdoutWriters = io.MultiWriter(os.Stdout, &stdoutBuff) 105 | stderrWriters = io.MultiWriter(os.Stderr, &stderrBuff) 106 | } else { 107 | stdoutWriters = &stdoutBuff 108 | stderrWriters = &stderrBuff 109 | } 110 | 111 | cmd.Stdout = stdoutWriters 112 | cmd.Stderr = stderrWriters 113 | 114 | startErr := cmd.Start() 115 | 116 | if startErr != nil { 117 | return ExecResult{}, startErr 118 | } 119 | 120 | exitCode := 0 121 | execErr := cmd.Wait() 122 | if execErr != nil { 123 | if exitError, ok := execErr.(*exec.ExitError); ok { 124 | 125 | exitCode = exitError.ExitCode() 126 | } 127 | } 128 | 129 | return ExecResult{ 130 | Stdout: string(stdoutBuff.Bytes()), 131 | Stderr: string(stderrBuff.Bytes()), 132 | ExitCode: exitCode, 133 | }, nil 134 | } 135 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of gorilla/mux authors for copyright purposes. 2 | # 3 | # Please keep the list sorted. 4 | 5 | Google LLC (https://opensource.google.com/) 6 | Kamil Kisielk 7 | Matt Silverlock 8 | Rodrigo Moraes (https://github.com/moraes) 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 mux implements a request router and dispatcher. 7 | 8 | The name mux stands for "HTTP request multiplexer". Like the standard 9 | http.ServeMux, mux.Router matches incoming requests against a list of 10 | registered routes and calls a handler for the route that matches the URL 11 | or other conditions. The main features are: 12 | 13 | * Requests can be matched based on URL host, path, path prefix, schemes, 14 | header and query values, HTTP methods or using custom matchers. 15 | * URL hosts, paths and query values can have variables with an optional 16 | regular expression. 17 | * Registered URLs can be built, or "reversed", which helps maintaining 18 | references to resources. 19 | * Routes can be used as subrouters: nested routes are only tested if the 20 | parent route matches. This is useful to define groups of routes that 21 | share common conditions like a host, a path prefix or other repeated 22 | attributes. As a bonus, this optimizes request matching. 23 | * It implements the http.Handler interface so it is compatible with the 24 | standard http.ServeMux. 25 | 26 | Let's start registering a couple of URL paths and handlers: 27 | 28 | func main() { 29 | r := mux.NewRouter() 30 | r.HandleFunc("/", HomeHandler) 31 | r.HandleFunc("/products", ProductsHandler) 32 | r.HandleFunc("/articles", ArticlesHandler) 33 | http.Handle("/", r) 34 | } 35 | 36 | Here we register three routes mapping URL paths to handlers. This is 37 | equivalent to how http.HandleFunc() works: if an incoming request URL matches 38 | one of the paths, the corresponding handler is called passing 39 | (http.ResponseWriter, *http.Request) as parameters. 40 | 41 | Paths can have variables. They are defined using the format {name} or 42 | {name:pattern}. If a regular expression pattern is not defined, the matched 43 | variable will be anything until the next slash. For example: 44 | 45 | r := mux.NewRouter() 46 | r.HandleFunc("/products/{key}", ProductHandler) 47 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 48 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 49 | 50 | Groups can be used inside patterns, as long as they are non-capturing (?:re). For example: 51 | 52 | r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler) 53 | 54 | The names are used to create a map of route variables which can be retrieved 55 | calling mux.Vars(): 56 | 57 | vars := mux.Vars(request) 58 | category := vars["category"] 59 | 60 | Note that if any capturing groups are present, mux will panic() during parsing. To prevent 61 | this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to 62 | "/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably 63 | when capturing groups were present. 64 | 65 | And this is all you need to know about the basic usage. More advanced options 66 | are explained below. 67 | 68 | Routes can also be restricted to a domain or subdomain. Just define a host 69 | pattern to be matched. They can also have variables: 70 | 71 | r := mux.NewRouter() 72 | // Only matches if domain is "www.example.com". 73 | r.Host("www.example.com") 74 | // Matches a dynamic subdomain. 75 | r.Host("{subdomain:[a-z]+}.domain.com") 76 | 77 | There are several other matchers that can be added. To match path prefixes: 78 | 79 | r.PathPrefix("/products/") 80 | 81 | ...or HTTP methods: 82 | 83 | r.Methods("GET", "POST") 84 | 85 | ...or URL schemes: 86 | 87 | r.Schemes("https") 88 | 89 | ...or header values: 90 | 91 | r.Headers("X-Requested-With", "XMLHttpRequest") 92 | 93 | ...or query values: 94 | 95 | r.Queries("key", "value") 96 | 97 | ...or to use a custom matcher function: 98 | 99 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 100 | return r.ProtoMajor == 0 101 | }) 102 | 103 | ...and finally, it is possible to combine several matchers in a single route: 104 | 105 | r.HandleFunc("/products", ProductsHandler). 106 | Host("www.example.com"). 107 | Methods("GET"). 108 | Schemes("http") 109 | 110 | Setting the same matching conditions again and again can be boring, so we have 111 | a way to group several routes that share the same requirements. 112 | We call it "subrouting". 113 | 114 | For example, let's say we have several URLs that should only match when the 115 | host is "www.example.com". Create a route for that host and get a "subrouter" 116 | from it: 117 | 118 | r := mux.NewRouter() 119 | s := r.Host("www.example.com").Subrouter() 120 | 121 | Then register routes in the subrouter: 122 | 123 | s.HandleFunc("/products/", ProductsHandler) 124 | s.HandleFunc("/products/{key}", ProductHandler) 125 | s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) 126 | 127 | The three URL paths we registered above will only be tested if the domain is 128 | "www.example.com", because the subrouter is tested first. This is not 129 | only convenient, but also optimizes request matching. You can create 130 | subrouters combining any attribute matchers accepted by a route. 131 | 132 | Subrouters can be used to create domain or path "namespaces": you define 133 | subrouters in a central place and then parts of the app can register its 134 | paths relatively to a given subrouter. 135 | 136 | There's one more thing about subroutes. When a subrouter has a path prefix, 137 | the inner routes use it as base for their paths: 138 | 139 | r := mux.NewRouter() 140 | s := r.PathPrefix("/products").Subrouter() 141 | // "/products/" 142 | s.HandleFunc("/", ProductsHandler) 143 | // "/products/{key}/" 144 | s.HandleFunc("/{key}/", ProductHandler) 145 | // "/products/{key}/details" 146 | s.HandleFunc("/{key}/details", ProductDetailsHandler) 147 | 148 | Note that the path provided to PathPrefix() represents a "wildcard": calling 149 | PathPrefix("/static/").Handler(...) means that the handler will be passed any 150 | request that matches "/static/*". This makes it easy to serve static files with mux: 151 | 152 | func main() { 153 | var dir string 154 | 155 | flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") 156 | flag.Parse() 157 | r := mux.NewRouter() 158 | 159 | // This will serve files under http://localhost:8000/static/ 160 | r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) 161 | 162 | srv := &http.Server{ 163 | Handler: r, 164 | Addr: "127.0.0.1:8000", 165 | // Good practice: enforce timeouts for servers you create! 166 | WriteTimeout: 15 * time.Second, 167 | ReadTimeout: 15 * time.Second, 168 | } 169 | 170 | log.Fatal(srv.ListenAndServe()) 171 | } 172 | 173 | Now let's see how to build registered URLs. 174 | 175 | Routes can be named. All routes that define a name can have their URLs built, 176 | or "reversed". We define a name calling Name() on a route. For example: 177 | 178 | r := mux.NewRouter() 179 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 180 | Name("article") 181 | 182 | To build a URL, get the route and call the URL() method, passing a sequence of 183 | key/value pairs for the route variables. For the previous route, we would do: 184 | 185 | url, err := r.Get("article").URL("category", "technology", "id", "42") 186 | 187 | ...and the result will be a url.URL with the following path: 188 | 189 | "/articles/technology/42" 190 | 191 | This also works for host and query value variables: 192 | 193 | r := mux.NewRouter() 194 | r.Host("{subdomain}.domain.com"). 195 | Path("/articles/{category}/{id:[0-9]+}"). 196 | Queries("filter", "{filter}"). 197 | HandlerFunc(ArticleHandler). 198 | Name("article") 199 | 200 | // url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla" 201 | url, err := r.Get("article").URL("subdomain", "news", 202 | "category", "technology", 203 | "id", "42", 204 | "filter", "gorilla") 205 | 206 | All variables defined in the route are required, and their values must 207 | conform to the corresponding patterns. These requirements guarantee that a 208 | generated URL will always match a registered route -- the only exception is 209 | for explicitly defined "build-only" routes which never match. 210 | 211 | Regex support also exists for matching Headers within a route. For example, we could do: 212 | 213 | r.HeadersRegexp("Content-Type", "application/(text|json)") 214 | 215 | ...and the route will match both requests with a Content-Type of `application/json` as well as 216 | `application/text` 217 | 218 | There's also a way to build only the URL host or path for a route: 219 | use the methods URLHost() or URLPath() instead. For the previous route, 220 | we would do: 221 | 222 | // "http://news.domain.com/" 223 | host, err := r.Get("article").URLHost("subdomain", "news") 224 | 225 | // "/articles/technology/42" 226 | path, err := r.Get("article").URLPath("category", "technology", "id", "42") 227 | 228 | And if you use subrouters, host and path defined separately can be built 229 | as well: 230 | 231 | r := mux.NewRouter() 232 | s := r.Host("{subdomain}.domain.com").Subrouter() 233 | s.Path("/articles/{category}/{id:[0-9]+}"). 234 | HandlerFunc(ArticleHandler). 235 | Name("article") 236 | 237 | // "http://news.domain.com/articles/technology/42" 238 | url, err := r.Get("article").URL("subdomain", "news", 239 | "category", "technology", 240 | "id", "42") 241 | 242 | Mux supports the addition of middlewares to a Router, which are executed in the order they are added if a match is found, including its subrouters. Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or ResponseWriter hijacking. 243 | 244 | type MiddlewareFunc func(http.Handler) http.Handler 245 | 246 | Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc (closures can access variables from the context where they are created). 247 | 248 | A very basic middleware which logs the URI of the request being handled could be written as: 249 | 250 | func simpleMw(next http.Handler) http.Handler { 251 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 252 | // Do stuff here 253 | log.Println(r.RequestURI) 254 | // Call the next handler, which can be another middleware in the chain, or the final handler. 255 | next.ServeHTTP(w, r) 256 | }) 257 | } 258 | 259 | Middlewares can be added to a router using `Router.Use()`: 260 | 261 | r := mux.NewRouter() 262 | r.HandleFunc("/", handler) 263 | r.Use(simpleMw) 264 | 265 | A more complex authentication middleware, which maps session token to users, could be written as: 266 | 267 | // Define our struct 268 | type authenticationMiddleware struct { 269 | tokenUsers map[string]string 270 | } 271 | 272 | // Initialize it somewhere 273 | func (amw *authenticationMiddleware) Populate() { 274 | amw.tokenUsers["00000000"] = "user0" 275 | amw.tokenUsers["aaaaaaaa"] = "userA" 276 | amw.tokenUsers["05f717e5"] = "randomUser" 277 | amw.tokenUsers["deadbeef"] = "user0" 278 | } 279 | 280 | // Middleware function, which will be called for each request 281 | func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { 282 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 283 | token := r.Header.Get("X-Session-Token") 284 | 285 | if user, found := amw.tokenUsers[token]; found { 286 | // We found the token in our map 287 | log.Printf("Authenticated user %s\n", user) 288 | next.ServeHTTP(w, r) 289 | } else { 290 | http.Error(w, "Forbidden", http.StatusForbidden) 291 | } 292 | }) 293 | } 294 | 295 | r := mux.NewRouter() 296 | r.HandleFunc("/", handler) 297 | 298 | amw := authenticationMiddleware{tokenUsers: make(map[string]string)} 299 | amw.Populate() 300 | 301 | r.Use(amw.Middleware) 302 | 303 | Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. 304 | 305 | */ 306 | package mux 307 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gorilla/mux 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/middleware.go: -------------------------------------------------------------------------------- 1 | package mux 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | // MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. 9 | // Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed 10 | // to it, and then calls the handler passed as parameter to the MiddlewareFunc. 11 | type MiddlewareFunc func(http.Handler) http.Handler 12 | 13 | // middleware interface is anything which implements a MiddlewareFunc named Middleware. 14 | type middleware interface { 15 | Middleware(handler http.Handler) http.Handler 16 | } 17 | 18 | // Middleware allows MiddlewareFunc to implement the middleware interface. 19 | func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { 20 | return mw(handler) 21 | } 22 | 23 | // Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. 24 | func (r *Router) Use(mwf ...MiddlewareFunc) { 25 | for _, fn := range mwf { 26 | r.middlewares = append(r.middlewares, fn) 27 | } 28 | } 29 | 30 | // useInterface appends a middleware to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. 31 | func (r *Router) useInterface(mw middleware) { 32 | r.middlewares = append(r.middlewares, mw) 33 | } 34 | 35 | // CORSMethodMiddleware automatically sets the Access-Control-Allow-Methods response header 36 | // on requests for routes that have an OPTIONS method matcher to all the method matchers on 37 | // the route. Routes that do not explicitly handle OPTIONS requests will not be processed 38 | // by the middleware. See examples for usage. 39 | func CORSMethodMiddleware(r *Router) MiddlewareFunc { 40 | return func(next http.Handler) http.Handler { 41 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 42 | allMethods, err := getAllMethodsForRoute(r, req) 43 | if err == nil { 44 | for _, v := range allMethods { 45 | if v == http.MethodOptions { 46 | w.Header().Set("Access-Control-Allow-Methods", strings.Join(allMethods, ",")) 47 | } 48 | } 49 | } 50 | 51 | next.ServeHTTP(w, req) 52 | }) 53 | } 54 | } 55 | 56 | // getAllMethodsForRoute returns all the methods from method matchers matching a given 57 | // request. 58 | func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { 59 | var allMethods []string 60 | 61 | for _, route := range r.routes { 62 | var match RouteMatch 63 | if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch { 64 | methods, err := route.GetMethods() 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | allMethods = append(allMethods, methods...) 70 | } 71 | } 72 | 73 | return allMethods, nil 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/mux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 mux 6 | 7 | import ( 8 | "context" 9 | "errors" 10 | "fmt" 11 | "net/http" 12 | "path" 13 | "regexp" 14 | ) 15 | 16 | var ( 17 | // ErrMethodMismatch is returned when the method in the request does not match 18 | // the method defined against the route. 19 | ErrMethodMismatch = errors.New("method is not allowed") 20 | // ErrNotFound is returned when no route match is found. 21 | ErrNotFound = errors.New("no matching route was found") 22 | ) 23 | 24 | // NewRouter returns a new router instance. 25 | func NewRouter() *Router { 26 | return &Router{namedRoutes: make(map[string]*Route)} 27 | } 28 | 29 | // Router registers routes to be matched and dispatches a handler. 30 | // 31 | // It implements the http.Handler interface, so it can be registered to serve 32 | // requests: 33 | // 34 | // var router = mux.NewRouter() 35 | // 36 | // func main() { 37 | // http.Handle("/", router) 38 | // } 39 | // 40 | // Or, for Google App Engine, register it in a init() function: 41 | // 42 | // func init() { 43 | // http.Handle("/", router) 44 | // } 45 | // 46 | // This will send all incoming requests to the router. 47 | type Router struct { 48 | // Configurable Handler to be used when no route matches. 49 | NotFoundHandler http.Handler 50 | 51 | // Configurable Handler to be used when the request method does not match the route. 52 | MethodNotAllowedHandler http.Handler 53 | 54 | // Routes to be matched, in order. 55 | routes []*Route 56 | 57 | // Routes by name for URL building. 58 | namedRoutes map[string]*Route 59 | 60 | // If true, do not clear the request context after handling the request. 61 | // 62 | // Deprecated: No effect, since the context is stored on the request itself. 63 | KeepContext bool 64 | 65 | // Slice of middlewares to be called after a match is found 66 | middlewares []middleware 67 | 68 | // configuration shared with `Route` 69 | routeConf 70 | } 71 | 72 | // common route configuration shared between `Router` and `Route` 73 | type routeConf struct { 74 | // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to" 75 | useEncodedPath bool 76 | 77 | // If true, when the path pattern is "/path/", accessing "/path" will 78 | // redirect to the former and vice versa. 79 | strictSlash bool 80 | 81 | // If true, when the path pattern is "/path//to", accessing "/path//to" 82 | // will not redirect 83 | skipClean bool 84 | 85 | // Manager for the variables from host and path. 86 | regexp routeRegexpGroup 87 | 88 | // List of matchers. 89 | matchers []matcher 90 | 91 | // The scheme used when building URLs. 92 | buildScheme string 93 | 94 | buildVarsFunc BuildVarsFunc 95 | } 96 | 97 | // returns an effective deep copy of `routeConf` 98 | func copyRouteConf(r routeConf) routeConf { 99 | c := r 100 | 101 | if r.regexp.path != nil { 102 | c.regexp.path = copyRouteRegexp(r.regexp.path) 103 | } 104 | 105 | if r.regexp.host != nil { 106 | c.regexp.host = copyRouteRegexp(r.regexp.host) 107 | } 108 | 109 | c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries)) 110 | for _, q := range r.regexp.queries { 111 | c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q)) 112 | } 113 | 114 | c.matchers = make([]matcher, len(r.matchers)) 115 | copy(c.matchers, r.matchers) 116 | 117 | return c 118 | } 119 | 120 | func copyRouteRegexp(r *routeRegexp) *routeRegexp { 121 | c := *r 122 | return &c 123 | } 124 | 125 | // Match attempts to match the given request against the router's registered routes. 126 | // 127 | // If the request matches a route of this router or one of its subrouters the Route, 128 | // Handler, and Vars fields of the the match argument are filled and this function 129 | // returns true. 130 | // 131 | // If the request does not match any of this router's or its subrouters' routes 132 | // then this function returns false. If available, a reason for the match failure 133 | // will be filled in the match argument's MatchErr field. If the match failure type 134 | // (eg: not found) has a registered handler, the handler is assigned to the Handler 135 | // field of the match argument. 136 | func (r *Router) Match(req *http.Request, match *RouteMatch) bool { 137 | for _, route := range r.routes { 138 | if route.Match(req, match) { 139 | // Build middleware chain if no error was found 140 | if match.MatchErr == nil { 141 | for i := len(r.middlewares) - 1; i >= 0; i-- { 142 | match.Handler = r.middlewares[i].Middleware(match.Handler) 143 | } 144 | } 145 | return true 146 | } 147 | } 148 | 149 | if match.MatchErr == ErrMethodMismatch { 150 | if r.MethodNotAllowedHandler != nil { 151 | match.Handler = r.MethodNotAllowedHandler 152 | return true 153 | } 154 | 155 | return false 156 | } 157 | 158 | // Closest match for a router (includes sub-routers) 159 | if r.NotFoundHandler != nil { 160 | match.Handler = r.NotFoundHandler 161 | match.MatchErr = ErrNotFound 162 | return true 163 | } 164 | 165 | match.MatchErr = ErrNotFound 166 | return false 167 | } 168 | 169 | // ServeHTTP dispatches the handler registered in the matched route. 170 | // 171 | // When there is a match, the route variables can be retrieved calling 172 | // mux.Vars(request). 173 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { 174 | if !r.skipClean { 175 | path := req.URL.Path 176 | if r.useEncodedPath { 177 | path = req.URL.EscapedPath() 178 | } 179 | // Clean path to canonical form and redirect. 180 | if p := cleanPath(path); p != path { 181 | 182 | // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. 183 | // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: 184 | // http://code.google.com/p/go/issues/detail?id=5252 185 | url := *req.URL 186 | url.Path = p 187 | p = url.String() 188 | 189 | w.Header().Set("Location", p) 190 | w.WriteHeader(http.StatusMovedPermanently) 191 | return 192 | } 193 | } 194 | var match RouteMatch 195 | var handler http.Handler 196 | if r.Match(req, &match) { 197 | handler = match.Handler 198 | req = requestWithVars(req, match.Vars) 199 | req = requestWithRoute(req, match.Route) 200 | } 201 | 202 | if handler == nil && match.MatchErr == ErrMethodMismatch { 203 | handler = methodNotAllowedHandler() 204 | } 205 | 206 | if handler == nil { 207 | handler = http.NotFoundHandler() 208 | } 209 | 210 | handler.ServeHTTP(w, req) 211 | } 212 | 213 | // Get returns a route registered with the given name. 214 | func (r *Router) Get(name string) *Route { 215 | return r.namedRoutes[name] 216 | } 217 | 218 | // GetRoute returns a route registered with the given name. This method 219 | // was renamed to Get() and remains here for backwards compatibility. 220 | func (r *Router) GetRoute(name string) *Route { 221 | return r.namedRoutes[name] 222 | } 223 | 224 | // StrictSlash defines the trailing slash behavior for new routes. The initial 225 | // value is false. 226 | // 227 | // When true, if the route path is "/path/", accessing "/path" will perform a redirect 228 | // to the former and vice versa. In other words, your application will always 229 | // see the path as specified in the route. 230 | // 231 | // When false, if the route path is "/path", accessing "/path/" will not match 232 | // this route and vice versa. 233 | // 234 | // The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for 235 | // routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed 236 | // request will be made as a GET by most clients. Use middleware or client settings 237 | // to modify this behaviour as needed. 238 | // 239 | // Special case: when a route sets a path prefix using the PathPrefix() method, 240 | // strict slash is ignored for that route because the redirect behavior can't 241 | // be determined from a prefix alone. However, any subrouters created from that 242 | // route inherit the original StrictSlash setting. 243 | func (r *Router) StrictSlash(value bool) *Router { 244 | r.strictSlash = value 245 | return r 246 | } 247 | 248 | // SkipClean defines the path cleaning behaviour for new routes. The initial 249 | // value is false. Users should be careful about which routes are not cleaned 250 | // 251 | // When true, if the route path is "/path//to", it will remain with the double 252 | // slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/ 253 | // 254 | // When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will 255 | // become /fetch/http/xkcd.com/534 256 | func (r *Router) SkipClean(value bool) *Router { 257 | r.skipClean = value 258 | return r 259 | } 260 | 261 | // UseEncodedPath tells the router to match the encoded original path 262 | // to the routes. 263 | // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". 264 | // 265 | // If not called, the router will match the unencoded path to the routes. 266 | // For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to" 267 | func (r *Router) UseEncodedPath() *Router { 268 | r.useEncodedPath = true 269 | return r 270 | } 271 | 272 | // ---------------------------------------------------------------------------- 273 | // Route factories 274 | // ---------------------------------------------------------------------------- 275 | 276 | // NewRoute registers an empty route. 277 | func (r *Router) NewRoute() *Route { 278 | // initialize a route with a copy of the parent router's configuration 279 | route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} 280 | r.routes = append(r.routes, route) 281 | return route 282 | } 283 | 284 | // Name registers a new route with a name. 285 | // See Route.Name(). 286 | func (r *Router) Name(name string) *Route { 287 | return r.NewRoute().Name(name) 288 | } 289 | 290 | // Handle registers a new route with a matcher for the URL path. 291 | // See Route.Path() and Route.Handler(). 292 | func (r *Router) Handle(path string, handler http.Handler) *Route { 293 | return r.NewRoute().Path(path).Handler(handler) 294 | } 295 | 296 | // HandleFunc registers a new route with a matcher for the URL path. 297 | // See Route.Path() and Route.HandlerFunc(). 298 | func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, 299 | *http.Request)) *Route { 300 | return r.NewRoute().Path(path).HandlerFunc(f) 301 | } 302 | 303 | // Headers registers a new route with a matcher for request header values. 304 | // See Route.Headers(). 305 | func (r *Router) Headers(pairs ...string) *Route { 306 | return r.NewRoute().Headers(pairs...) 307 | } 308 | 309 | // Host registers a new route with a matcher for the URL host. 310 | // See Route.Host(). 311 | func (r *Router) Host(tpl string) *Route { 312 | return r.NewRoute().Host(tpl) 313 | } 314 | 315 | // MatcherFunc registers a new route with a custom matcher function. 316 | // See Route.MatcherFunc(). 317 | func (r *Router) MatcherFunc(f MatcherFunc) *Route { 318 | return r.NewRoute().MatcherFunc(f) 319 | } 320 | 321 | // Methods registers a new route with a matcher for HTTP methods. 322 | // See Route.Methods(). 323 | func (r *Router) Methods(methods ...string) *Route { 324 | return r.NewRoute().Methods(methods...) 325 | } 326 | 327 | // Path registers a new route with a matcher for the URL path. 328 | // See Route.Path(). 329 | func (r *Router) Path(tpl string) *Route { 330 | return r.NewRoute().Path(tpl) 331 | } 332 | 333 | // PathPrefix registers a new route with a matcher for the URL path prefix. 334 | // See Route.PathPrefix(). 335 | func (r *Router) PathPrefix(tpl string) *Route { 336 | return r.NewRoute().PathPrefix(tpl) 337 | } 338 | 339 | // Queries registers a new route with a matcher for URL query values. 340 | // See Route.Queries(). 341 | func (r *Router) Queries(pairs ...string) *Route { 342 | return r.NewRoute().Queries(pairs...) 343 | } 344 | 345 | // Schemes registers a new route with a matcher for URL schemes. 346 | // See Route.Schemes(). 347 | func (r *Router) Schemes(schemes ...string) *Route { 348 | return r.NewRoute().Schemes(schemes...) 349 | } 350 | 351 | // BuildVarsFunc registers a new route with a custom function for modifying 352 | // route variables before building a URL. 353 | func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { 354 | return r.NewRoute().BuildVarsFunc(f) 355 | } 356 | 357 | // Walk walks the router and all its sub-routers, calling walkFn for each route 358 | // in the tree. The routes are walked in the order they were added. Sub-routers 359 | // are explored depth-first. 360 | func (r *Router) Walk(walkFn WalkFunc) error { 361 | return r.walk(walkFn, []*Route{}) 362 | } 363 | 364 | // SkipRouter is used as a return value from WalkFuncs to indicate that the 365 | // router that walk is about to descend down to should be skipped. 366 | var SkipRouter = errors.New("skip this router") 367 | 368 | // WalkFunc is the type of the function called for each route visited by Walk. 369 | // At every invocation, it is given the current route, and the current router, 370 | // and a list of ancestor routes that lead to the current route. 371 | type WalkFunc func(route *Route, router *Router, ancestors []*Route) error 372 | 373 | func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { 374 | for _, t := range r.routes { 375 | err := walkFn(t, r, ancestors) 376 | if err == SkipRouter { 377 | continue 378 | } 379 | if err != nil { 380 | return err 381 | } 382 | for _, sr := range t.matchers { 383 | if h, ok := sr.(*Router); ok { 384 | ancestors = append(ancestors, t) 385 | err := h.walk(walkFn, ancestors) 386 | if err != nil { 387 | return err 388 | } 389 | ancestors = ancestors[:len(ancestors)-1] 390 | } 391 | } 392 | if h, ok := t.handler.(*Router); ok { 393 | ancestors = append(ancestors, t) 394 | err := h.walk(walkFn, ancestors) 395 | if err != nil { 396 | return err 397 | } 398 | ancestors = ancestors[:len(ancestors)-1] 399 | } 400 | } 401 | return nil 402 | } 403 | 404 | // ---------------------------------------------------------------------------- 405 | // Context 406 | // ---------------------------------------------------------------------------- 407 | 408 | // RouteMatch stores information about a matched route. 409 | type RouteMatch struct { 410 | Route *Route 411 | Handler http.Handler 412 | Vars map[string]string 413 | 414 | // MatchErr is set to appropriate matching error 415 | // It is set to ErrMethodMismatch if there is a mismatch in 416 | // the request method and route method 417 | MatchErr error 418 | } 419 | 420 | type contextKey int 421 | 422 | const ( 423 | varsKey contextKey = iota 424 | routeKey 425 | ) 426 | 427 | // Vars returns the route variables for the current request, if any. 428 | func Vars(r *http.Request) map[string]string { 429 | if rv := r.Context().Value(varsKey); rv != nil { 430 | return rv.(map[string]string) 431 | } 432 | return nil 433 | } 434 | 435 | // CurrentRoute returns the matched route for the current request, if any. 436 | // This only works when called inside the handler of the matched route 437 | // because the matched route is stored in the request context which is cleared 438 | // after the handler returns. 439 | func CurrentRoute(r *http.Request) *Route { 440 | if rv := r.Context().Value(routeKey); rv != nil { 441 | return rv.(*Route) 442 | } 443 | return nil 444 | } 445 | 446 | func requestWithVars(r *http.Request, vars map[string]string) *http.Request { 447 | ctx := context.WithValue(r.Context(), varsKey, vars) 448 | return r.WithContext(ctx) 449 | } 450 | 451 | func requestWithRoute(r *http.Request, route *Route) *http.Request { 452 | ctx := context.WithValue(r.Context(), routeKey, route) 453 | return r.WithContext(ctx) 454 | } 455 | 456 | // ---------------------------------------------------------------------------- 457 | // Helpers 458 | // ---------------------------------------------------------------------------- 459 | 460 | // cleanPath returns the canonical path for p, eliminating . and .. elements. 461 | // Borrowed from the net/http package. 462 | func cleanPath(p string) string { 463 | if p == "" { 464 | return "/" 465 | } 466 | if p[0] != '/' { 467 | p = "/" + p 468 | } 469 | np := path.Clean(p) 470 | // path.Clean removes trailing slash except for root; 471 | // put the trailing slash back if necessary. 472 | if p[len(p)-1] == '/' && np != "/" { 473 | np += "/" 474 | } 475 | 476 | return np 477 | } 478 | 479 | // uniqueVars returns an error if two slices contain duplicated strings. 480 | func uniqueVars(s1, s2 []string) error { 481 | for _, v1 := range s1 { 482 | for _, v2 := range s2 { 483 | if v1 == v2 { 484 | return fmt.Errorf("mux: duplicated route variable %q", v2) 485 | } 486 | } 487 | } 488 | return nil 489 | } 490 | 491 | // checkPairs returns the count of strings passed in, and an error if 492 | // the count is not an even number. 493 | func checkPairs(pairs ...string) (int, error) { 494 | length := len(pairs) 495 | if length%2 != 0 { 496 | return length, fmt.Errorf( 497 | "mux: number of parameters must be multiple of 2, got %v", pairs) 498 | } 499 | return length, nil 500 | } 501 | 502 | // mapFromPairsToString converts variadic string parameters to a 503 | // string to string map. 504 | func mapFromPairsToString(pairs ...string) (map[string]string, error) { 505 | length, err := checkPairs(pairs...) 506 | if err != nil { 507 | return nil, err 508 | } 509 | m := make(map[string]string, length/2) 510 | for i := 0; i < length; i += 2 { 511 | m[pairs[i]] = pairs[i+1] 512 | } 513 | return m, nil 514 | } 515 | 516 | // mapFromPairsToRegex converts variadic string parameters to a 517 | // string to regex map. 518 | func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { 519 | length, err := checkPairs(pairs...) 520 | if err != nil { 521 | return nil, err 522 | } 523 | m := make(map[string]*regexp.Regexp, length/2) 524 | for i := 0; i < length; i += 2 { 525 | regex, err := regexp.Compile(pairs[i+1]) 526 | if err != nil { 527 | return nil, err 528 | } 529 | m[pairs[i]] = regex 530 | } 531 | return m, nil 532 | } 533 | 534 | // matchInArray returns true if the given string value is in the array. 535 | func matchInArray(arr []string, value string) bool { 536 | for _, v := range arr { 537 | if v == value { 538 | return true 539 | } 540 | } 541 | return false 542 | } 543 | 544 | // matchMapWithString returns true if the given key/value pairs exist in a given map. 545 | func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { 546 | for k, v := range toCheck { 547 | // Check if key exists. 548 | if canonicalKey { 549 | k = http.CanonicalHeaderKey(k) 550 | } 551 | if values := toMatch[k]; values == nil { 552 | return false 553 | } else if v != "" { 554 | // If value was defined as an empty string we only check that the 555 | // key exists. Otherwise we also check for equality. 556 | valueExists := false 557 | for _, value := range values { 558 | if v == value { 559 | valueExists = true 560 | break 561 | } 562 | } 563 | if !valueExists { 564 | return false 565 | } 566 | } 567 | } 568 | return true 569 | } 570 | 571 | // matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against 572 | // the given regex 573 | func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { 574 | for k, v := range toCheck { 575 | // Check if key exists. 576 | if canonicalKey { 577 | k = http.CanonicalHeaderKey(k) 578 | } 579 | if values := toMatch[k]; values == nil { 580 | return false 581 | } else if v != nil { 582 | // If value was defined as an empty string we only check that the 583 | // key exists. Otherwise we also check for equality. 584 | valueExists := false 585 | for _, value := range values { 586 | if v.MatchString(value) { 587 | valueExists = true 588 | break 589 | } 590 | } 591 | if !valueExists { 592 | return false 593 | } 594 | } 595 | } 596 | return true 597 | } 598 | 599 | // methodNotAllowed replies to the request with an HTTP status code 405. 600 | func methodNotAllowed(w http.ResponseWriter, r *http.Request) { 601 | w.WriteHeader(http.StatusMethodNotAllowed) 602 | } 603 | 604 | // methodNotAllowedHandler returns a simple request handler 605 | // that replies to each request with a status code 405. 606 | func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) } 607 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/regexp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 mux 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/http" 11 | "net/url" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | type routeRegexpOptions struct { 18 | strictSlash bool 19 | useEncodedPath bool 20 | } 21 | 22 | type regexpType int 23 | 24 | const ( 25 | regexpTypePath regexpType = 0 26 | regexpTypeHost regexpType = 1 27 | regexpTypePrefix regexpType = 2 28 | regexpTypeQuery regexpType = 3 29 | ) 30 | 31 | // newRouteRegexp parses a route template and returns a routeRegexp, 32 | // used to match a host, a path or a query string. 33 | // 34 | // It will extract named variables, assemble a regexp to be matched, create 35 | // a "reverse" template to build URLs and compile regexps to validate variable 36 | // values used in URL building. 37 | // 38 | // Previously we accepted only Python-like identifiers for variable 39 | // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that 40 | // name and pattern can't be empty, and names can't contain a colon. 41 | func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*routeRegexp, error) { 42 | // Check if it is well-formed. 43 | idxs, errBraces := braceIndices(tpl) 44 | if errBraces != nil { 45 | return nil, errBraces 46 | } 47 | // Backup the original. 48 | template := tpl 49 | // Now let's parse it. 50 | defaultPattern := "[^/]+" 51 | if typ == regexpTypeQuery { 52 | defaultPattern = ".*" 53 | } else if typ == regexpTypeHost { 54 | defaultPattern = "[^.]+" 55 | } 56 | // Only match strict slash if not matching 57 | if typ != regexpTypePath { 58 | options.strictSlash = false 59 | } 60 | // Set a flag for strictSlash. 61 | endSlash := false 62 | if options.strictSlash && strings.HasSuffix(tpl, "/") { 63 | tpl = tpl[:len(tpl)-1] 64 | endSlash = true 65 | } 66 | varsN := make([]string, len(idxs)/2) 67 | varsR := make([]*regexp.Regexp, len(idxs)/2) 68 | pattern := bytes.NewBufferString("") 69 | pattern.WriteByte('^') 70 | reverse := bytes.NewBufferString("") 71 | var end int 72 | var err error 73 | for i := 0; i < len(idxs); i += 2 { 74 | // Set all values we are interested in. 75 | raw := tpl[end:idxs[i]] 76 | end = idxs[i+1] 77 | parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) 78 | name := parts[0] 79 | patt := defaultPattern 80 | if len(parts) == 2 { 81 | patt = parts[1] 82 | } 83 | // Name or pattern can't be empty. 84 | if name == "" || patt == "" { 85 | return nil, fmt.Errorf("mux: missing name or pattern in %q", 86 | tpl[idxs[i]:end]) 87 | } 88 | // Build the regexp pattern. 89 | fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt) 90 | 91 | // Build the reverse template. 92 | fmt.Fprintf(reverse, "%s%%s", raw) 93 | 94 | // Append variable name and compiled pattern. 95 | varsN[i/2] = name 96 | varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) 97 | if err != nil { 98 | return nil, err 99 | } 100 | } 101 | // Add the remaining. 102 | raw := tpl[end:] 103 | pattern.WriteString(regexp.QuoteMeta(raw)) 104 | if options.strictSlash { 105 | pattern.WriteString("[/]?") 106 | } 107 | if typ == regexpTypeQuery { 108 | // Add the default pattern if the query value is empty 109 | if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { 110 | pattern.WriteString(defaultPattern) 111 | } 112 | } 113 | if typ != regexpTypePrefix { 114 | pattern.WriteByte('$') 115 | } 116 | 117 | var wildcardHostPort bool 118 | if typ == regexpTypeHost { 119 | if !strings.Contains(pattern.String(), ":") { 120 | wildcardHostPort = true 121 | } 122 | } 123 | reverse.WriteString(raw) 124 | if endSlash { 125 | reverse.WriteByte('/') 126 | } 127 | // Compile full regexp. 128 | reg, errCompile := regexp.Compile(pattern.String()) 129 | if errCompile != nil { 130 | return nil, errCompile 131 | } 132 | 133 | // Check for capturing groups which used to work in older versions 134 | if reg.NumSubexp() != len(idxs)/2 { 135 | panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) + 136 | "Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)") 137 | } 138 | 139 | // Done! 140 | return &routeRegexp{ 141 | template: template, 142 | regexpType: typ, 143 | options: options, 144 | regexp: reg, 145 | reverse: reverse.String(), 146 | varsN: varsN, 147 | varsR: varsR, 148 | wildcardHostPort: wildcardHostPort, 149 | }, nil 150 | } 151 | 152 | // routeRegexp stores a regexp to match a host or path and information to 153 | // collect and validate route variables. 154 | type routeRegexp struct { 155 | // The unmodified template. 156 | template string 157 | // The type of match 158 | regexpType regexpType 159 | // Options for matching 160 | options routeRegexpOptions 161 | // Expanded regexp. 162 | regexp *regexp.Regexp 163 | // Reverse template. 164 | reverse string 165 | // Variable names. 166 | varsN []string 167 | // Variable regexps (validators). 168 | varsR []*regexp.Regexp 169 | // Wildcard host-port (no strict port match in hostname) 170 | wildcardHostPort bool 171 | } 172 | 173 | // Match matches the regexp against the URL host or path. 174 | func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { 175 | if r.regexpType == regexpTypeHost { 176 | host := getHost(req) 177 | if r.wildcardHostPort { 178 | // Don't be strict on the port match 179 | if i := strings.Index(host, ":"); i != -1 { 180 | host = host[:i] 181 | } 182 | } 183 | return r.regexp.MatchString(host) 184 | } 185 | 186 | if r.regexpType == regexpTypeQuery { 187 | return r.matchQueryString(req) 188 | } 189 | path := req.URL.Path 190 | if r.options.useEncodedPath { 191 | path = req.URL.EscapedPath() 192 | } 193 | return r.regexp.MatchString(path) 194 | } 195 | 196 | // url builds a URL part using the given values. 197 | func (r *routeRegexp) url(values map[string]string) (string, error) { 198 | urlValues := make([]interface{}, len(r.varsN), len(r.varsN)) 199 | for k, v := range r.varsN { 200 | value, ok := values[v] 201 | if !ok { 202 | return "", fmt.Errorf("mux: missing route variable %q", v) 203 | } 204 | if r.regexpType == regexpTypeQuery { 205 | value = url.QueryEscape(value) 206 | } 207 | urlValues[k] = value 208 | } 209 | rv := fmt.Sprintf(r.reverse, urlValues...) 210 | if !r.regexp.MatchString(rv) { 211 | // The URL is checked against the full regexp, instead of checking 212 | // individual variables. This is faster but to provide a good error 213 | // message, we check individual regexps if the URL doesn't match. 214 | for k, v := range r.varsN { 215 | if !r.varsR[k].MatchString(values[v]) { 216 | return "", fmt.Errorf( 217 | "mux: variable %q doesn't match, expected %q", values[v], 218 | r.varsR[k].String()) 219 | } 220 | } 221 | } 222 | return rv, nil 223 | } 224 | 225 | // getURLQuery returns a single query parameter from a request URL. 226 | // For a URL with foo=bar&baz=ding, we return only the relevant key 227 | // value pair for the routeRegexp. 228 | func (r *routeRegexp) getURLQuery(req *http.Request) string { 229 | if r.regexpType != regexpTypeQuery { 230 | return "" 231 | } 232 | templateKey := strings.SplitN(r.template, "=", 2)[0] 233 | val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey) 234 | if ok { 235 | return templateKey + "=" + val 236 | } 237 | return "" 238 | } 239 | 240 | // findFirstQueryKey returns the same result as (*url.URL).Query()[key][0]. 241 | // If key was not found, empty string and false is returned. 242 | func findFirstQueryKey(rawQuery, key string) (value string, ok bool) { 243 | query := []byte(rawQuery) 244 | for len(query) > 0 { 245 | foundKey := query 246 | if i := bytes.IndexAny(foundKey, "&;"); i >= 0 { 247 | foundKey, query = foundKey[:i], foundKey[i+1:] 248 | } else { 249 | query = query[:0] 250 | } 251 | if len(foundKey) == 0 { 252 | continue 253 | } 254 | var value []byte 255 | if i := bytes.IndexByte(foundKey, '='); i >= 0 { 256 | foundKey, value = foundKey[:i], foundKey[i+1:] 257 | } 258 | if len(foundKey) < len(key) { 259 | // Cannot possibly be key. 260 | continue 261 | } 262 | keyString, err := url.QueryUnescape(string(foundKey)) 263 | if err != nil { 264 | continue 265 | } 266 | if keyString != key { 267 | continue 268 | } 269 | valueString, err := url.QueryUnescape(string(value)) 270 | if err != nil { 271 | continue 272 | } 273 | return valueString, true 274 | } 275 | return "", false 276 | } 277 | 278 | func (r *routeRegexp) matchQueryString(req *http.Request) bool { 279 | return r.regexp.MatchString(r.getURLQuery(req)) 280 | } 281 | 282 | // braceIndices returns the first level curly brace indices from a string. 283 | // It returns an error in case of unbalanced braces. 284 | func braceIndices(s string) ([]int, error) { 285 | var level, idx int 286 | var idxs []int 287 | for i := 0; i < len(s); i++ { 288 | switch s[i] { 289 | case '{': 290 | if level++; level == 1 { 291 | idx = i 292 | } 293 | case '}': 294 | if level--; level == 0 { 295 | idxs = append(idxs, idx, i+1) 296 | } else if level < 0 { 297 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s) 298 | } 299 | } 300 | } 301 | if level != 0 { 302 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s) 303 | } 304 | return idxs, nil 305 | } 306 | 307 | // varGroupName builds a capturing group name for the indexed variable. 308 | func varGroupName(idx int) string { 309 | return "v" + strconv.Itoa(idx) 310 | } 311 | 312 | // ---------------------------------------------------------------------------- 313 | // routeRegexpGroup 314 | // ---------------------------------------------------------------------------- 315 | 316 | // routeRegexpGroup groups the route matchers that carry variables. 317 | type routeRegexpGroup struct { 318 | host *routeRegexp 319 | path *routeRegexp 320 | queries []*routeRegexp 321 | } 322 | 323 | // setMatch extracts the variables from the URL once a route matches. 324 | func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { 325 | // Store host variables. 326 | if v.host != nil { 327 | host := getHost(req) 328 | if v.host.wildcardHostPort { 329 | // Don't be strict on the port match 330 | if i := strings.Index(host, ":"); i != -1 { 331 | host = host[:i] 332 | } 333 | } 334 | matches := v.host.regexp.FindStringSubmatchIndex(host) 335 | if len(matches) > 0 { 336 | extractVars(host, matches, v.host.varsN, m.Vars) 337 | } 338 | } 339 | path := req.URL.Path 340 | if r.useEncodedPath { 341 | path = req.URL.EscapedPath() 342 | } 343 | // Store path variables. 344 | if v.path != nil { 345 | matches := v.path.regexp.FindStringSubmatchIndex(path) 346 | if len(matches) > 0 { 347 | extractVars(path, matches, v.path.varsN, m.Vars) 348 | // Check if we should redirect. 349 | if v.path.options.strictSlash { 350 | p1 := strings.HasSuffix(path, "/") 351 | p2 := strings.HasSuffix(v.path.template, "/") 352 | if p1 != p2 { 353 | u, _ := url.Parse(req.URL.String()) 354 | if p1 { 355 | u.Path = u.Path[:len(u.Path)-1] 356 | } else { 357 | u.Path += "/" 358 | } 359 | m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently) 360 | } 361 | } 362 | } 363 | } 364 | // Store query string variables. 365 | for _, q := range v.queries { 366 | queryURL := q.getURLQuery(req) 367 | matches := q.regexp.FindStringSubmatchIndex(queryURL) 368 | if len(matches) > 0 { 369 | extractVars(queryURL, matches, q.varsN, m.Vars) 370 | } 371 | } 372 | } 373 | 374 | // getHost tries its best to return the request host. 375 | // According to section 14.23 of RFC 2616 the Host header 376 | // can include the port number if the default value of 80 is not used. 377 | func getHost(r *http.Request) string { 378 | if r.URL.IsAbs() { 379 | return r.URL.Host 380 | } 381 | return r.Host 382 | } 383 | 384 | func extractVars(input string, matches []int, names []string, output map[string]string) { 385 | for i, name := range names { 386 | output[name] = input[matches[2*i+2]:matches[2*i+3]] 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/route.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 mux 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net/http" 11 | "net/url" 12 | "regexp" 13 | "strings" 14 | ) 15 | 16 | // Route stores information to match a request and build URLs. 17 | type Route struct { 18 | // Request handler for the route. 19 | handler http.Handler 20 | // If true, this route never matches: it is only used to build URLs. 21 | buildOnly bool 22 | // The name used to build URLs. 23 | name string 24 | // Error resulted from building a route. 25 | err error 26 | 27 | // "global" reference to all named routes 28 | namedRoutes map[string]*Route 29 | 30 | // config possibly passed in from `Router` 31 | routeConf 32 | } 33 | 34 | // SkipClean reports whether path cleaning is enabled for this route via 35 | // Router.SkipClean. 36 | func (r *Route) SkipClean() bool { 37 | return r.skipClean 38 | } 39 | 40 | // Match matches the route against the request. 41 | func (r *Route) Match(req *http.Request, match *RouteMatch) bool { 42 | if r.buildOnly || r.err != nil { 43 | return false 44 | } 45 | 46 | var matchErr error 47 | 48 | // Match everything. 49 | for _, m := range r.matchers { 50 | if matched := m.Match(req, match); !matched { 51 | if _, ok := m.(methodMatcher); ok { 52 | matchErr = ErrMethodMismatch 53 | continue 54 | } 55 | 56 | // Ignore ErrNotFound errors. These errors arise from match call 57 | // to Subrouters. 58 | // 59 | // This prevents subsequent matching subrouters from failing to 60 | // run middleware. If not ignored, the middleware would see a 61 | // non-nil MatchErr and be skipped, even when there was a 62 | // matching route. 63 | if match.MatchErr == ErrNotFound { 64 | match.MatchErr = nil 65 | } 66 | 67 | matchErr = nil 68 | return false 69 | } 70 | } 71 | 72 | if matchErr != nil { 73 | match.MatchErr = matchErr 74 | return false 75 | } 76 | 77 | if match.MatchErr == ErrMethodMismatch && r.handler != nil { 78 | // We found a route which matches request method, clear MatchErr 79 | match.MatchErr = nil 80 | // Then override the mis-matched handler 81 | match.Handler = r.handler 82 | } 83 | 84 | // Yay, we have a match. Let's collect some info about it. 85 | if match.Route == nil { 86 | match.Route = r 87 | } 88 | if match.Handler == nil { 89 | match.Handler = r.handler 90 | } 91 | if match.Vars == nil { 92 | match.Vars = make(map[string]string) 93 | } 94 | 95 | // Set variables. 96 | r.regexp.setMatch(req, match, r) 97 | return true 98 | } 99 | 100 | // ---------------------------------------------------------------------------- 101 | // Route attributes 102 | // ---------------------------------------------------------------------------- 103 | 104 | // GetError returns an error resulted from building the route, if any. 105 | func (r *Route) GetError() error { 106 | return r.err 107 | } 108 | 109 | // BuildOnly sets the route to never match: it is only used to build URLs. 110 | func (r *Route) BuildOnly() *Route { 111 | r.buildOnly = true 112 | return r 113 | } 114 | 115 | // Handler -------------------------------------------------------------------- 116 | 117 | // Handler sets a handler for the route. 118 | func (r *Route) Handler(handler http.Handler) *Route { 119 | if r.err == nil { 120 | r.handler = handler 121 | } 122 | return r 123 | } 124 | 125 | // HandlerFunc sets a handler function for the route. 126 | func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { 127 | return r.Handler(http.HandlerFunc(f)) 128 | } 129 | 130 | // GetHandler returns the handler for the route, if any. 131 | func (r *Route) GetHandler() http.Handler { 132 | return r.handler 133 | } 134 | 135 | // Name ----------------------------------------------------------------------- 136 | 137 | // Name sets the name for the route, used to build URLs. 138 | // It is an error to call Name more than once on a route. 139 | func (r *Route) Name(name string) *Route { 140 | if r.name != "" { 141 | r.err = fmt.Errorf("mux: route already has name %q, can't set %q", 142 | r.name, name) 143 | } 144 | if r.err == nil { 145 | r.name = name 146 | r.namedRoutes[name] = r 147 | } 148 | return r 149 | } 150 | 151 | // GetName returns the name for the route, if any. 152 | func (r *Route) GetName() string { 153 | return r.name 154 | } 155 | 156 | // ---------------------------------------------------------------------------- 157 | // Matchers 158 | // ---------------------------------------------------------------------------- 159 | 160 | // matcher types try to match a request. 161 | type matcher interface { 162 | Match(*http.Request, *RouteMatch) bool 163 | } 164 | 165 | // addMatcher adds a matcher to the route. 166 | func (r *Route) addMatcher(m matcher) *Route { 167 | if r.err == nil { 168 | r.matchers = append(r.matchers, m) 169 | } 170 | return r 171 | } 172 | 173 | // addRegexpMatcher adds a host or path matcher and builder to a route. 174 | func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error { 175 | if r.err != nil { 176 | return r.err 177 | } 178 | if typ == regexpTypePath || typ == regexpTypePrefix { 179 | if len(tpl) > 0 && tpl[0] != '/' { 180 | return fmt.Errorf("mux: path must start with a slash, got %q", tpl) 181 | } 182 | if r.regexp.path != nil { 183 | tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl 184 | } 185 | } 186 | rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ 187 | strictSlash: r.strictSlash, 188 | useEncodedPath: r.useEncodedPath, 189 | }) 190 | if err != nil { 191 | return err 192 | } 193 | for _, q := range r.regexp.queries { 194 | if err = uniqueVars(rr.varsN, q.varsN); err != nil { 195 | return err 196 | } 197 | } 198 | if typ == regexpTypeHost { 199 | if r.regexp.path != nil { 200 | if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { 201 | return err 202 | } 203 | } 204 | r.regexp.host = rr 205 | } else { 206 | if r.regexp.host != nil { 207 | if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { 208 | return err 209 | } 210 | } 211 | if typ == regexpTypeQuery { 212 | r.regexp.queries = append(r.regexp.queries, rr) 213 | } else { 214 | r.regexp.path = rr 215 | } 216 | } 217 | r.addMatcher(rr) 218 | return nil 219 | } 220 | 221 | // Headers -------------------------------------------------------------------- 222 | 223 | // headerMatcher matches the request against header values. 224 | type headerMatcher map[string]string 225 | 226 | func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { 227 | return matchMapWithString(m, r.Header, true) 228 | } 229 | 230 | // Headers adds a matcher for request header values. 231 | // It accepts a sequence of key/value pairs to be matched. For example: 232 | // 233 | // r := mux.NewRouter() 234 | // r.Headers("Content-Type", "application/json", 235 | // "X-Requested-With", "XMLHttpRequest") 236 | // 237 | // The above route will only match if both request header values match. 238 | // If the value is an empty string, it will match any value if the key is set. 239 | func (r *Route) Headers(pairs ...string) *Route { 240 | if r.err == nil { 241 | var headers map[string]string 242 | headers, r.err = mapFromPairsToString(pairs...) 243 | return r.addMatcher(headerMatcher(headers)) 244 | } 245 | return r 246 | } 247 | 248 | // headerRegexMatcher matches the request against the route given a regex for the header 249 | type headerRegexMatcher map[string]*regexp.Regexp 250 | 251 | func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { 252 | return matchMapWithRegex(m, r.Header, true) 253 | } 254 | 255 | // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex 256 | // support. For example: 257 | // 258 | // r := mux.NewRouter() 259 | // r.HeadersRegexp("Content-Type", "application/(text|json)", 260 | // "X-Requested-With", "XMLHttpRequest") 261 | // 262 | // The above route will only match if both the request header matches both regular expressions. 263 | // If the value is an empty string, it will match any value if the key is set. 264 | // Use the start and end of string anchors (^ and $) to match an exact value. 265 | func (r *Route) HeadersRegexp(pairs ...string) *Route { 266 | if r.err == nil { 267 | var headers map[string]*regexp.Regexp 268 | headers, r.err = mapFromPairsToRegex(pairs...) 269 | return r.addMatcher(headerRegexMatcher(headers)) 270 | } 271 | return r 272 | } 273 | 274 | // Host ----------------------------------------------------------------------- 275 | 276 | // Host adds a matcher for the URL host. 277 | // It accepts a template with zero or more URL variables enclosed by {}. 278 | // Variables can define an optional regexp pattern to be matched: 279 | // 280 | // - {name} matches anything until the next dot. 281 | // 282 | // - {name:pattern} matches the given regexp pattern. 283 | // 284 | // For example: 285 | // 286 | // r := mux.NewRouter() 287 | // r.Host("www.example.com") 288 | // r.Host("{subdomain}.domain.com") 289 | // r.Host("{subdomain:[a-z]+}.domain.com") 290 | // 291 | // Variable names must be unique in a given route. They can be retrieved 292 | // calling mux.Vars(request). 293 | func (r *Route) Host(tpl string) *Route { 294 | r.err = r.addRegexpMatcher(tpl, regexpTypeHost) 295 | return r 296 | } 297 | 298 | // MatcherFunc ---------------------------------------------------------------- 299 | 300 | // MatcherFunc is the function signature used by custom matchers. 301 | type MatcherFunc func(*http.Request, *RouteMatch) bool 302 | 303 | // Match returns the match for a given request. 304 | func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { 305 | return m(r, match) 306 | } 307 | 308 | // MatcherFunc adds a custom function to be used as request matcher. 309 | func (r *Route) MatcherFunc(f MatcherFunc) *Route { 310 | return r.addMatcher(f) 311 | } 312 | 313 | // Methods -------------------------------------------------------------------- 314 | 315 | // methodMatcher matches the request against HTTP methods. 316 | type methodMatcher []string 317 | 318 | func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { 319 | return matchInArray(m, r.Method) 320 | } 321 | 322 | // Methods adds a matcher for HTTP methods. 323 | // It accepts a sequence of one or more methods to be matched, e.g.: 324 | // "GET", "POST", "PUT". 325 | func (r *Route) Methods(methods ...string) *Route { 326 | for k, v := range methods { 327 | methods[k] = strings.ToUpper(v) 328 | } 329 | return r.addMatcher(methodMatcher(methods)) 330 | } 331 | 332 | // Path ----------------------------------------------------------------------- 333 | 334 | // Path adds a matcher for the URL path. 335 | // It accepts a template with zero or more URL variables enclosed by {}. The 336 | // template must start with a "/". 337 | // Variables can define an optional regexp pattern to be matched: 338 | // 339 | // - {name} matches anything until the next slash. 340 | // 341 | // - {name:pattern} matches the given regexp pattern. 342 | // 343 | // For example: 344 | // 345 | // r := mux.NewRouter() 346 | // r.Path("/products/").Handler(ProductsHandler) 347 | // r.Path("/products/{key}").Handler(ProductsHandler) 348 | // r.Path("/articles/{category}/{id:[0-9]+}"). 349 | // Handler(ArticleHandler) 350 | // 351 | // Variable names must be unique in a given route. They can be retrieved 352 | // calling mux.Vars(request). 353 | func (r *Route) Path(tpl string) *Route { 354 | r.err = r.addRegexpMatcher(tpl, regexpTypePath) 355 | return r 356 | } 357 | 358 | // PathPrefix ----------------------------------------------------------------- 359 | 360 | // PathPrefix adds a matcher for the URL path prefix. This matches if the given 361 | // template is a prefix of the full URL path. See Route.Path() for details on 362 | // the tpl argument. 363 | // 364 | // Note that it does not treat slashes specially ("/foobar/" will be matched by 365 | // the prefix "/foo") so you may want to use a trailing slash here. 366 | // 367 | // Also note that the setting of Router.StrictSlash() has no effect on routes 368 | // with a PathPrefix matcher. 369 | func (r *Route) PathPrefix(tpl string) *Route { 370 | r.err = r.addRegexpMatcher(tpl, regexpTypePrefix) 371 | return r 372 | } 373 | 374 | // Query ---------------------------------------------------------------------- 375 | 376 | // Queries adds a matcher for URL query values. 377 | // It accepts a sequence of key/value pairs. Values may define variables. 378 | // For example: 379 | // 380 | // r := mux.NewRouter() 381 | // r.Queries("foo", "bar", "id", "{id:[0-9]+}") 382 | // 383 | // The above route will only match if the URL contains the defined queries 384 | // values, e.g.: ?foo=bar&id=42. 385 | // 386 | // If the value is an empty string, it will match any value if the key is set. 387 | // 388 | // Variables can define an optional regexp pattern to be matched: 389 | // 390 | // - {name} matches anything until the next slash. 391 | // 392 | // - {name:pattern} matches the given regexp pattern. 393 | func (r *Route) Queries(pairs ...string) *Route { 394 | length := len(pairs) 395 | if length%2 != 0 { 396 | r.err = fmt.Errorf( 397 | "mux: number of parameters must be multiple of 2, got %v", pairs) 398 | return nil 399 | } 400 | for i := 0; i < length; i += 2 { 401 | if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil { 402 | return r 403 | } 404 | } 405 | 406 | return r 407 | } 408 | 409 | // Schemes -------------------------------------------------------------------- 410 | 411 | // schemeMatcher matches the request against URL schemes. 412 | type schemeMatcher []string 413 | 414 | func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { 415 | scheme := r.URL.Scheme 416 | // https://golang.org/pkg/net/http/#Request 417 | // "For [most] server requests, fields other than Path and RawQuery will be 418 | // empty." 419 | // Since we're an http muxer, the scheme is either going to be http or https 420 | // though, so we can just set it based on the tls termination state. 421 | if scheme == "" { 422 | if r.TLS == nil { 423 | scheme = "http" 424 | } else { 425 | scheme = "https" 426 | } 427 | } 428 | return matchInArray(m, scheme) 429 | } 430 | 431 | // Schemes adds a matcher for URL schemes. 432 | // It accepts a sequence of schemes to be matched, e.g.: "http", "https". 433 | // If the request's URL has a scheme set, it will be matched against. 434 | // Generally, the URL scheme will only be set if a previous handler set it, 435 | // such as the ProxyHeaders handler from gorilla/handlers. 436 | // If unset, the scheme will be determined based on the request's TLS 437 | // termination state. 438 | // The first argument to Schemes will be used when constructing a route URL. 439 | func (r *Route) Schemes(schemes ...string) *Route { 440 | for k, v := range schemes { 441 | schemes[k] = strings.ToLower(v) 442 | } 443 | if len(schemes) > 0 { 444 | r.buildScheme = schemes[0] 445 | } 446 | return r.addMatcher(schemeMatcher(schemes)) 447 | } 448 | 449 | // BuildVarsFunc -------------------------------------------------------------- 450 | 451 | // BuildVarsFunc is the function signature used by custom build variable 452 | // functions (which can modify route variables before a route's URL is built). 453 | type BuildVarsFunc func(map[string]string) map[string]string 454 | 455 | // BuildVarsFunc adds a custom function to be used to modify build variables 456 | // before a route's URL is built. 457 | func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { 458 | if r.buildVarsFunc != nil { 459 | // compose the old and new functions 460 | old := r.buildVarsFunc 461 | r.buildVarsFunc = func(m map[string]string) map[string]string { 462 | return f(old(m)) 463 | } 464 | } else { 465 | r.buildVarsFunc = f 466 | } 467 | return r 468 | } 469 | 470 | // Subrouter ------------------------------------------------------------------ 471 | 472 | // Subrouter creates a subrouter for the route. 473 | // 474 | // It will test the inner routes only if the parent route matched. For example: 475 | // 476 | // r := mux.NewRouter() 477 | // s := r.Host("www.example.com").Subrouter() 478 | // s.HandleFunc("/products/", ProductsHandler) 479 | // s.HandleFunc("/products/{key}", ProductHandler) 480 | // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) 481 | // 482 | // Here, the routes registered in the subrouter won't be tested if the host 483 | // doesn't match. 484 | func (r *Route) Subrouter() *Router { 485 | // initialize a subrouter with a copy of the parent route's configuration 486 | router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} 487 | r.addMatcher(router) 488 | return router 489 | } 490 | 491 | // ---------------------------------------------------------------------------- 492 | // URL building 493 | // ---------------------------------------------------------------------------- 494 | 495 | // URL builds a URL for the route. 496 | // 497 | // It accepts a sequence of key/value pairs for the route variables. For 498 | // example, given this route: 499 | // 500 | // r := mux.NewRouter() 501 | // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 502 | // Name("article") 503 | // 504 | // ...a URL for it can be built using: 505 | // 506 | // url, err := r.Get("article").URL("category", "technology", "id", "42") 507 | // 508 | // ...which will return an url.URL with the following path: 509 | // 510 | // "/articles/technology/42" 511 | // 512 | // This also works for host variables: 513 | // 514 | // r := mux.NewRouter() 515 | // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 516 | // Host("{subdomain}.domain.com"). 517 | // Name("article") 518 | // 519 | // // url.String() will be "http://news.domain.com/articles/technology/42" 520 | // url, err := r.Get("article").URL("subdomain", "news", 521 | // "category", "technology", 522 | // "id", "42") 523 | // 524 | // The scheme of the resulting url will be the first argument that was passed to Schemes: 525 | // 526 | // // url.String() will be "https://example.com" 527 | // r := mux.NewRouter() 528 | // url, err := r.Host("example.com") 529 | // .Schemes("https", "http").URL() 530 | // 531 | // All variables defined in the route are required, and their values must 532 | // conform to the corresponding patterns. 533 | func (r *Route) URL(pairs ...string) (*url.URL, error) { 534 | if r.err != nil { 535 | return nil, r.err 536 | } 537 | values, err := r.prepareVars(pairs...) 538 | if err != nil { 539 | return nil, err 540 | } 541 | var scheme, host, path string 542 | queries := make([]string, 0, len(r.regexp.queries)) 543 | if r.regexp.host != nil { 544 | if host, err = r.regexp.host.url(values); err != nil { 545 | return nil, err 546 | } 547 | scheme = "http" 548 | if r.buildScheme != "" { 549 | scheme = r.buildScheme 550 | } 551 | } 552 | if r.regexp.path != nil { 553 | if path, err = r.regexp.path.url(values); err != nil { 554 | return nil, err 555 | } 556 | } 557 | for _, q := range r.regexp.queries { 558 | var query string 559 | if query, err = q.url(values); err != nil { 560 | return nil, err 561 | } 562 | queries = append(queries, query) 563 | } 564 | return &url.URL{ 565 | Scheme: scheme, 566 | Host: host, 567 | Path: path, 568 | RawQuery: strings.Join(queries, "&"), 569 | }, nil 570 | } 571 | 572 | // URLHost builds the host part of the URL for a route. See Route.URL(). 573 | // 574 | // The route must have a host defined. 575 | func (r *Route) URLHost(pairs ...string) (*url.URL, error) { 576 | if r.err != nil { 577 | return nil, r.err 578 | } 579 | if r.regexp.host == nil { 580 | return nil, errors.New("mux: route doesn't have a host") 581 | } 582 | values, err := r.prepareVars(pairs...) 583 | if err != nil { 584 | return nil, err 585 | } 586 | host, err := r.regexp.host.url(values) 587 | if err != nil { 588 | return nil, err 589 | } 590 | u := &url.URL{ 591 | Scheme: "http", 592 | Host: host, 593 | } 594 | if r.buildScheme != "" { 595 | u.Scheme = r.buildScheme 596 | } 597 | return u, nil 598 | } 599 | 600 | // URLPath builds the path part of the URL for a route. See Route.URL(). 601 | // 602 | // The route must have a path defined. 603 | func (r *Route) URLPath(pairs ...string) (*url.URL, error) { 604 | if r.err != nil { 605 | return nil, r.err 606 | } 607 | if r.regexp.path == nil { 608 | return nil, errors.New("mux: route doesn't have a path") 609 | } 610 | values, err := r.prepareVars(pairs...) 611 | if err != nil { 612 | return nil, err 613 | } 614 | path, err := r.regexp.path.url(values) 615 | if err != nil { 616 | return nil, err 617 | } 618 | return &url.URL{ 619 | Path: path, 620 | }, nil 621 | } 622 | 623 | // GetPathTemplate returns the template used to build the 624 | // route match. 625 | // This is useful for building simple REST API documentation and for instrumentation 626 | // against third-party services. 627 | // An error will be returned if the route does not define a path. 628 | func (r *Route) GetPathTemplate() (string, error) { 629 | if r.err != nil { 630 | return "", r.err 631 | } 632 | if r.regexp.path == nil { 633 | return "", errors.New("mux: route doesn't have a path") 634 | } 635 | return r.regexp.path.template, nil 636 | } 637 | 638 | // GetPathRegexp returns the expanded regular expression used to match route path. 639 | // This is useful for building simple REST API documentation and for instrumentation 640 | // against third-party services. 641 | // An error will be returned if the route does not define a path. 642 | func (r *Route) GetPathRegexp() (string, error) { 643 | if r.err != nil { 644 | return "", r.err 645 | } 646 | if r.regexp.path == nil { 647 | return "", errors.New("mux: route does not have a path") 648 | } 649 | return r.regexp.path.regexp.String(), nil 650 | } 651 | 652 | // GetQueriesRegexp returns the expanded regular expressions used to match the 653 | // route queries. 654 | // This is useful for building simple REST API documentation and for instrumentation 655 | // against third-party services. 656 | // An error will be returned if the route does not have queries. 657 | func (r *Route) GetQueriesRegexp() ([]string, error) { 658 | if r.err != nil { 659 | return nil, r.err 660 | } 661 | if r.regexp.queries == nil { 662 | return nil, errors.New("mux: route doesn't have queries") 663 | } 664 | queries := make([]string, 0, len(r.regexp.queries)) 665 | for _, query := range r.regexp.queries { 666 | queries = append(queries, query.regexp.String()) 667 | } 668 | return queries, nil 669 | } 670 | 671 | // GetQueriesTemplates returns the templates used to build the 672 | // query matching. 673 | // This is useful for building simple REST API documentation and for instrumentation 674 | // against third-party services. 675 | // An error will be returned if the route does not define queries. 676 | func (r *Route) GetQueriesTemplates() ([]string, error) { 677 | if r.err != nil { 678 | return nil, r.err 679 | } 680 | if r.regexp.queries == nil { 681 | return nil, errors.New("mux: route doesn't have queries") 682 | } 683 | queries := make([]string, 0, len(r.regexp.queries)) 684 | for _, query := range r.regexp.queries { 685 | queries = append(queries, query.template) 686 | } 687 | return queries, nil 688 | } 689 | 690 | // GetMethods returns the methods the route matches against 691 | // This is useful for building simple REST API documentation and for instrumentation 692 | // against third-party services. 693 | // An error will be returned if route does not have methods. 694 | func (r *Route) GetMethods() ([]string, error) { 695 | if r.err != nil { 696 | return nil, r.err 697 | } 698 | for _, m := range r.matchers { 699 | if methods, ok := m.(methodMatcher); ok { 700 | return []string(methods), nil 701 | } 702 | } 703 | return nil, errors.New("mux: route doesn't have methods") 704 | } 705 | 706 | // GetHostTemplate returns the template used to build the 707 | // route match. 708 | // This is useful for building simple REST API documentation and for instrumentation 709 | // against third-party services. 710 | // An error will be returned if the route does not define a host. 711 | func (r *Route) GetHostTemplate() (string, error) { 712 | if r.err != nil { 713 | return "", r.err 714 | } 715 | if r.regexp.host == nil { 716 | return "", errors.New("mux: route doesn't have a host") 717 | } 718 | return r.regexp.host.template, nil 719 | } 720 | 721 | // prepareVars converts the route variable pairs into a map. If the route has a 722 | // BuildVarsFunc, it is invoked. 723 | func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { 724 | m, err := mapFromPairsToString(pairs...) 725 | if err != nil { 726 | return nil, err 727 | } 728 | return r.buildVars(m), nil 729 | } 730 | 731 | func (r *Route) buildVars(m map[string]string) map[string]string { 732 | if r.buildVarsFunc != nil { 733 | m = r.buildVarsFunc(m) 734 | } 735 | return m 736 | } 737 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/test_helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla 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 mux 6 | 7 | import "net/http" 8 | 9 | // SetURLVars sets the URL variables for the given request, to be accessed via 10 | // mux.Vars for testing route behaviour. Arguments are not modified, a shallow 11 | // copy is returned. 12 | // 13 | // This API should only be used for testing purposes; it provides a way to 14 | // inject variables into the request context. Alternatively, URL variables 15 | // can be set by making a route that captures the required variables, 16 | // starting a server and sending the request to that server. 17 | func SetURLVars(r *http.Request, val map[string]string) *http.Request { 18 | return requestWithVars(r, val) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | *.png 3 | *.directory 4 | qrcode/qrcode 5 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7 5 | 6 | script: 7 | - go test -v ./... 8 | 9 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Tom Harwood 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/README.md: -------------------------------------------------------------------------------- 1 | # go-qrcode # 2 | 3 | 4 | 5 | Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode) 6 | 7 | A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :) 8 | 9 | Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. 10 | 11 | ## Install 12 | 13 | go get -u github.com/skip2/go-qrcode/... 14 | 15 | A command-line tool `qrcode` will be built into `$GOPATH/bin/`. 16 | 17 | ## Usage 18 | 19 | import qrcode "github.com/skip2/go-qrcode" 20 | 21 | - **Create a 256x256 PNG image:** 22 | 23 | var png []byte 24 | png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) 25 | 26 | - **Create a 256x256 PNG image and write to a file:** 27 | 28 | err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") 29 | 30 | - **Create a 256x256 PNG image with custom colors and write to file:** 31 | 32 | err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") 33 | 34 | All examples use the qrcode.Medium error Recovery Level and create a fixed 256x256px size QR Code. The last function creates a white on black instead of black on white QR Code. 35 | 36 | ## Documentation 37 | 38 | [![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode) 39 | 40 | ## Demoapp 41 | 42 | [http://go-qrcode.appspot.com](http://go-qrcode.appspot.com) 43 | 44 | ## CLI 45 | 46 | A command-line tool `qrcode` will be built into `$GOPATH/bin/`. 47 | 48 | ``` 49 | qrcode -- QR Code encoder in Go 50 | https://github.com/skip2/go-qrcode 51 | 52 | Flags: 53 | -d disable QR Code border 54 | -i invert black and white 55 | -o string 56 | out PNG file prefix, empty for stdout 57 | -s int 58 | image size (pixel) (default 256) 59 | -t print as text-art on stdout 60 | 61 | Usage: 62 | 1. Arguments except for flags are joined by " " and used to generate QR code. 63 | Default output is STDOUT, pipe to imagemagick command "display" to display 64 | on any X server. 65 | 66 | qrcode hello word | display 67 | 68 | 2. Save to file if "display" not available: 69 | 70 | qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png 71 | 72 | ``` 73 | ## Maximum capacity 74 | The maximum capacity of a QR Code varies according to the content encoded and the error recovery level. The maximum capacity is 2,953 bytes, 4,296 alphanumeric characters, 7,089 numeric digits, or a combination of these. 75 | 76 | ## Borderless QR Codes 77 | 78 | To aid QR Code reading software, QR codes have a built in whitespace border. 79 | 80 | If you know what you're doing, and don't want a border, see https://gist.github.com/skip2/7e3d8a82f5317df9be437f8ec8ec0b7d for how to do it. It's still recommended you include a border manually. 81 | 82 | ## Links 83 | 84 | - [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code) 85 | - [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)
86 | - [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing) 87 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/bitset/bitset.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | // Package bitset implements an append only bit array. 5 | // 6 | // To create a Bitset and append some bits: 7 | // // Bitset Contents 8 | // b := bitset.New() // {} 9 | // b.AppendBools(true, true, false) // {1, 1, 0} 10 | // b.AppendBools(true) // {1, 1, 0, 1} 11 | // b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} 12 | // 13 | // To read values: 14 | // 15 | // len := b.Len() // 8 16 | // v := b.At(0) // 1 17 | // v = b.At(1) // 1 18 | // v = b.At(2) // 0 19 | // v = b.At(8) // 0 20 | package bitset 21 | 22 | import ( 23 | "bytes" 24 | "fmt" 25 | "log" 26 | ) 27 | 28 | const ( 29 | b0 = false 30 | b1 = true 31 | ) 32 | 33 | // Bitset stores an array of bits. 34 | type Bitset struct { 35 | // The number of bits stored. 36 | numBits int 37 | 38 | // Storage for individual bits. 39 | bits []byte 40 | } 41 | 42 | // New returns an initialised Bitset with optional initial bits v. 43 | func New(v ...bool) *Bitset { 44 | b := &Bitset{numBits: 0, bits: make([]byte, 0)} 45 | b.AppendBools(v...) 46 | 47 | return b 48 | } 49 | 50 | // Clone returns a copy. 51 | func Clone(from *Bitset) *Bitset { 52 | return &Bitset{numBits: from.numBits, bits: from.bits[:]} 53 | } 54 | 55 | // Substr returns a substring, consisting of the bits from indexes start to end. 56 | func (b *Bitset) Substr(start int, end int) *Bitset { 57 | if start > end || end > b.numBits { 58 | log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits) 59 | } 60 | 61 | result := New() 62 | result.ensureCapacity(end - start) 63 | 64 | for i := start; i < end; i++ { 65 | if b.At(i) { 66 | result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8) 67 | } 68 | result.numBits++ 69 | } 70 | 71 | return result 72 | } 73 | 74 | // NewFromBase2String constructs and returns a Bitset from a string. The string 75 | // consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0' 76 | // characters represent true/false bits respectively, and ' ' characters are 77 | // ignored. 78 | // 79 | // The function panics if the input string contains other characters. 80 | func NewFromBase2String(b2string string) *Bitset { 81 | b := &Bitset{numBits: 0, bits: make([]byte, 0)} 82 | 83 | for _, c := range b2string { 84 | switch c { 85 | case '1': 86 | b.AppendBools(true) 87 | case '0': 88 | b.AppendBools(false) 89 | case ' ': 90 | default: 91 | log.Panicf("Invalid char %c in NewFromBase2String", c) 92 | } 93 | } 94 | 95 | return b 96 | } 97 | 98 | // AppendBytes appends a list of whole bytes. 99 | func (b *Bitset) AppendBytes(data []byte) { 100 | for _, d := range data { 101 | b.AppendByte(d, 8) 102 | } 103 | } 104 | 105 | // AppendByte appends the numBits least significant bits from value. 106 | func (b *Bitset) AppendByte(value byte, numBits int) { 107 | b.ensureCapacity(numBits) 108 | 109 | if numBits > 8 { 110 | log.Panicf("numBits %d out of range 0-8", numBits) 111 | } 112 | 113 | for i := numBits - 1; i >= 0; i-- { 114 | if value&(1<> uint(b.numBits%8) 116 | } 117 | 118 | b.numBits++ 119 | } 120 | } 121 | 122 | // AppendUint32 appends the numBits least significant bits from value. 123 | func (b *Bitset) AppendUint32(value uint32, numBits int) { 124 | b.ensureCapacity(numBits) 125 | 126 | if numBits > 32 { 127 | log.Panicf("numBits %d out of range 0-32", numBits) 128 | } 129 | 130 | for i := numBits - 1; i >= 0; i-- { 131 | if value&(1<> uint(b.numBits%8) 133 | } 134 | 135 | b.numBits++ 136 | } 137 | } 138 | 139 | // ensureCapacity ensures the Bitset can store an additional |numBits|. 140 | // 141 | // The underlying array is expanded if necessary. To prevent frequent 142 | // reallocation, expanding the underlying array at least doubles its capacity. 143 | func (b *Bitset) ensureCapacity(numBits int) { 144 | numBits += b.numBits 145 | 146 | newNumBytes := numBits / 8 147 | if numBits%8 != 0 { 148 | newNumBytes++ 149 | } 150 | 151 | if len(b.bits) >= newNumBytes { 152 | return 153 | } 154 | 155 | b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...) 156 | } 157 | 158 | // Append bits copied from |other|. 159 | // 160 | // The new length is b.Len() + other.Len(). 161 | func (b *Bitset) Append(other *Bitset) { 162 | b.ensureCapacity(other.numBits) 163 | 164 | for i := 0; i < other.numBits; i++ { 165 | if other.At(i) { 166 | b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) 167 | } 168 | b.numBits++ 169 | } 170 | } 171 | 172 | // AppendBools appends bits to the Bitset. 173 | func (b *Bitset) AppendBools(bits ...bool) { 174 | b.ensureCapacity(len(bits)) 175 | 176 | for _, v := range bits { 177 | if v { 178 | b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) 179 | } 180 | b.numBits++ 181 | } 182 | } 183 | 184 | // AppendNumBools appends num bits of value value. 185 | func (b *Bitset) AppendNumBools(num int, value bool) { 186 | for i := 0; i < num; i++ { 187 | b.AppendBools(value) 188 | } 189 | } 190 | 191 | // String returns a human readable representation of the Bitset's contents. 192 | func (b *Bitset) String() string { 193 | var bitString string 194 | for i := 0; i < b.numBits; i++ { 195 | if (i % 8) == 0 { 196 | bitString += " " 197 | } 198 | 199 | if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 { 200 | bitString += "1" 201 | } else { 202 | bitString += "0" 203 | } 204 | } 205 | 206 | return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString) 207 | } 208 | 209 | // Len returns the length of the Bitset in bits. 210 | func (b *Bitset) Len() int { 211 | return b.numBits 212 | } 213 | 214 | // Bits returns the contents of the Bitset. 215 | func (b *Bitset) Bits() []bool { 216 | result := make([]bool, b.numBits) 217 | 218 | var i int 219 | for i = 0; i < b.numBits; i++ { 220 | result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 221 | } 222 | 223 | return result 224 | } 225 | 226 | // At returns the value of the bit at |index|. 227 | func (b *Bitset) At(index int) bool { 228 | if index >= b.numBits { 229 | log.Panicf("Index %d out of range", index) 230 | } 231 | 232 | return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0 233 | } 234 | 235 | // Equals returns true if the Bitset equals other. 236 | func (b *Bitset) Equals(other *Bitset) bool { 237 | if b.numBits != other.numBits { 238 | return false 239 | } 240 | 241 | if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) { 242 | return false 243 | } 244 | 245 | for i := 8 * (b.numBits / 8); i < b.numBits; i++ { 246 | a := (b.bits[i/8] & (0x80 >> byte(i%8))) 247 | b := (other.bits[i/8] & (0x80 >> byte(i%8))) 248 | 249 | if a != b { 250 | return false 251 | } 252 | } 253 | 254 | return true 255 | } 256 | 257 | // ByteAt returns a byte consisting of upto 8 bits starting at index. 258 | func (b *Bitset) ByteAt(index int) byte { 259 | if index < 0 || index >= b.numBits { 260 | log.Panicf("Index %d out of range", index) 261 | } 262 | 263 | var result byte 264 | 265 | for i := index; i < index+8 && i < b.numBits; i++ { 266 | result <<= 1 267 | if b.At(i) { 268 | result |= 1 269 | } 270 | } 271 | 272 | return result 273 | } 274 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/encoder.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | package qrcode 5 | 6 | import ( 7 | "errors" 8 | "log" 9 | 10 | bitset "github.com/skip2/go-qrcode/bitset" 11 | ) 12 | 13 | // Data encoding. 14 | // 15 | // The main data portion of a QR Code consists of one or more segments of data. 16 | // A segment consists of: 17 | // 18 | // - The segment Data Mode: numeric, alphanumeric, or byte. 19 | // - The length of segment in bits. 20 | // - Encoded data. 21 | // 22 | // For example, the string "123ZZ#!#!" may be represented as: 23 | // 24 | // [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"] 25 | // 26 | // Multiple data modes exist to minimise the size of encoded data. For example, 27 | // 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be 28 | // encoded at a higher density of 3 numbers (e.g. 123) per 10 bits. 29 | // 30 | // Some data can be represented in multiple modes. Numeric data can be 31 | // represented in all three modes, whereas alphanumeric data (e.g. 'A') can be 32 | // represented in alphanumeric and byte mode. 33 | // 34 | // Starting a new segment (to use a different Data Mode) has a cost, the bits to 35 | // state the new segment Data Mode and length. To minimise each QR Code's symbol 36 | // size, an optimisation routine coalesces segment types where possible, to 37 | // reduce the encoded data length. 38 | // 39 | // There are several other data modes available (e.g. Kanji mode) which are not 40 | // implemented here. 41 | 42 | // A segment encoding mode. 43 | type dataMode uint8 44 | 45 | const ( 46 | // Each dataMode is a subset of the subsequent dataMode: 47 | // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte 48 | // 49 | // This ordering is important for determining which data modes a character can 50 | // be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and 51 | // dataModeByte. 52 | dataModeNone dataMode = 1 << iota 53 | dataModeNumeric 54 | dataModeAlphanumeric 55 | dataModeByte 56 | ) 57 | 58 | // dataModeString returns d as a short printable string. 59 | func dataModeString(d dataMode) string { 60 | switch d { 61 | case dataModeNone: 62 | return "none" 63 | case dataModeNumeric: 64 | return "numeric" 65 | case dataModeAlphanumeric: 66 | return "alphanumeric" 67 | case dataModeByte: 68 | return "byte" 69 | } 70 | 71 | return "unknown" 72 | } 73 | 74 | type dataEncoderType uint8 75 | 76 | const ( 77 | dataEncoderType1To9 dataEncoderType = iota 78 | dataEncoderType10To26 79 | dataEncoderType27To40 80 | ) 81 | 82 | // segment is a single segment of data. 83 | type segment struct { 84 | // Data Mode (e.g. numeric). 85 | dataMode dataMode 86 | 87 | // segment data (e.g. "abc"). 88 | data []byte 89 | } 90 | 91 | // A dataEncoder encodes data for a particular QR Code version. 92 | type dataEncoder struct { 93 | // Minimum & maximum versions supported. 94 | minVersion int 95 | maxVersion int 96 | 97 | // Mode indicator bit sequences. 98 | numericModeIndicator *bitset.Bitset 99 | alphanumericModeIndicator *bitset.Bitset 100 | byteModeIndicator *bitset.Bitset 101 | 102 | // Character count lengths. 103 | numNumericCharCountBits int 104 | numAlphanumericCharCountBits int 105 | numByteCharCountBits int 106 | 107 | // The raw input data. 108 | data []byte 109 | 110 | // The data classified into unoptimised segments. 111 | actual []segment 112 | 113 | // The data classified into optimised segments. 114 | optimised []segment 115 | } 116 | 117 | // newDataEncoder constructs a dataEncoder. 118 | func newDataEncoder(t dataEncoderType) *dataEncoder { 119 | d := &dataEncoder{} 120 | 121 | switch t { 122 | case dataEncoderType1To9: 123 | d = &dataEncoder{ 124 | minVersion: 1, 125 | maxVersion: 9, 126 | numericModeIndicator: bitset.New(b0, b0, b0, b1), 127 | alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), 128 | byteModeIndicator: bitset.New(b0, b1, b0, b0), 129 | numNumericCharCountBits: 10, 130 | numAlphanumericCharCountBits: 9, 131 | numByteCharCountBits: 8, 132 | } 133 | case dataEncoderType10To26: 134 | d = &dataEncoder{ 135 | minVersion: 10, 136 | maxVersion: 26, 137 | numericModeIndicator: bitset.New(b0, b0, b0, b1), 138 | alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), 139 | byteModeIndicator: bitset.New(b0, b1, b0, b0), 140 | numNumericCharCountBits: 12, 141 | numAlphanumericCharCountBits: 11, 142 | numByteCharCountBits: 16, 143 | } 144 | case dataEncoderType27To40: 145 | d = &dataEncoder{ 146 | minVersion: 27, 147 | maxVersion: 40, 148 | numericModeIndicator: bitset.New(b0, b0, b0, b1), 149 | alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), 150 | byteModeIndicator: bitset.New(b0, b1, b0, b0), 151 | numNumericCharCountBits: 14, 152 | numAlphanumericCharCountBits: 13, 153 | numByteCharCountBits: 16, 154 | } 155 | default: 156 | log.Panic("Unknown dataEncoderType") 157 | } 158 | 159 | return d 160 | } 161 | 162 | // encode data as one or more segments and return the encoded data. 163 | // 164 | // The returned data does not include the terminator bit sequence. 165 | func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) { 166 | d.data = data 167 | d.actual = nil 168 | d.optimised = nil 169 | 170 | if len(data) == 0 { 171 | return nil, errors.New("no data to encode") 172 | } 173 | 174 | // Classify data into unoptimised segments. 175 | highestRequiredMode := d.classifyDataModes() 176 | 177 | // Optimise segments. 178 | err := d.optimiseDataModes() 179 | if err != nil { 180 | return nil, err 181 | } 182 | 183 | // Check if a single byte encoded segment would be more efficient. 184 | optimizedLength := 0 185 | for _, s := range d.optimised { 186 | length, err := d.encodedLength(s.dataMode, len(s.data)) 187 | if err != nil { 188 | return nil, err 189 | } 190 | optimizedLength += length 191 | } 192 | 193 | singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data)) 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | if singleByteSegmentLength <= optimizedLength { 199 | d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}} 200 | } 201 | 202 | // Encode data. 203 | encoded := bitset.New() 204 | for _, s := range d.optimised { 205 | d.encodeDataRaw(s.data, s.dataMode, encoded) 206 | } 207 | 208 | return encoded, nil 209 | } 210 | 211 | // classifyDataModes classifies the raw data into unoptimised segments. 212 | // e.g. "123ZZ#!#!" => 213 | // [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]. 214 | // 215 | // Returns the highest data mode needed to encode the data. e.g. for a mixed 216 | // numeric/alphanumeric input, the highest is alphanumeric. 217 | // 218 | // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte 219 | func (d *dataEncoder) classifyDataModes() dataMode { 220 | var start int 221 | mode := dataModeNone 222 | highestRequiredMode := mode 223 | 224 | for i, v := range d.data { 225 | newMode := dataModeNone 226 | switch { 227 | case v >= 0x30 && v <= 0x39: 228 | newMode = dataModeNumeric 229 | case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v == 230 | 0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a): 231 | newMode = dataModeAlphanumeric 232 | default: 233 | newMode = dataModeByte 234 | } 235 | 236 | if newMode != mode { 237 | if i > 0 { 238 | d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]}) 239 | 240 | start = i 241 | } 242 | 243 | mode = newMode 244 | } 245 | 246 | if newMode > highestRequiredMode { 247 | highestRequiredMode = newMode 248 | } 249 | } 250 | 251 | d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]}) 252 | 253 | return highestRequiredMode 254 | } 255 | 256 | // optimiseDataModes optimises the list of segments to reduce the overall output 257 | // encoded data length. 258 | // 259 | // The algorithm coalesces adjacent segments. segments are only coalesced when 260 | // the Data Modes are compatible, and when the coalesced segment has a shorter 261 | // encoded length than separate segments. 262 | // 263 | // Multiple segments may be coalesced. For example a string of alternating 264 | // alphanumeric/numeric segments ANANANANA can be optimised to just A. 265 | func (d *dataEncoder) optimiseDataModes() error { 266 | for i := 0; i < len(d.actual); { 267 | mode := d.actual[i].dataMode 268 | numChars := len(d.actual[i].data) 269 | 270 | j := i + 1 271 | for j < len(d.actual) { 272 | nextNumChars := len(d.actual[j].data) 273 | nextMode := d.actual[j].dataMode 274 | 275 | if nextMode > mode { 276 | break 277 | } 278 | 279 | coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars) 280 | 281 | if err != nil { 282 | return err 283 | } 284 | 285 | seperateLength1, err := d.encodedLength(mode, numChars) 286 | 287 | if err != nil { 288 | return err 289 | } 290 | 291 | seperateLength2, err := d.encodedLength(nextMode, nextNumChars) 292 | 293 | if err != nil { 294 | return err 295 | } 296 | 297 | if coalescedLength < seperateLength1+seperateLength2 { 298 | j++ 299 | numChars += nextNumChars 300 | } else { 301 | break 302 | } 303 | } 304 | 305 | optimised := segment{dataMode: mode, 306 | data: make([]byte, 0, numChars)} 307 | 308 | for k := i; k < j; k++ { 309 | optimised.data = append(optimised.data, d.actual[k].data...) 310 | } 311 | 312 | d.optimised = append(d.optimised, optimised) 313 | 314 | i = j 315 | } 316 | 317 | return nil 318 | } 319 | 320 | // encodeDataRaw encodes data in dataMode. The encoded data is appended to 321 | // encoded. 322 | func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) { 323 | modeIndicator := d.modeIndicator(dataMode) 324 | charCountBits := d.charCountBits(dataMode) 325 | 326 | // Append mode indicator. 327 | encoded.Append(modeIndicator) 328 | 329 | // Append character count. 330 | encoded.AppendUint32(uint32(len(data)), charCountBits) 331 | 332 | // Append data. 333 | switch dataMode { 334 | case dataModeNumeric: 335 | for i := 0; i < len(data); i += 3 { 336 | charsRemaining := len(data) - i 337 | 338 | var value uint32 339 | bitsUsed := 1 340 | 341 | for j := 0; j < charsRemaining && j < 3; j++ { 342 | value *= 10 343 | value += uint32(data[i+j] - 0x30) 344 | bitsUsed += 3 345 | } 346 | encoded.AppendUint32(value, bitsUsed) 347 | } 348 | case dataModeAlphanumeric: 349 | for i := 0; i < len(data); i += 2 { 350 | charsRemaining := len(data) - i 351 | 352 | var value uint32 353 | for j := 0; j < charsRemaining && j < 2; j++ { 354 | value *= 45 355 | value += encodeAlphanumericCharacter(data[i+j]) 356 | } 357 | 358 | bitsUsed := 6 359 | if charsRemaining > 1 { 360 | bitsUsed = 11 361 | } 362 | 363 | encoded.AppendUint32(value, bitsUsed) 364 | } 365 | case dataModeByte: 366 | for _, b := range data { 367 | encoded.AppendByte(b, 8) 368 | } 369 | } 370 | } 371 | 372 | // modeIndicator returns the segment header bits for a segment of type dataMode. 373 | func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset { 374 | switch dataMode { 375 | case dataModeNumeric: 376 | return d.numericModeIndicator 377 | case dataModeAlphanumeric: 378 | return d.alphanumericModeIndicator 379 | case dataModeByte: 380 | return d.byteModeIndicator 381 | default: 382 | log.Panic("Unknown data mode") 383 | } 384 | 385 | return nil 386 | } 387 | 388 | // charCountBits returns the number of bits used to encode the length of a data 389 | // segment of type dataMode. 390 | func (d *dataEncoder) charCountBits(dataMode dataMode) int { 391 | switch dataMode { 392 | case dataModeNumeric: 393 | return d.numNumericCharCountBits 394 | case dataModeAlphanumeric: 395 | return d.numAlphanumericCharCountBits 396 | case dataModeByte: 397 | return d.numByteCharCountBits 398 | default: 399 | log.Panic("Unknown data mode") 400 | } 401 | 402 | return 0 403 | } 404 | 405 | // encodedLength returns the number of bits required to encode n symbols in 406 | // dataMode. 407 | // 408 | // The number of bits required is affected by: 409 | // - QR code type - Mode Indicator length. 410 | // - Data mode - number of bits used to represent data length. 411 | // - Data mode - how the data is encoded. 412 | // - Number of symbols encoded. 413 | // 414 | // An error is returned if the mode is not supported, or the length requested is 415 | // too long to be represented. 416 | func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) { 417 | modeIndicator := d.modeIndicator(dataMode) 418 | charCountBits := d.charCountBits(dataMode) 419 | 420 | if modeIndicator == nil { 421 | return 0, errors.New("mode not supported") 422 | } 423 | 424 | maxLength := (1 << uint8(charCountBits)) - 1 425 | 426 | if n > maxLength { 427 | return 0, errors.New("length too long to be represented") 428 | } 429 | 430 | length := modeIndicator.Len() + charCountBits 431 | 432 | switch dataMode { 433 | case dataModeNumeric: 434 | length += 10 * (n / 3) 435 | 436 | if n%3 != 0 { 437 | length += 1 + 3*(n%3) 438 | } 439 | case dataModeAlphanumeric: 440 | length += 11 * (n / 2) 441 | length += 6 * (n % 2) 442 | case dataModeByte: 443 | length += 8 * n 444 | } 445 | 446 | return length, nil 447 | } 448 | 449 | // encodeAlphanumericChar returns the QR Code encoded value of v. 450 | // 451 | // v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or 452 | // :. The characters are mapped to values in the range 0-44 respectively. 453 | func encodeAlphanumericCharacter(v byte) uint32 { 454 | c := uint32(v) 455 | 456 | switch { 457 | case c >= '0' && c <= '9': 458 | // 0-9 encoded as 0-9. 459 | return c - '0' 460 | case c >= 'A' && c <= 'Z': 461 | // A-Z encoded as 10-35. 462 | return c - 'A' + 10 463 | case c == ' ': 464 | return 36 465 | case c == '$': 466 | return 37 467 | case c == '%': 468 | return 38 469 | case c == '*': 470 | return 39 471 | case c == '+': 472 | return 40 473 | case c == '-': 474 | return 41 475 | case c == '.': 476 | return 42 477 | case c == '/': 478 | return 43 479 | case c == ':': 480 | return 44 481 | default: 482 | log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v) 483 | } 484 | 485 | return 0 486 | } 487 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/skip2/go-qrcode 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/qrcode.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | /* 5 | Package qrcode implements a QR Code encoder. 6 | 7 | A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be 8 | encoded. 9 | 10 | A QR Code contains error recovery information to aid reading damaged or 11 | obscured codes. There are four levels of error recovery: qrcode.{Low, Medium, 12 | High, Highest}. QR Codes with a higher recovery level are more robust to damage, 13 | at the cost of being physically larger. 14 | 15 | Three functions cover most use cases: 16 | 17 | - Create a PNG image: 18 | 19 | var png []byte 20 | png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) 21 | 22 | - Create a PNG image and write to a file: 23 | 24 | err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") 25 | 26 | - Create a PNG image with custom colors and write to file: 27 | 28 | err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") 29 | 30 | All examples use the qrcode.Medium error Recovery Level and create a fixed 31 | 256x256px size QR Code. The last function creates a white on black instead of black 32 | on white QR Code. 33 | 34 | To generate a variable sized image instead, specify a negative size (in place of 35 | the 256 above), such as -4 or -5. Larger negative numbers create larger images: 36 | A size of -5 sets each module (QR Code "pixel") to be 5px wide/high. 37 | 38 | - Create a PNG image (variable size, with minimum white padding) and write to a file: 39 | 40 | err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png") 41 | 42 | The maximum capacity of a QR Code varies according to the content encoded and 43 | the error recovery level. The maximum capacity is 2,953 bytes, 4,296 44 | alphanumeric characters, 7,089 numeric digits, or a combination of these. 45 | 46 | This package implements a subset of QR Code 2005, as defined in ISO/IEC 47 | 18004:2006. 48 | */ 49 | package qrcode 50 | 51 | import ( 52 | "bytes" 53 | "errors" 54 | "fmt" 55 | "image" 56 | "image/color" 57 | "image/png" 58 | "io" 59 | "io/ioutil" 60 | "log" 61 | "os" 62 | 63 | bitset "github.com/skip2/go-qrcode/bitset" 64 | reedsolomon "github.com/skip2/go-qrcode/reedsolomon" 65 | ) 66 | 67 | // Encode a QR Code and return a raw PNG image. 68 | // 69 | // size is both the image width and height in pixels. If size is too small then 70 | // a larger image is silently returned. Negative values for size cause a 71 | // variable sized image to be returned: See the documentation for Image(). 72 | // 73 | // To serve over HTTP, remember to send a Content-Type: image/png header. 74 | func Encode(content string, level RecoveryLevel, size int) ([]byte, error) { 75 | var q *QRCode 76 | 77 | q, err := New(content, level) 78 | 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return q.PNG(size) 84 | } 85 | 86 | // WriteFile encodes, then writes a QR Code to the given filename in PNG format. 87 | // 88 | // size is both the image width and height in pixels. If size is too small then 89 | // a larger image is silently written. Negative values for size cause a variable 90 | // sized image to be written: See the documentation for Image(). 91 | func WriteFile(content string, level RecoveryLevel, size int, filename string) error { 92 | var q *QRCode 93 | 94 | q, err := New(content, level) 95 | 96 | if err != nil { 97 | return err 98 | } 99 | 100 | return q.WriteFile(size, filename) 101 | } 102 | 103 | // WriteColorFile encodes, then writes a QR Code to the given filename in PNG format. 104 | // With WriteColorFile you can also specify the colors you want to use. 105 | // 106 | // size is both the image width and height in pixels. If size is too small then 107 | // a larger image is silently written. Negative values for size cause a variable 108 | // sized image to be written: See the documentation for Image(). 109 | func WriteColorFile(content string, level RecoveryLevel, size int, background, 110 | foreground color.Color, filename string) error { 111 | 112 | var q *QRCode 113 | 114 | q, err := New(content, level) 115 | 116 | q.BackgroundColor = background 117 | q.ForegroundColor = foreground 118 | 119 | if err != nil { 120 | return err 121 | } 122 | 123 | return q.WriteFile(size, filename) 124 | } 125 | 126 | // A QRCode represents a valid encoded QRCode. 127 | type QRCode struct { 128 | // Original content encoded. 129 | Content string 130 | 131 | // QR Code type. 132 | Level RecoveryLevel 133 | VersionNumber int 134 | 135 | // User settable drawing options. 136 | ForegroundColor color.Color 137 | BackgroundColor color.Color 138 | 139 | // Disable the QR Code border. 140 | DisableBorder bool 141 | 142 | encoder *dataEncoder 143 | version qrCodeVersion 144 | 145 | data *bitset.Bitset 146 | symbol *symbol 147 | mask int 148 | } 149 | 150 | // New constructs a QRCode. 151 | // 152 | // var q *qrcode.QRCode 153 | // q, err := qrcode.New("my content", qrcode.Medium) 154 | // 155 | // An error occurs if the content is too long. 156 | func New(content string, level RecoveryLevel) (*QRCode, error) { 157 | encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26, 158 | dataEncoderType27To40} 159 | 160 | var encoder *dataEncoder 161 | var encoded *bitset.Bitset 162 | var chosenVersion *qrCodeVersion 163 | var err error 164 | 165 | for _, t := range encoders { 166 | encoder = newDataEncoder(t) 167 | encoded, err = encoder.encode([]byte(content)) 168 | 169 | if err != nil { 170 | continue 171 | } 172 | 173 | chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len()) 174 | 175 | if chosenVersion != nil { 176 | break 177 | } 178 | } 179 | 180 | if err != nil { 181 | return nil, err 182 | } else if chosenVersion == nil { 183 | return nil, errors.New("content too long to encode") 184 | } 185 | 186 | q := &QRCode{ 187 | Content: content, 188 | 189 | Level: level, 190 | VersionNumber: chosenVersion.version, 191 | 192 | ForegroundColor: color.Black, 193 | BackgroundColor: color.White, 194 | 195 | encoder: encoder, 196 | data: encoded, 197 | version: *chosenVersion, 198 | } 199 | 200 | return q, nil 201 | } 202 | 203 | // NewWithForcedVersion constructs a QRCode of a specific version. 204 | // 205 | // var q *qrcode.QRCode 206 | // q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium) 207 | // 208 | // An error occurs in case of invalid version. 209 | func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { 210 | var encoder *dataEncoder 211 | 212 | switch { 213 | case version >= 1 && version <= 9: 214 | encoder = newDataEncoder(dataEncoderType1To9) 215 | case version >= 10 && version <= 26: 216 | encoder = newDataEncoder(dataEncoderType10To26) 217 | case version >= 27 && version <= 40: 218 | encoder = newDataEncoder(dataEncoderType27To40) 219 | default: 220 | return nil, fmt.Errorf("Invalid version %d (expected 1-40 inclusive)", version) 221 | } 222 | 223 | var encoded *bitset.Bitset 224 | encoded, err := encoder.encode([]byte(content)) 225 | 226 | if err != nil { 227 | return nil, err 228 | } 229 | 230 | chosenVersion := getQRCodeVersion(level, version) 231 | 232 | if chosenVersion == nil { 233 | return nil, errors.New("cannot find QR Code version") 234 | } 235 | 236 | if encoded.Len() > chosenVersion.numDataBits() { 237 | return nil, fmt.Errorf("Cannot encode QR code: content too large for fixed size QR Code version %d (encoded length is %d bits, maximum length is %d bits)", 238 | version, 239 | encoded.Len(), 240 | chosenVersion.numDataBits()) 241 | } 242 | 243 | q := &QRCode{ 244 | Content: content, 245 | 246 | Level: level, 247 | VersionNumber: chosenVersion.version, 248 | 249 | ForegroundColor: color.Black, 250 | BackgroundColor: color.White, 251 | 252 | encoder: encoder, 253 | data: encoded, 254 | version: *chosenVersion, 255 | } 256 | 257 | return q, nil 258 | } 259 | 260 | // Bitmap returns the QR Code as a 2D array of 1-bit pixels. 261 | // 262 | // bitmap[y][x] is true if the pixel at (x, y) is set. 263 | // 264 | // The bitmap includes the required "quiet zone" around the QR Code to aid 265 | // decoding. 266 | func (q *QRCode) Bitmap() [][]bool { 267 | // Build QR code. 268 | q.encode() 269 | 270 | return q.symbol.bitmap() 271 | } 272 | 273 | // Image returns the QR Code as an image.Image. 274 | // 275 | // A positive size sets a fixed image width and height (e.g. 256 yields an 276 | // 256x256px image). 277 | // 278 | // Depending on the amount of data encoded, fixed size images can have different 279 | // amounts of padding (white space around the QR Code). As an alternative, a 280 | // variable sized image can be generated instead: 281 | // 282 | // A negative size causes a variable sized image to be returned. The image 283 | // returned is the minimum size required for the QR Code. Choose a larger 284 | // negative number to increase the scale of the image. e.g. a size of -5 causes 285 | // each module (QR Code "pixel") to be 5px in size. 286 | func (q *QRCode) Image(size int) image.Image { 287 | // Build QR code. 288 | q.encode() 289 | 290 | // Minimum pixels (both width and height) required. 291 | realSize := q.symbol.size 292 | 293 | // Variable size support. 294 | if size < 0 { 295 | size = size * -1 * realSize 296 | } 297 | 298 | // Actual pixels available to draw the symbol. Automatically increase the 299 | // image size if it's not large enough. 300 | if size < realSize { 301 | size = realSize 302 | } 303 | 304 | // Output image. 305 | rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}} 306 | 307 | // Saves a few bytes to have them in this order 308 | p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor}) 309 | img := image.NewPaletted(rect, p) 310 | fgClr := uint8(img.Palette.Index(q.ForegroundColor)) 311 | 312 | // QR code bitmap. 313 | bitmap := q.symbol.bitmap() 314 | 315 | // Map each image pixel to the nearest QR code module. 316 | modulesPerPixel := float64(realSize) / float64(size) 317 | for y := 0; y < size; y++ { 318 | y2 := int(float64(y) * modulesPerPixel) 319 | for x := 0; x < size; x++ { 320 | x2 := int(float64(x) * modulesPerPixel) 321 | 322 | v := bitmap[y2][x2] 323 | 324 | if v { 325 | pos := img.PixOffset(x, y) 326 | img.Pix[pos] = fgClr 327 | } 328 | } 329 | } 330 | 331 | return img 332 | } 333 | 334 | // PNG returns the QR Code as a PNG image. 335 | // 336 | // size is both the image width and height in pixels. If size is too small then 337 | // a larger image is silently returned. Negative values for size cause a 338 | // variable sized image to be returned: See the documentation for Image(). 339 | func (q *QRCode) PNG(size int) ([]byte, error) { 340 | img := q.Image(size) 341 | 342 | encoder := png.Encoder{CompressionLevel: png.BestCompression} 343 | 344 | var b bytes.Buffer 345 | err := encoder.Encode(&b, img) 346 | 347 | if err != nil { 348 | return nil, err 349 | } 350 | 351 | return b.Bytes(), nil 352 | } 353 | 354 | // Write writes the QR Code as a PNG image to io.Writer. 355 | // 356 | // size is both the image width and height in pixels. If size is too small then 357 | // a larger image is silently written. Negative values for size cause a 358 | // variable sized image to be written: See the documentation for Image(). 359 | func (q *QRCode) Write(size int, out io.Writer) error { 360 | var png []byte 361 | 362 | png, err := q.PNG(size) 363 | 364 | if err != nil { 365 | return err 366 | } 367 | _, err = out.Write(png) 368 | return err 369 | } 370 | 371 | // WriteFile writes the QR Code as a PNG image to the specified file. 372 | // 373 | // size is both the image width and height in pixels. If size is too small then 374 | // a larger image is silently written. Negative values for size cause a 375 | // variable sized image to be written: See the documentation for Image(). 376 | func (q *QRCode) WriteFile(size int, filename string) error { 377 | var png []byte 378 | 379 | png, err := q.PNG(size) 380 | 381 | if err != nil { 382 | return err 383 | } 384 | 385 | return ioutil.WriteFile(filename, png, os.FileMode(0644)) 386 | } 387 | 388 | // encode completes the steps required to encode the QR Code. These include 389 | // adding the terminator bits and padding, splitting the data into blocks and 390 | // applying the error correction, and selecting the best data mask. 391 | func (q *QRCode) encode() { 392 | numTerminatorBits := q.version.numTerminatorBitsRequired(q.data.Len()) 393 | 394 | q.addTerminatorBits(numTerminatorBits) 395 | q.addPadding() 396 | 397 | encoded := q.encodeBlocks() 398 | 399 | const numMasks int = 8 400 | penalty := 0 401 | 402 | for mask := 0; mask < numMasks; mask++ { 403 | var s *symbol 404 | var err error 405 | 406 | s, err = buildRegularSymbol(q.version, mask, encoded, !q.DisableBorder) 407 | 408 | if err != nil { 409 | log.Panic(err.Error()) 410 | } 411 | 412 | numEmptyModules := s.numEmptyModules() 413 | if numEmptyModules != 0 { 414 | log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)", 415 | numEmptyModules, q.VersionNumber) 416 | } 417 | 418 | p := s.penaltyScore() 419 | 420 | //log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4()) 421 | 422 | if q.symbol == nil || p < penalty { 423 | q.symbol = s 424 | q.mask = mask 425 | penalty = p 426 | } 427 | } 428 | } 429 | 430 | // addTerminatorBits adds final terminator bits to the encoded data. 431 | // 432 | // The number of terminator bits required is determined when the QR Code version 433 | // is chosen (which itself depends on the length of the data encoded). The 434 | // terminator bits are thus added after the QR Code version 435 | // is chosen, rather than at the data encoding stage. 436 | func (q *QRCode) addTerminatorBits(numTerminatorBits int) { 437 | q.data.AppendNumBools(numTerminatorBits, false) 438 | } 439 | 440 | // encodeBlocks takes the completed (terminated & padded) encoded data, splits 441 | // the data into blocks (as specified by the QR Code version), applies error 442 | // correction to each block, then interleaves the blocks together. 443 | // 444 | // The QR Code's final data sequence is returned. 445 | func (q *QRCode) encodeBlocks() *bitset.Bitset { 446 | // Split into blocks. 447 | type dataBlock struct { 448 | data *bitset.Bitset 449 | ecStartOffset int 450 | } 451 | 452 | block := make([]dataBlock, q.version.numBlocks()) 453 | 454 | start := 0 455 | end := 0 456 | blockID := 0 457 | 458 | for _, b := range q.version.block { 459 | for j := 0; j < b.numBlocks; j++ { 460 | start = end 461 | end = start + b.numDataCodewords*8 462 | 463 | // Apply error correction to each block. 464 | numErrorCodewords := b.numCodewords - b.numDataCodewords 465 | block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords) 466 | block[blockID].ecStartOffset = end - start 467 | 468 | blockID++ 469 | } 470 | } 471 | 472 | // Interleave the blocks. 473 | 474 | result := bitset.New() 475 | 476 | // Combine data blocks. 477 | working := true 478 | for i := 0; working; i += 8 { 479 | working = false 480 | 481 | for j, b := range block { 482 | if i >= block[j].ecStartOffset { 483 | continue 484 | } 485 | 486 | result.Append(b.data.Substr(i, i+8)) 487 | 488 | working = true 489 | } 490 | } 491 | 492 | // Combine error correction blocks. 493 | working = true 494 | for i := 0; working; i += 8 { 495 | working = false 496 | 497 | for j, b := range block { 498 | offset := i + block[j].ecStartOffset 499 | if offset >= block[j].data.Len() { 500 | continue 501 | } 502 | 503 | result.Append(b.data.Substr(offset, offset+8)) 504 | 505 | working = true 506 | } 507 | } 508 | 509 | // Append remainder bits. 510 | result.AppendNumBools(q.version.numRemainderBits, false) 511 | 512 | return result 513 | } 514 | 515 | // max returns the maximum of a and b. 516 | func max(a int, b int) int { 517 | if a > b { 518 | return a 519 | } 520 | 521 | return b 522 | } 523 | 524 | // addPadding pads the encoded data upto the full length required. 525 | func (q *QRCode) addPadding() { 526 | numDataBits := q.version.numDataBits() 527 | 528 | if q.data.Len() == numDataBits { 529 | return 530 | } 531 | 532 | // Pad to the nearest codeword boundary. 533 | q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false) 534 | 535 | // Pad codewords 0b11101100 and 0b00010001. 536 | padding := [2]*bitset.Bitset{ 537 | bitset.New(true, true, true, false, true, true, false, false), 538 | bitset.New(false, false, false, true, false, false, false, true), 539 | } 540 | 541 | // Insert pad codewords alternately. 542 | i := 0 543 | for numDataBits-q.data.Len() >= 8 { 544 | q.data.Append(padding[i]) 545 | 546 | i = 1 - i // Alternate between 0 and 1. 547 | } 548 | 549 | if q.data.Len() != numDataBits { 550 | log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits) 551 | } 552 | } 553 | 554 | // ToString produces a multi-line string that forms a QR-code image. 555 | func (q *QRCode) ToString(inverseColor bool) string { 556 | bits := q.Bitmap() 557 | var buf bytes.Buffer 558 | for y := range bits { 559 | for x := range bits[y] { 560 | if bits[y][x] != inverseColor { 561 | buf.WriteString(" ") 562 | } else { 563 | buf.WriteString("██") 564 | } 565 | } 566 | buf.WriteString("\n") 567 | } 568 | return buf.String() 569 | } 570 | 571 | // ToSmallString produces a multi-line string that forms a QR-code image, a 572 | // factor two smaller in x and y then ToString. 573 | func (q *QRCode) ToSmallString(inverseColor bool) string { 574 | bits := q.Bitmap() 575 | var buf bytes.Buffer 576 | // if there is an odd number of rows, the last one needs special treatment 577 | for y := 0; y < len(bits)-1; y += 2 { 578 | for x := range bits[y] { 579 | if bits[y][x] == bits[y+1][x] { 580 | if bits[y][x] != inverseColor { 581 | buf.WriteString(" ") 582 | } else { 583 | buf.WriteString("█") 584 | } 585 | } else { 586 | if bits[y][x] != inverseColor { 587 | buf.WriteString("▄") 588 | } else { 589 | buf.WriteString("▀") 590 | } 591 | } 592 | } 593 | buf.WriteString("\n") 594 | } 595 | // special treatment for the last row if odd 596 | if len(bits)%2 == 1 { 597 | y := len(bits) - 1 598 | for x := range bits[y] { 599 | if bits[y][x] != inverseColor { 600 | buf.WriteString(" ") 601 | } else { 602 | buf.WriteString("▀") 603 | } 604 | } 605 | buf.WriteString("\n") 606 | } 607 | return buf.String() 608 | } 609 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | package reedsolomon 5 | 6 | // Addition, subtraction, multiplication, and division in GF(2^8). 7 | // Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1. 8 | 9 | // http://en.wikipedia.org/wiki/Finite_field_arithmetic 10 | 11 | import "log" 12 | 13 | const ( 14 | gfZero = gfElement(0) 15 | gfOne = gfElement(1) 16 | ) 17 | 18 | var ( 19 | gfExpTable = [256]gfElement{ 20 | /* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 21 | /* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 22 | /* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 23 | /* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 24 | /* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 25 | /* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 26 | /* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 27 | /* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 28 | /* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 29 | /* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 30 | /* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 31 | /* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 32 | /* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 33 | /* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 34 | /* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 35 | /* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 36 | /* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 37 | /* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 38 | /* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 39 | /* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 40 | /* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 41 | /* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 42 | /* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 43 | /* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44 | /* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 45 | /* 250 - 255 */ 108, 216, 173, 71, 142, 1} 46 | 47 | gfLogTable = [256]int{ 48 | /* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223, 49 | /* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 50 | /* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 51 | /* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 52 | /* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 53 | /* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 54 | /* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 55 | /* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 56 | /* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 57 | /* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 58 | /* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 59 | /* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 60 | /* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 61 | /* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 62 | /* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 63 | /* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 64 | /* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 65 | /* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 66 | /* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 67 | /* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 68 | /* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 69 | /* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 70 | /* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 71 | /* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 72 | /* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 73 | /* 250 - 255 */ 244, 234, 168, 80, 88, 175} 74 | ) 75 | 76 | // gfElement is an element in GF(2^8). 77 | type gfElement uint8 78 | 79 | // newGFElement creates and returns a new gfElement. 80 | func newGFElement(data byte) gfElement { 81 | return gfElement(data) 82 | } 83 | 84 | // gfAdd returns a + b. 85 | func gfAdd(a, b gfElement) gfElement { 86 | return a ^ b 87 | } 88 | 89 | // gfSub returns a - b. 90 | // 91 | // Note addition is equivalent to subtraction in GF(2). 92 | func gfSub(a, b gfElement) gfElement { 93 | return a ^ b 94 | } 95 | 96 | // gfMultiply returns a * b. 97 | func gfMultiply(a, b gfElement) gfElement { 98 | if a == gfZero || b == gfZero { 99 | return gfZero 100 | } 101 | 102 | return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255] 103 | } 104 | 105 | // gfDivide returns a / b. 106 | // 107 | // Divide by zero results in a panic. 108 | func gfDivide(a, b gfElement) gfElement { 109 | if a == gfZero { 110 | return gfZero 111 | } else if b == gfZero { 112 | log.Panicln("Divide by zero") 113 | } 114 | 115 | return gfMultiply(a, gfInverse(b)) 116 | } 117 | 118 | // gfInverse returns the multiplicative inverse of a, a^-1. 119 | // 120 | // a * a^-1 = 1 121 | func gfInverse(a gfElement) gfElement { 122 | if a == gfZero { 123 | log.Panicln("No multiplicative inverse of 0") 124 | } 125 | 126 | return gfExpTable[255-gfLogTable[a]] 127 | } 128 | 129 | // a^i | bits | polynomial | decimal 130 | // -------------------------------------------------------------------------- 131 | // 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0 132 | // a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 133 | // a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2 134 | // a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4 135 | // a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8 136 | // a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16 137 | // a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32 138 | // a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64 139 | // a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128 140 | // a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29 141 | // a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58 142 | // a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116 143 | // a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232 144 | // a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205 145 | // a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135 146 | // a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19 147 | // a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38 148 | // a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76 149 | // a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152 150 | // a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45 151 | // a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90 152 | // a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180 153 | // a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117 154 | // a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234 155 | // a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201 156 | // a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143 157 | // a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3 158 | // a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6 159 | // a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12 160 | // a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24 161 | // a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48 162 | // a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96 163 | // a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192 164 | // a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157 165 | // a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39 166 | // a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78 167 | // a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156 168 | // a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37 169 | // a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74 170 | // a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148 171 | // a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53 172 | // a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106 173 | // a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212 174 | // a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181 175 | // a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119 176 | // a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238 177 | // a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193 178 | // a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159 179 | // a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35 180 | // a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70 181 | // a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140 182 | // a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5 183 | // a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10 184 | // a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20 185 | // a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40 186 | // a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80 187 | // a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160 188 | // a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93 189 | // a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186 190 | // a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105 191 | // a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210 192 | // a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185 193 | // a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111 194 | // a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222 195 | // a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161 196 | // a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95 197 | // a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190 198 | // a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97 199 | // a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194 200 | // a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153 201 | // a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47 202 | // a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94 203 | // a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188 204 | // a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101 205 | // a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202 206 | // a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137 207 | // a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15 208 | // a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30 209 | // a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60 210 | // a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120 211 | // a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240 212 | // a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253 213 | // a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231 214 | // a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211 215 | // a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187 216 | // a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107 217 | // a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214 218 | // a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177 219 | // a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127 220 | // a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254 221 | // a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225 222 | // a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223 223 | // a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163 224 | // a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91 225 | // a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182 226 | // a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113 227 | // a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226 228 | // a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217 229 | // a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175 230 | // a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67 231 | // a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134 232 | // a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17 233 | // a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34 234 | // a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68 235 | // a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136 236 | // a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13 237 | // a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26 238 | // a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52 239 | // a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104 240 | // a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208 241 | // a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189 242 | // a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103 243 | // a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206 244 | // a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129 245 | // a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31 246 | // a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62 247 | // a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124 248 | // a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248 249 | // a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237 250 | // a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199 251 | // a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147 252 | // a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59 253 | // a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118 254 | // a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236 255 | // a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197 256 | // a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151 257 | // a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51 258 | // a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102 259 | // a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204 260 | // a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133 261 | // a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23 262 | // a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46 263 | // a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92 264 | // a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184 265 | // a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109 266 | // a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218 267 | // a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169 268 | // a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79 269 | // a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158 270 | // a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33 271 | // a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66 272 | // a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132 273 | // a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21 274 | // a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42 275 | // a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84 276 | // a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168 277 | // a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77 278 | // a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154 279 | // a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41 280 | // a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82 281 | // a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164 282 | // a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85 283 | // a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170 284 | // a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73 285 | // a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146 286 | // a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57 287 | // a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114 288 | // a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228 289 | // a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213 290 | // a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183 291 | // a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115 292 | // a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230 293 | // a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209 294 | // a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191 295 | // a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99 296 | // a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198 297 | // a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145 298 | // a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63 299 | // a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126 300 | // a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252 301 | // a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229 302 | // a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215 303 | // a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179 304 | // a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123 305 | // a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246 306 | // a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241 307 | // a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255 308 | // a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227 309 | // a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219 310 | // a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171 311 | // a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75 312 | // a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150 313 | // a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49 314 | // a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98 315 | // a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196 316 | // a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149 317 | // a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55 318 | // a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110 319 | // a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220 320 | // a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165 321 | // a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87 322 | // a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174 323 | // a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65 324 | // a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130 325 | // a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25 326 | // a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50 327 | // a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100 328 | // a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200 329 | // a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141 330 | // a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7 331 | // a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14 332 | // a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28 333 | // a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56 334 | // a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112 335 | // a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224 336 | // a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221 337 | // a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167 338 | // a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83 339 | // a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166 340 | // a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81 341 | // a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162 342 | // a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89 343 | // a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178 344 | // a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121 345 | // a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242 346 | // a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249 347 | // a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239 348 | // a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195 349 | // a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155 350 | // a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43 351 | // a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86 352 | // a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172 353 | // a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69 354 | // a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138 355 | // a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9 356 | // a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18 357 | // a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36 358 | // a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72 359 | // a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144 360 | // a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61 361 | // a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122 362 | // a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244 363 | // a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245 364 | // a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247 365 | // a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243 366 | // a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251 367 | // a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235 368 | // a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203 369 | // a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139 370 | // a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11 371 | // a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22 372 | // a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44 373 | // a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88 374 | // a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176 375 | // a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125 376 | // a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250 377 | // a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233 378 | // a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207 379 | // a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131 380 | // a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27 381 | // a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54 382 | // a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108 383 | // a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216 384 | // a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173 385 | // a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71 386 | // a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142 387 | // a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 388 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | package reedsolomon 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | 10 | bitset "github.com/skip2/go-qrcode/bitset" 11 | ) 12 | 13 | // gfPoly is a polynomial over GF(2^8). 14 | type gfPoly struct { 15 | // The ith value is the coefficient of the ith degree of x. 16 | // term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ... 17 | term []gfElement 18 | } 19 | 20 | // newGFPolyFromData returns |data| as a polynomial over GF(2^8). 21 | // 22 | // Each data byte becomes the coefficient of an x term. 23 | // 24 | // For an n byte input the polynomial is: 25 | // data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0). 26 | func newGFPolyFromData(data *bitset.Bitset) gfPoly { 27 | numTotalBytes := data.Len() / 8 28 | if data.Len()%8 != 0 { 29 | numTotalBytes++ 30 | } 31 | 32 | result := gfPoly{term: make([]gfElement, numTotalBytes)} 33 | 34 | i := numTotalBytes - 1 35 | for j := 0; j < data.Len(); j += 8 { 36 | result.term[i] = gfElement(data.ByteAt(j)) 37 | i-- 38 | } 39 | 40 | return result 41 | } 42 | 43 | // newGFPolyMonomial returns term*(x^degree). 44 | func newGFPolyMonomial(term gfElement, degree int) gfPoly { 45 | if term == gfZero { 46 | return gfPoly{} 47 | } 48 | 49 | result := gfPoly{term: make([]gfElement, degree+1)} 50 | result.term[degree] = term 51 | 52 | return result 53 | } 54 | 55 | func (e gfPoly) data(numTerms int) []byte { 56 | result := make([]byte, numTerms) 57 | 58 | i := numTerms - len(e.term) 59 | for j := len(e.term) - 1; j >= 0; j-- { 60 | result[i] = byte(e.term[j]) 61 | i++ 62 | } 63 | 64 | return result 65 | } 66 | 67 | // numTerms returns the number of 68 | func (e gfPoly) numTerms() int { 69 | return len(e.term) 70 | } 71 | 72 | // gfPolyMultiply returns a * b. 73 | func gfPolyMultiply(a, b gfPoly) gfPoly { 74 | numATerms := a.numTerms() 75 | numBTerms := b.numTerms() 76 | 77 | result := gfPoly{term: make([]gfElement, numATerms+numBTerms)} 78 | 79 | for i := 0; i < numATerms; i++ { 80 | for j := 0; j < numBTerms; j++ { 81 | if a.term[i] != 0 && b.term[j] != 0 { 82 | monomial := gfPoly{term: make([]gfElement, i+j+1)} 83 | monomial.term[i+j] = gfMultiply(a.term[i], b.term[j]) 84 | 85 | result = gfPolyAdd(result, monomial) 86 | } 87 | } 88 | } 89 | 90 | return result.normalised() 91 | } 92 | 93 | // gfPolyRemainder return the remainder of numerator / denominator. 94 | func gfPolyRemainder(numerator, denominator gfPoly) gfPoly { 95 | if denominator.equals(gfPoly{}) { 96 | log.Panicln("Remainder by zero") 97 | } 98 | 99 | remainder := numerator 100 | 101 | for remainder.numTerms() >= denominator.numTerms() { 102 | degree := remainder.numTerms() - denominator.numTerms() 103 | coefficient := gfDivide(remainder.term[remainder.numTerms()-1], 104 | denominator.term[denominator.numTerms()-1]) 105 | 106 | divisor := gfPolyMultiply(denominator, 107 | newGFPolyMonomial(coefficient, degree)) 108 | 109 | remainder = gfPolyAdd(remainder, divisor) 110 | } 111 | 112 | return remainder.normalised() 113 | } 114 | 115 | // gfPolyAdd returns a + b. 116 | func gfPolyAdd(a, b gfPoly) gfPoly { 117 | numATerms := a.numTerms() 118 | numBTerms := b.numTerms() 119 | 120 | numTerms := numATerms 121 | if numBTerms > numTerms { 122 | numTerms = numBTerms 123 | } 124 | 125 | result := gfPoly{term: make([]gfElement, numTerms)} 126 | 127 | for i := 0; i < numTerms; i++ { 128 | switch { 129 | case numATerms > i && numBTerms > i: 130 | result.term[i] = gfAdd(a.term[i], b.term[i]) 131 | case numATerms > i: 132 | result.term[i] = a.term[i] 133 | default: 134 | result.term[i] = b.term[i] 135 | } 136 | } 137 | 138 | return result.normalised() 139 | } 140 | 141 | func (e gfPoly) normalised() gfPoly { 142 | numTerms := e.numTerms() 143 | maxNonzeroTerm := numTerms - 1 144 | 145 | for i := numTerms - 1; i >= 0; i-- { 146 | if e.term[i] != 0 { 147 | break 148 | } 149 | 150 | maxNonzeroTerm = i - 1 151 | } 152 | 153 | if maxNonzeroTerm < 0 { 154 | return gfPoly{} 155 | } else if maxNonzeroTerm < numTerms-1 { 156 | e.term = e.term[0 : maxNonzeroTerm+1] 157 | } 158 | 159 | return e 160 | } 161 | 162 | func (e gfPoly) string(useIndexForm bool) string { 163 | var str string 164 | numTerms := e.numTerms() 165 | 166 | for i := numTerms - 1; i >= 0; i-- { 167 | if e.term[i] > 0 { 168 | if len(str) > 0 { 169 | str += " + " 170 | } 171 | 172 | if !useIndexForm { 173 | str += fmt.Sprintf("%dx^%d", e.term[i], i) 174 | } else { 175 | str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i) 176 | } 177 | } 178 | } 179 | 180 | if len(str) == 0 { 181 | str = "0" 182 | } 183 | 184 | return str 185 | } 186 | 187 | // equals returns true if e == other. 188 | func (e gfPoly) equals(other gfPoly) bool { 189 | var minecPoly *gfPoly 190 | var maxecPoly *gfPoly 191 | 192 | if e.numTerms() > other.numTerms() { 193 | minecPoly = &other 194 | maxecPoly = &e 195 | } else { 196 | minecPoly = &e 197 | maxecPoly = &other 198 | } 199 | 200 | numMinTerms := minecPoly.numTerms() 201 | numMaxTerms := maxecPoly.numTerms() 202 | 203 | for i := 0; i < numMinTerms; i++ { 204 | if e.term[i] != other.term[i] { 205 | return false 206 | } 207 | } 208 | 209 | for i := numMinTerms; i < numMaxTerms; i++ { 210 | if maxecPoly.term[i] != 0 { 211 | return false 212 | } 213 | } 214 | 215 | return true 216 | } 217 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | // Package reedsolomon provides error correction encoding for QR Code 2005. 5 | // 6 | // QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct 7 | // errors encountered during decoding. 8 | // 9 | // The generated RS codes are systematic, and consist of the input data with 10 | // error correction bytes appended. 11 | package reedsolomon 12 | 13 | import ( 14 | "log" 15 | 16 | bitset "github.com/skip2/go-qrcode/bitset" 17 | ) 18 | 19 | // Encode data for QR Code 2005 using the appropriate Reed-Solomon code. 20 | // 21 | // numECBytes is the number of error correction bytes to append, and is 22 | // determined by the target QR Code's version and error correction level. 23 | // 24 | // ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has 25 | // numECBytes=7. 26 | func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset { 27 | // Create a polynomial representing |data|. 28 | // 29 | // The bytes are interpreted as the sequence of coefficients of a polynomial. 30 | // The last byte's value becomes the x^0 coefficient, the second to last 31 | // becomes the x^1 coefficient and so on. 32 | ecpoly := newGFPolyFromData(data) 33 | ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes)) 34 | 35 | // Pick the generator polynomial. 36 | generator := rsGeneratorPoly(numECBytes) 37 | 38 | // Generate the error correction bytes. 39 | remainder := gfPolyRemainder(ecpoly, generator) 40 | 41 | // Combine the data & error correcting bytes. 42 | // The mathematically correct answer is: 43 | // 44 | // result := gfPolyAdd(ecpoly, remainder). 45 | // 46 | // The encoding used by QR Code 2005 is slightly different this result: To 47 | // preserve the original |data| bit sequence exactly, the data and remainder 48 | // are combined manually below. This ensures any most significant zero bits 49 | // are preserved (and not optimised away). 50 | result := bitset.Clone(data) 51 | result.AppendBytes(remainder.data(numECBytes)) 52 | 53 | return result 54 | } 55 | 56 | // rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|. 57 | // 58 | // The generator polynomial is calculated as: 59 | // (x + a^0)(x + a^1)...(x + a^degree-1) 60 | func rsGeneratorPoly(degree int) gfPoly { 61 | if degree < 2 { 62 | log.Panic("degree < 2") 63 | } 64 | 65 | generator := gfPoly{term: []gfElement{1}} 66 | 67 | for i := 0; i < degree; i++ { 68 | nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}} 69 | generator = gfPolyMultiply(generator, nextPoly) 70 | } 71 | 72 | return generator 73 | } 74 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/regular_symbol.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | package qrcode 5 | 6 | import ( 7 | bitset "github.com/skip2/go-qrcode/bitset" 8 | ) 9 | 10 | type regularSymbol struct { 11 | version qrCodeVersion 12 | mask int 13 | 14 | data *bitset.Bitset 15 | 16 | symbol *symbol 17 | size int 18 | } 19 | 20 | // Abbreviated true/false. 21 | const ( 22 | b0 = false 23 | b1 = true 24 | ) 25 | 26 | var ( 27 | alignmentPatternCenter = [][]int{ 28 | {}, // Version 0 doesn't exist. 29 | {}, // Version 1 doesn't use alignment patterns. 30 | {6, 18}, 31 | {6, 22}, 32 | {6, 26}, 33 | {6, 30}, 34 | {6, 34}, 35 | {6, 22, 38}, 36 | {6, 24, 42}, 37 | {6, 26, 46}, 38 | {6, 28, 50}, 39 | {6, 30, 54}, 40 | {6, 32, 58}, 41 | {6, 34, 62}, 42 | {6, 26, 46, 66}, 43 | {6, 26, 48, 70}, 44 | {6, 26, 50, 74}, 45 | {6, 30, 54, 78}, 46 | {6, 30, 56, 82}, 47 | {6, 30, 58, 86}, 48 | {6, 34, 62, 90}, 49 | {6, 28, 50, 72, 94}, 50 | {6, 26, 50, 74, 98}, 51 | {6, 30, 54, 78, 102}, 52 | {6, 28, 54, 80, 106}, 53 | {6, 32, 58, 84, 110}, 54 | {6, 30, 58, 86, 114}, 55 | {6, 34, 62, 90, 118}, 56 | {6, 26, 50, 74, 98, 122}, 57 | {6, 30, 54, 78, 102, 126}, 58 | {6, 26, 52, 78, 104, 130}, 59 | {6, 30, 56, 82, 108, 134}, 60 | {6, 34, 60, 86, 112, 138}, 61 | {6, 30, 58, 86, 114, 142}, 62 | {6, 34, 62, 90, 118, 146}, 63 | {6, 30, 54, 78, 102, 126, 150}, 64 | {6, 24, 50, 76, 102, 128, 154}, 65 | {6, 28, 54, 80, 106, 132, 158}, 66 | {6, 32, 58, 84, 110, 136, 162}, 67 | {6, 26, 54, 82, 110, 138, 166}, 68 | {6, 30, 58, 86, 114, 142, 170}, 69 | } 70 | 71 | finderPattern = [][]bool{ 72 | {b1, b1, b1, b1, b1, b1, b1}, 73 | {b1, b0, b0, b0, b0, b0, b1}, 74 | {b1, b0, b1, b1, b1, b0, b1}, 75 | {b1, b0, b1, b1, b1, b0, b1}, 76 | {b1, b0, b1, b1, b1, b0, b1}, 77 | {b1, b0, b0, b0, b0, b0, b1}, 78 | {b1, b1, b1, b1, b1, b1, b1}, 79 | } 80 | 81 | finderPatternSize = 7 82 | 83 | finderPatternHorizontalBorder = [][]bool{ 84 | {b0, b0, b0, b0, b0, b0, b0, b0}, 85 | } 86 | 87 | finderPatternVerticalBorder = [][]bool{ 88 | {b0}, 89 | {b0}, 90 | {b0}, 91 | {b0}, 92 | {b0}, 93 | {b0}, 94 | {b0}, 95 | {b0}, 96 | } 97 | 98 | alignmentPattern = [][]bool{ 99 | {b1, b1, b1, b1, b1}, 100 | {b1, b0, b0, b0, b1}, 101 | {b1, b0, b1, b0, b1}, 102 | {b1, b0, b0, b0, b1}, 103 | {b1, b1, b1, b1, b1}, 104 | } 105 | ) 106 | 107 | func buildRegularSymbol(version qrCodeVersion, mask int, 108 | data *bitset.Bitset, includeQuietZone bool) (*symbol, error) { 109 | 110 | quietZoneSize := 0 111 | if includeQuietZone { 112 | quietZoneSize = version.quietZoneSize() 113 | } 114 | 115 | m := ®ularSymbol{ 116 | version: version, 117 | mask: mask, 118 | data: data, 119 | 120 | symbol: newSymbol(version.symbolSize(), quietZoneSize), 121 | size: version.symbolSize(), 122 | } 123 | 124 | m.addFinderPatterns() 125 | m.addAlignmentPatterns() 126 | m.addTimingPatterns() 127 | m.addFormatInfo() 128 | m.addVersionInfo() 129 | 130 | ok, err := m.addData() 131 | if !ok { 132 | return nil, err 133 | } 134 | 135 | return m.symbol, nil 136 | } 137 | 138 | func (m *regularSymbol) addFinderPatterns() { 139 | fpSize := finderPatternSize 140 | fp := finderPattern 141 | fpHBorder := finderPatternHorizontalBorder 142 | fpVBorder := finderPatternVerticalBorder 143 | 144 | // Top left Finder Pattern. 145 | m.symbol.set2dPattern(0, 0, fp) 146 | m.symbol.set2dPattern(0, fpSize, fpHBorder) 147 | m.symbol.set2dPattern(fpSize, 0, fpVBorder) 148 | 149 | // Top right Finder Pattern. 150 | m.symbol.set2dPattern(m.size-fpSize, 0, fp) 151 | m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder) 152 | m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder) 153 | 154 | // Bottom left Finder Pattern. 155 | m.symbol.set2dPattern(0, m.size-fpSize, fp) 156 | m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder) 157 | m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder) 158 | } 159 | 160 | func (m *regularSymbol) addAlignmentPatterns() { 161 | for _, x := range alignmentPatternCenter[m.version.version] { 162 | for _, y := range alignmentPatternCenter[m.version.version] { 163 | if !m.symbol.empty(x, y) { 164 | continue 165 | } 166 | 167 | m.symbol.set2dPattern(x-2, y-2, alignmentPattern) 168 | } 169 | } 170 | } 171 | 172 | func (m *regularSymbol) addTimingPatterns() { 173 | value := true 174 | 175 | for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ { 176 | m.symbol.set(i, finderPatternSize-1, value) 177 | m.symbol.set(finderPatternSize-1, i, value) 178 | 179 | value = !value 180 | } 181 | } 182 | 183 | func (m *regularSymbol) addFormatInfo() { 184 | fpSize := finderPatternSize 185 | l := formatInfoLengthBits - 1 186 | 187 | f := m.version.formatInfo(m.mask) 188 | 189 | // Bits 0-7, under the top right finder pattern. 190 | for i := 0; i <= 7; i++ { 191 | m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i)) 192 | } 193 | 194 | // Bits 0-5, right of the top left finder pattern. 195 | for i := 0; i <= 5; i++ { 196 | m.symbol.set(fpSize+1, i, f.At(l-i)) 197 | } 198 | 199 | // Bits 6-8 on the corner of the top left finder pattern. 200 | m.symbol.set(fpSize+1, fpSize, f.At(l-6)) 201 | m.symbol.set(fpSize+1, fpSize+1, f.At(l-7)) 202 | m.symbol.set(fpSize, fpSize+1, f.At(l-8)) 203 | 204 | // Bits 9-14 on the underside of the top left finder pattern. 205 | for i := 9; i <= 14; i++ { 206 | m.symbol.set(14-i, fpSize+1, f.At(l-i)) 207 | } 208 | 209 | // Bits 8-14 on the right side of the bottom left finder pattern. 210 | for i := 8; i <= 14; i++ { 211 | m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i)) 212 | } 213 | 214 | // Always dark symbol. 215 | m.symbol.set(fpSize+1, m.size-fpSize-1, true) 216 | } 217 | 218 | func (m *regularSymbol) addVersionInfo() { 219 | fpSize := finderPatternSize 220 | 221 | v := m.version.versionInfo() 222 | l := versionInfoLengthBits - 1 223 | 224 | if v == nil { 225 | return 226 | } 227 | 228 | for i := 0; i < v.Len(); i++ { 229 | // Above the bottom left finder pattern. 230 | m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i)) 231 | 232 | // Left of the top right finder pattern. 233 | m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i)) 234 | } 235 | } 236 | 237 | type direction uint8 238 | 239 | const ( 240 | up direction = iota 241 | down 242 | ) 243 | 244 | func (m *regularSymbol) addData() (bool, error) { 245 | xOffset := 1 246 | dir := up 247 | 248 | x := m.size - 2 249 | y := m.size - 1 250 | 251 | for i := 0; i < m.data.Len(); i++ { 252 | var mask bool 253 | switch m.mask { 254 | case 0: 255 | mask = (y+x+xOffset)%2 == 0 256 | case 1: 257 | mask = y%2 == 0 258 | case 2: 259 | mask = (x+xOffset)%3 == 0 260 | case 3: 261 | mask = (y+x+xOffset)%3 == 0 262 | case 4: 263 | mask = (y/2+(x+xOffset)/3)%2 == 0 264 | case 5: 265 | mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0 266 | case 6: 267 | mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0 268 | case 7: 269 | mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0 270 | } 271 | 272 | // != is equivalent to XOR. 273 | m.symbol.set(x+xOffset, y, mask != m.data.At(i)) 274 | 275 | if i == m.data.Len()-1 { 276 | break 277 | } 278 | 279 | // Find next free bit in the symbol. 280 | for { 281 | if xOffset == 1 { 282 | xOffset = 0 283 | } else { 284 | xOffset = 1 285 | 286 | if dir == up { 287 | if y > 0 { 288 | y-- 289 | } else { 290 | dir = down 291 | x -= 2 292 | } 293 | } else { 294 | if y < m.size-1 { 295 | y++ 296 | } else { 297 | dir = up 298 | x -= 2 299 | } 300 | } 301 | } 302 | 303 | // Skip over the vertical timing pattern entirely. 304 | if x == 5 { 305 | x-- 306 | } 307 | 308 | if m.symbol.empty(x+xOffset, y) { 309 | break 310 | } 311 | } 312 | } 313 | 314 | return true, nil 315 | } 316 | -------------------------------------------------------------------------------- /vendor/github.com/skip2/go-qrcode/symbol.go: -------------------------------------------------------------------------------- 1 | // go-qrcode 2 | // Copyright 2014 Tom Harwood 3 | 4 | package qrcode 5 | 6 | // symbol is a 2D array of bits representing a QR Code symbol. 7 | // 8 | // A symbol consists of size*size modules, with each module normally drawn as a 9 | // black or white square. The symbol also has a border of quietZoneSize modules. 10 | // 11 | // A (fictional) size=2, quietZoneSize=1 QR Code looks like: 12 | // 13 | // +----+ 14 | // | | 15 | // | ab | 16 | // | cd | 17 | // | | 18 | // +----+ 19 | // 20 | // For ease of implementation, the functions to set/get bits ignore the border, 21 | // so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the 22 | // border) is returned by bitmap(). 23 | // 24 | type symbol struct { 25 | // Value of module at [y][x]. True is set. 26 | module [][]bool 27 | 28 | // True if the module at [y][x] is used (to either true or false). 29 | // Used to identify unused modules. 30 | isUsed [][]bool 31 | 32 | // Combined width/height of the symbol and quiet zones. 33 | // 34 | // size = symbolSize + 2*quietZoneSize. 35 | size int 36 | 37 | // Width/height of the symbol only. 38 | symbolSize int 39 | 40 | // Width/height of a single quiet zone. 41 | quietZoneSize int 42 | } 43 | 44 | // newSymbol constructs a symbol of size size*size, with a border of 45 | // quietZoneSize. 46 | func newSymbol(size int, quietZoneSize int) *symbol { 47 | var m symbol 48 | 49 | m.module = make([][]bool, size+2*quietZoneSize) 50 | m.isUsed = make([][]bool, size+2*quietZoneSize) 51 | 52 | for i := range m.module { 53 | m.module[i] = make([]bool, size+2*quietZoneSize) 54 | m.isUsed[i] = make([]bool, size+2*quietZoneSize) 55 | } 56 | 57 | m.size = size + 2*quietZoneSize 58 | m.symbolSize = size 59 | m.quietZoneSize = quietZoneSize 60 | 61 | return &m 62 | } 63 | 64 | // get returns the module value at (x, y). 65 | func (m *symbol) get(x int, y int) (v bool) { 66 | v = m.module[y+m.quietZoneSize][x+m.quietZoneSize] 67 | return 68 | } 69 | 70 | // empty returns true if the module at (x, y) has not been set (to either true 71 | // or false). 72 | func (m *symbol) empty(x int, y int) bool { 73 | return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] 74 | } 75 | 76 | // numEmptyModules returns the number of empty modules. 77 | // 78 | // Initially numEmptyModules is symbolSize * symbolSize. After every module has 79 | // been set (to either true or false), the number of empty modules is zero. 80 | func (m *symbol) numEmptyModules() int { 81 | var count int 82 | for y := 0; y < m.symbolSize; y++ { 83 | for x := 0; x < m.symbolSize; x++ { 84 | if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] { 85 | count++ 86 | } 87 | } 88 | } 89 | 90 | return count 91 | } 92 | 93 | // set sets the module at (x, y) to v. 94 | func (m *symbol) set(x int, y int, v bool) { 95 | m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v 96 | m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true 97 | } 98 | 99 | // set2dPattern sets a 2D array of modules, starting at (x, y). 100 | func (m *symbol) set2dPattern(x int, y int, v [][]bool) { 101 | for j, row := range v { 102 | for i, value := range row { 103 | m.set(x+i, y+j, value) 104 | } 105 | } 106 | } 107 | 108 | // bitmap returns the entire symbol, including the quiet zone. 109 | func (m *symbol) bitmap() [][]bool { 110 | module := make([][]bool, len(m.module)) 111 | 112 | for i := range m.module { 113 | module[i] = m.module[i][:] 114 | } 115 | 116 | return module 117 | } 118 | 119 | // string returns a pictorial representation of the symbol, suitable for 120 | // printing in a TTY. 121 | func (m *symbol) string() string { 122 | var result string 123 | 124 | for _, row := range m.module { 125 | for _, value := range row { 126 | switch value { 127 | case true: 128 | result += " " 129 | case false: 130 | // Unicode 'FULL BLOCK' (U+2588). 131 | result += "██" 132 | } 133 | } 134 | result += "\n" 135 | } 136 | 137 | return result 138 | } 139 | 140 | // Constants used to weight penalty calculations. Specified by ISO/IEC 141 | // 18004:2006. 142 | const ( 143 | penaltyWeight1 = 3 144 | penaltyWeight2 = 3 145 | penaltyWeight3 = 40 146 | penaltyWeight4 = 10 147 | ) 148 | 149 | // penaltyScore returns the penalty score of the symbol. The penalty score 150 | // consists of the sum of the four individual penalty types. 151 | func (m *symbol) penaltyScore() int { 152 | return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4() 153 | } 154 | 155 | // penalty1 returns the penalty score for "adjacent modules in row/column with 156 | // same colour". 157 | // 158 | // The numbers of adjacent matching modules and scores are: 159 | // 0-5: score = 0 160 | // 6+ : score = penaltyWeight1 + (numAdjacentModules - 5) 161 | func (m *symbol) penalty1() int { 162 | penalty := 0 163 | 164 | for x := 0; x < m.symbolSize; x++ { 165 | lastValue := m.get(x, 0) 166 | count := 1 167 | 168 | for y := 1; y < m.symbolSize; y++ { 169 | v := m.get(x, y) 170 | 171 | if v != lastValue { 172 | count = 1 173 | lastValue = v 174 | } else { 175 | count++ 176 | if count == 6 { 177 | penalty += penaltyWeight1 + 1 178 | } else if count > 6 { 179 | penalty++ 180 | } 181 | } 182 | } 183 | } 184 | 185 | for y := 0; y < m.symbolSize; y++ { 186 | lastValue := m.get(0, y) 187 | count := 1 188 | 189 | for x := 1; x < m.symbolSize; x++ { 190 | v := m.get(x, y) 191 | 192 | if v != lastValue { 193 | count = 1 194 | lastValue = v 195 | } else { 196 | count++ 197 | if count == 6 { 198 | penalty += penaltyWeight1 + 1 199 | } else if count > 6 { 200 | penalty++ 201 | } 202 | } 203 | } 204 | } 205 | 206 | return penalty 207 | } 208 | 209 | // penalty2 returns the penalty score for "block of modules in the same colour". 210 | // 211 | // m*n: score = penaltyWeight2 * (m-1) * (n-1). 212 | func (m *symbol) penalty2() int { 213 | penalty := 0 214 | 215 | for y := 1; y < m.symbolSize; y++ { 216 | for x := 1; x < m.symbolSize; x++ { 217 | topLeft := m.get(x-1, y-1) 218 | above := m.get(x, y-1) 219 | left := m.get(x-1, y) 220 | current := m.get(x, y) 221 | 222 | if current == left && current == above && current == topLeft { 223 | penalty++ 224 | } 225 | } 226 | } 227 | 228 | return penalty * penaltyWeight2 229 | } 230 | 231 | // penalty3 returns the penalty score for "1:1:3:1:1 ratio 232 | // (dark:light:dark:light:dark) pattern in row/column, preceded or followed by 233 | // light area 4 modules wide". 234 | // 235 | // Existence of the pattern scores penaltyWeight3. 236 | func (m *symbol) penalty3() int { 237 | penalty := 0 238 | 239 | for y := 0; y < m.symbolSize; y++ { 240 | var bitBuffer int16 = 0x00 241 | 242 | for x := 0; x < m.symbolSize; x++ { 243 | bitBuffer <<= 1 244 | if v := m.get(x, y); v { 245 | bitBuffer |= 1 246 | } 247 | 248 | switch bitBuffer & 0x7ff { 249 | // 0b000 0101 1101 or 0b10111010000 250 | // 0x05d or 0x5d0 251 | case 0x05d, 0x5d0: 252 | penalty += penaltyWeight3 253 | bitBuffer = 0xFF 254 | default: 255 | if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { 256 | penalty += penaltyWeight3 257 | bitBuffer = 0xFF 258 | } 259 | } 260 | } 261 | } 262 | 263 | for x := 0; x < m.symbolSize; x++ { 264 | var bitBuffer int16 = 0x00 265 | 266 | for y := 0; y < m.symbolSize; y++ { 267 | bitBuffer <<= 1 268 | if v := m.get(x, y); v { 269 | bitBuffer |= 1 270 | } 271 | 272 | switch bitBuffer & 0x7ff { 273 | // 0b000 0101 1101 or 0b10111010000 274 | // 0x05d or 0x5d0 275 | case 0x05d, 0x5d0: 276 | penalty += penaltyWeight3 277 | bitBuffer = 0xFF 278 | default: 279 | if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { 280 | penalty += penaltyWeight3 281 | bitBuffer = 0xFF 282 | } 283 | } 284 | } 285 | } 286 | 287 | return penalty 288 | } 289 | 290 | // penalty4 returns the penalty score... 291 | func (m *symbol) penalty4() int { 292 | numModules := m.symbolSize * m.symbolSize 293 | numDarkModules := 0 294 | 295 | for x := 0; x < m.symbolSize; x++ { 296 | for y := 0; y < m.symbolSize; y++ { 297 | if v := m.get(x, y); v { 298 | numDarkModules++ 299 | } 300 | } 301 | } 302 | 303 | numDarkModuleDeviation := numModules/2 - numDarkModules 304 | if numDarkModuleDeviation < 0 { 305 | numDarkModuleDeviation *= -1 306 | } 307 | 308 | return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20)) 309 | } 310 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f 2 | ## explicit 3 | # github.com/Rhymen/go-whatsapp v0.1.1 4 | ## explicit 5 | # github.com/alexellis/go-execute v0.0.0-20201205082949-69a2cde04f4f 6 | ## explicit 7 | github.com/alexellis/go-execute/pkg/v1 8 | # github.com/aws/aws-sdk-go v1.38.1 9 | ## explicit 10 | # github.com/davecgh/go-spew v1.1.1 11 | ## explicit 12 | # github.com/golang/snappy v0.0.3 13 | ## explicit 14 | # github.com/gorilla/mux v1.8.0 15 | ## explicit 16 | github.com/gorilla/mux 17 | # github.com/joho/godotenv v1.3.0 18 | ## explicit 19 | # github.com/klauspost/compress v1.11.12 20 | ## explicit 21 | # github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 22 | ## explicit 23 | github.com/skip2/go-qrcode 24 | github.com/skip2/go-qrcode/bitset 25 | github.com/skip2/go-qrcode/reedsolomon 26 | # github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a 27 | ## explicit 28 | # go.mongodb.org/mongo-driver v1.5.0 29 | ## explicit 30 | # golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 31 | ## explicit 32 | # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 33 | ## explicit 34 | # golang.org/x/text v0.3.5 35 | ## explicit 36 | --------------------------------------------------------------------------------