├── .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 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
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. [](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 | [](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 |
--------------------------------------------------------------------------------