Add dependencies/vendor (whatsapp)
This commit is contained in:
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
@@ -1,24 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
29
vendor/github.com/Baozisoftware/qrcode-terminal-go/LICENSE
generated
vendored
29
vendor/github.com/Baozisoftware/qrcode-terminal-go/LICENSE
generated
vendored
@@ -1,29 +0,0 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017, Baozisoftware
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
39
vendor/github.com/Baozisoftware/qrcode-terminal-go/README.md
generated
vendored
39
vendor/github.com/Baozisoftware/qrcode-terminal-go/README.md
generated
vendored
@@ -1,39 +0,0 @@
|
||||
|
||||
# qrcode-terminal-go
|
||||
QRCode terminal for golang.
|
||||
|
||||
# Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/Baozisoftware/qrcode-terminal-go"
|
||||
|
||||
func main() {
|
||||
Test1()
|
||||
Test2()
|
||||
}
|
||||
|
||||
func Test1(){
|
||||
content := "Hello, 世界"
|
||||
obj := qrcodeTerminal.New()
|
||||
obj.Get(content).Print()
|
||||
}
|
||||
|
||||
func Test2(){
|
||||
content := "https://github.com/Baozisoftware/qrcode-terminal-go"
|
||||
obj := qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightBlue,qrcodeTerminal.ConsoleColors.BrightGreen,qrcodeTerminal.QRCodeRecoveryLevels.Low)
|
||||
obj.Get([]byte(content)).Print()
|
||||
}
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
### Windows XP
|
||||

|
||||
### Windows 7
|
||||

|
||||
### Windows 10
|
||||

|
||||
### Ubuntu
|
||||

|
||||
### macOS
|
||||

|
||||
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
@@ -1,155 +0,0 @@
|
||||
package qrcodeTerminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/skip2/go-qrcode"
|
||||
"github.com/mattn/go-colorable"
|
||||
"image/png"
|
||||
nbytes "bytes"
|
||||
)
|
||||
|
||||
type consoleColor string
|
||||
type consoleColors struct {
|
||||
NormalBlack consoleColor
|
||||
NormalRed consoleColor
|
||||
NormalGreen consoleColor
|
||||
NormalYellow consoleColor
|
||||
NormalBlue consoleColor
|
||||
NormalMagenta consoleColor
|
||||
NormalCyan consoleColor
|
||||
NormalWhite consoleColor
|
||||
BrightBlack consoleColor
|
||||
BrightRed consoleColor
|
||||
BrightGreen consoleColor
|
||||
BrightYellow consoleColor
|
||||
BrightBlue consoleColor
|
||||
BrightMagenta consoleColor
|
||||
BrightCyan consoleColor
|
||||
BrightWhite consoleColor
|
||||
}
|
||||
type qrcodeRecoveryLevel qrcode.RecoveryLevel
|
||||
type qrcodeRecoveryLevels struct {
|
||||
Low qrcodeRecoveryLevel
|
||||
Medium qrcodeRecoveryLevel
|
||||
High qrcodeRecoveryLevel
|
||||
Highest qrcodeRecoveryLevel
|
||||
}
|
||||
|
||||
var (
|
||||
ConsoleColors consoleColors = consoleColors{
|
||||
NormalBlack: "\033[38;5;0m \033[0m",
|
||||
NormalRed: "\033[38;5;1m \033[0m",
|
||||
NormalGreen: "\033[38;5;2m \033[0m",
|
||||
NormalYellow: "\033[38;5;3m \033[0m",
|
||||
NormalBlue: "\033[38;5;4m \033[0m",
|
||||
NormalMagenta: "\033[38;5;5m \033[0m",
|
||||
NormalCyan: "\033[38;5;6m \033[0m",
|
||||
NormalWhite: "\033[38;5;7m \033[0m",
|
||||
BrightBlack: "\033[48;5;0m \033[0m",
|
||||
BrightRed: "\033[48;5;1m \033[0m",
|
||||
BrightGreen: "\033[48;5;2m \033[0m",
|
||||
BrightYellow: "\033[48;5;3m \033[0m",
|
||||
BrightBlue: "\033[48;5;4m \033[0m",
|
||||
BrightMagenta: "\033[48;5;5m \033[0m",
|
||||
BrightCyan: "\033[48;5;6m \033[0m",
|
||||
BrightWhite: "\033[48;5;7m \033[0m"}
|
||||
QRCodeRecoveryLevels = qrcodeRecoveryLevels{
|
||||
Low: qrcodeRecoveryLevel(qrcode.Low),
|
||||
Medium: qrcodeRecoveryLevel(qrcode.Medium),
|
||||
High: qrcodeRecoveryLevel(qrcode.High),
|
||||
Highest: qrcodeRecoveryLevel(qrcode.Highest)}
|
||||
)
|
||||
|
||||
type QRCodeString string
|
||||
|
||||
func (v *QRCodeString) Print() {
|
||||
fmt.Fprint(outer, *v)
|
||||
}
|
||||
|
||||
type qrcodeTerminal struct {
|
||||
front consoleColor
|
||||
back consoleColor
|
||||
level qrcodeRecoveryLevel
|
||||
}
|
||||
|
||||
func (v *qrcodeTerminal) Get(content interface{}) (result *QRCodeString) {
|
||||
var qr *qrcode.QRCode
|
||||
var err error
|
||||
if t, ok := content.(string); ok {
|
||||
qr, err = qrcode.New(t, qrcode.RecoveryLevel(v.level))
|
||||
} else if t, ok := content.([]byte); ok {
|
||||
qr, err = qrcode.New(string(t), qrcode.RecoveryLevel(v.level))
|
||||
}
|
||||
if qr != nil && err == nil {
|
||||
data := qr.Bitmap()
|
||||
result = v.getQRCodeString(data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *qrcodeTerminal) Get2(bytes []byte) (result *QRCodeString) {
|
||||
data, err := parseQR(bytes)
|
||||
if err == nil {
|
||||
result = v.getQRCodeString(data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func New2(front, back consoleColor, level qrcodeRecoveryLevel) *qrcodeTerminal {
|
||||
obj := qrcodeTerminal{front: front, back: back, level: level}
|
||||
return &obj
|
||||
}
|
||||
|
||||
func New() *qrcodeTerminal {
|
||||
front, back, level := ConsoleColors.BrightBlack, ConsoleColors.BrightWhite, QRCodeRecoveryLevels.Medium
|
||||
return New2(front, back, level)
|
||||
}
|
||||
|
||||
func (v *qrcodeTerminal) getQRCodeString(data [][]bool) (result *QRCodeString) {
|
||||
str := ""
|
||||
for ir, row := range data {
|
||||
lr := len(row)
|
||||
if ir == 0 || ir == 1 || ir == 2 ||
|
||||
ir == lr-1 || ir == lr-2 || ir == lr-3 {
|
||||
continue
|
||||
}
|
||||
for ic, col := range row {
|
||||
lc := len(data)
|
||||
if ic == 0 || ic == 1 || ic == 2 ||
|
||||
ic == lc-1 || ic == lc-2 || ic == lc-3 {
|
||||
continue
|
||||
}
|
||||
if col {
|
||||
str += fmt.Sprint(v.front)
|
||||
} else {
|
||||
str += fmt.Sprint(v.back)
|
||||
}
|
||||
}
|
||||
str += fmt.Sprintln()
|
||||
}
|
||||
obj := QRCodeString(str)
|
||||
result = &obj
|
||||
return
|
||||
}
|
||||
|
||||
func parseQR(bytes []byte) (data [][]bool, err error) {
|
||||
r := nbytes.NewReader(bytes)
|
||||
img, err := png.Decode(r)
|
||||
if err == nil {
|
||||
rect := img.Bounds()
|
||||
mx, my := rect.Max.X, rect.Max.Y
|
||||
data = make([][]bool, mx)
|
||||
for x := 0; x < mx; x++ {
|
||||
data[x] = make([]bool, my)
|
||||
for y := 0; y < my; y++ {
|
||||
c := img.At(x, y)
|
||||
r, _, _, _ := c.RGBA()
|
||||
data[x][y] = r == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var outer = colorable.NewColorableStdout()
|
||||
3
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
3
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
.idea/
|
||||
docs/
|
||||
build/
|
||||
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
139
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
139
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
@@ -1,139 +0,0 @@
|
||||
# go-whatsapp
|
||||
Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean interface for developers. Big thanks to all contributors of the [sigalor/whatsapp-web-reveng](https://github.com/sigalor/whatsapp-web-reveng) project. The official WhatsApp Business API was released in August 2018. You can check it out [here](https://www.whatsapp.com/business/api).
|
||||
|
||||
## Installation
|
||||
```sh
|
||||
go get github.com/Rhymen/go-whatsapp
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Creating a connection
|
||||
```go
|
||||
import (
|
||||
whatsapp "github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
wac, err := whatsapp.NewConn(20 * time.Second)
|
||||
```
|
||||
The duration passed to the NewConn function is used to timeout login requests. If you have a bad internet connection use a higher timeout value. This function only creates a websocket connection, it does not handle authentication.
|
||||
|
||||
### Login
|
||||
```go
|
||||
qrChan := make(chan string)
|
||||
go func() {
|
||||
fmt.Printf("qr code: %v\n", <-qrChan)
|
||||
//show qr code or save it somewhere to scan
|
||||
}()
|
||||
sess, err := wac.Login(qrChan)
|
||||
```
|
||||
The authentication process requires you to scan the qr code, that is send through the channel, with the device you are using whatsapp on. The session struct that is returned can be saved and used to restore the login without scanning the qr code again. The qr code has a ttl of 20 seconds and the login function throws a timeout err if the time has passed or any other request fails.
|
||||
|
||||
### Restore
|
||||
```go
|
||||
newSess, err := wac.RestoreWithSession(sess)
|
||||
```
|
||||
The restore function needs a valid session and returns the new session that was created.
|
||||
|
||||
### Add message handlers
|
||||
```go
|
||||
type myHandler struct{}
|
||||
|
||||
func (myHandler) HandleError(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
}
|
||||
|
||||
func (myHandler) HandleTextMessage(message whatsapp.TextMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleAudioMessage(message whatsapp.AudioMessage){
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleJsonMessage(message string) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleContactMessage(message whatsapp.ContactMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleBatteryMessage(message whatsapp.BatteryMessage) {
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func (myHandler) HandleNewContact(contact whatsapp.Contact) {
|
||||
fmt.Println(contact)
|
||||
}
|
||||
|
||||
wac.AddHandler(myHandler{})
|
||||
```
|
||||
The message handlers are all optional, you don't need to implement anything but the error handler to implement the interface. The ImageMessage, VideoMessage, AudioMessage and DocumentMessage provide a Download function to get the media data.
|
||||
|
||||
### Sending text messages
|
||||
```go
|
||||
text := whatsapp.TextMessage{
|
||||
Info: whatsapp.MessageInfo{
|
||||
RemoteJid: "0123456789@s.whatsapp.net",
|
||||
},
|
||||
Text: "Hello Whatsapp",
|
||||
}
|
||||
|
||||
err := wac.Send(text)
|
||||
```
|
||||
|
||||
### Sending Contact Messages
|
||||
```go
|
||||
contactMessage := whatsapp.ContactMessage{
|
||||
Info: whatsapp.MessageInfo{
|
||||
RemoteJid: "0123456789@s.whatsapp.net",
|
||||
},
|
||||
DisplayName: "Luke Skylwallker",
|
||||
Vcard: "BEGIN:VCARD\nVERSION:3.0\nN:Skyllwalker;Luke;;\nFN:Luke Skywallker\nitem1.TEL;waid=0123456789:+1 23 456789789\nitem1.X-ABLabel:Mobile\nEND:VCARD",
|
||||
}
|
||||
|
||||
id, error := client.WaConn.Send(contactMessage)
|
||||
```
|
||||
|
||||
|
||||
The message will be send over the websocket. The attributes seen above are the required ones. All other relevant attributes (id, timestamp, fromMe, status) are set if they are missing in the struct. For the time being we only support text messages, but other types are planned for the near future.
|
||||
|
||||
## Legal
|
||||
This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its
|
||||
affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk.
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
388
vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go
generated
vendored
388
vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go
generated
vendored
@@ -1,388 +0,0 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type binaryDecoder struct {
|
||||
data []byte
|
||||
index int
|
||||
}
|
||||
|
||||
func NewDecoder(data []byte) *binaryDecoder {
|
||||
return &binaryDecoder{data, 0}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) checkEOS(length int) error {
|
||||
if r.index+length > len(r.data) {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readByte() (byte, error) {
|
||||
if err := r.checkEOS(1); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
b := r.data[r.index]
|
||||
r.index++
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readIntN(n int, littleEndian bool) (int, error) {
|
||||
if err := r.checkEOS(n); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var ret int
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
var curShift int
|
||||
if littleEndian {
|
||||
curShift = i
|
||||
} else {
|
||||
curShift = n - i - 1
|
||||
}
|
||||
ret |= int(r.data[r.index+i]) << uint(curShift*8)
|
||||
}
|
||||
|
||||
r.index += n
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt8(littleEndian bool) (int, error) {
|
||||
return r.readIntN(1, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt16(littleEndian bool) (int, error) {
|
||||
return r.readIntN(2, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt20() (int, error) {
|
||||
if err := r.checkEOS(3); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ret := ((int(r.data[r.index]) & 15) << 16) + (int(r.data[r.index+1]) << 8) + int(r.data[r.index+2])
|
||||
r.index += 3
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt32(littleEndian bool) (int, error) {
|
||||
return r.readIntN(4, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt64(littleEndian bool) (int, error) {
|
||||
return r.readIntN(8, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readPacked8(tag int) (string, error) {
|
||||
startByte, err := r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ret := ""
|
||||
|
||||
for i := 0; i < int(startByte&127); i++ {
|
||||
currByte, err := r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lower, err := unpackByte(tag, currByte&0xF0>>4)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
upper, err := unpackByte(tag, currByte&0x0F)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ret += lower + upper
|
||||
}
|
||||
|
||||
if startByte>>7 != 0 {
|
||||
ret = ret[:len(ret)-1]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func unpackByte(tag int, value byte) (string, error) {
|
||||
switch tag {
|
||||
case token.NIBBLE_8:
|
||||
return unpackNibble(value)
|
||||
case token.HEX_8:
|
||||
return unpackHex(value)
|
||||
default:
|
||||
return "", fmt.Errorf("unpackByte with unknown tag %d", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func unpackNibble(value byte) (string, error) {
|
||||
switch {
|
||||
case value < 0 || value > 15:
|
||||
return "", fmt.Errorf("unpackNibble with value %d", value)
|
||||
case value == 10:
|
||||
return "-", nil
|
||||
case value == 11:
|
||||
return ".", nil
|
||||
case value == 15:
|
||||
return "\x00", nil
|
||||
default:
|
||||
return strconv.Itoa(int(value)), nil
|
||||
}
|
||||
}
|
||||
|
||||
func unpackHex(value byte) (string, error) {
|
||||
switch {
|
||||
case value < 0 || value > 15:
|
||||
return "", fmt.Errorf("unpackHex with value %d", value)
|
||||
case value < 10:
|
||||
return strconv.Itoa(int(value)), nil
|
||||
default:
|
||||
return string('A' + value - 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readListSize(tag int) (int, error) {
|
||||
switch tag {
|
||||
case token.LIST_EMPTY:
|
||||
return 0, nil
|
||||
case token.LIST_8:
|
||||
return r.readInt8(false)
|
||||
case token.LIST_16:
|
||||
return r.readInt16(false)
|
||||
default:
|
||||
return 0, fmt.Errorf("readListSize with unknown tag %d at position %d", tag, r.index)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readString(tag int) (string, error) {
|
||||
switch {
|
||||
case tag >= 3 && tag <= len(token.SingleByteTokens):
|
||||
tok, err := token.GetSingleToken(tag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if tok == "s.whatsapp.net" {
|
||||
tok = "c.us"
|
||||
}
|
||||
|
||||
return tok, nil
|
||||
case tag == token.DICTIONARY_0 || tag == token.DICTIONARY_1 || tag == token.DICTIONARY_2 || tag == token.DICTIONARY_3:
|
||||
i, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token.GetDoubleToken(tag-token.DICTIONARY_0, i)
|
||||
case tag == token.LIST_EMPTY:
|
||||
return "", nil
|
||||
case tag == token.BINARY_8:
|
||||
length, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return r.readStringFromChars(length)
|
||||
case tag == token.BINARY_20:
|
||||
length, err := r.readInt20()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return r.readStringFromChars(length)
|
||||
case tag == token.BINARY_32:
|
||||
length, err := r.readInt32(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return r.readStringFromChars(length)
|
||||
case tag == token.JID_PAIR:
|
||||
b, err := r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
i, err := r.readString(int(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
b, err = r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
j, err := r.readString(int(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if i == "" || j == "" {
|
||||
return "", fmt.Errorf("invalid jid pair: %s - %s", i, j)
|
||||
}
|
||||
|
||||
return i + "@" + j, nil
|
||||
case tag == token.NIBBLE_8 || tag == token.HEX_8:
|
||||
return r.readPacked8(tag)
|
||||
default:
|
||||
return "", fmt.Errorf("invalid string with tag %d", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readStringFromChars(length int) (string, error) {
|
||||
if err := r.checkEOS(length); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ret := r.data[r.index : r.index+length]
|
||||
r.index += length
|
||||
|
||||
return string(ret), nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readAttributes(n int) (map[string]string, error) {
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ret := make(map[string]string)
|
||||
for i := 0; i < n; i++ {
|
||||
idx, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index, err := r.readString(idx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idx, err = r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret[index], err = r.readString(idx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readList(tag int) ([]Node, error) {
|
||||
size, err := r.readListSize(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]Node, size)
|
||||
for i := 0; i < size; i++ {
|
||||
n, err := r.ReadNode()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret[i] = *n
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) ReadNode() (*Node, error) {
|
||||
ret := &Node{}
|
||||
|
||||
size, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listSize, err := r.readListSize(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
descrTag, err := r.readInt8(false)
|
||||
if descrTag == token.STREAM_END {
|
||||
return nil, fmt.Errorf("unexpected stream end")
|
||||
}
|
||||
ret.Description, err = r.readString(descrTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if listSize == 0 || ret.Description == "" {
|
||||
return nil, fmt.Errorf("invalid Node")
|
||||
}
|
||||
|
||||
ret.Attributes, err = r.readAttributes((listSize - 1) >> 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if listSize%2 == 1 {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
tag, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case token.LIST_EMPTY, token.LIST_8, token.LIST_16:
|
||||
ret.Content, err = r.readList(tag)
|
||||
case token.BINARY_8:
|
||||
size, err = r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Content, err = r.readBytes(size)
|
||||
case token.BINARY_20:
|
||||
size, err = r.readInt20()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Content, err = r.readBytes(size)
|
||||
case token.BINARY_32:
|
||||
size, err = r.readInt32(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Content, err = r.readBytes(size)
|
||||
default:
|
||||
ret.Content, err = r.readString(tag)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readBytes(n int) ([]byte, error) {
|
||||
ret := make([]byte, n)
|
||||
var err error
|
||||
|
||||
for i := range ret {
|
||||
ret[i], err = r.readByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
351
vendor/github.com/Rhymen/go-whatsapp/binary/encoder.go
generated
vendored
351
vendor/github.com/Rhymen/go-whatsapp/binary/encoder.go
generated
vendored
@@ -1,351 +0,0 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type binaryEncoder struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewEncoder() *binaryEncoder {
|
||||
return &binaryEncoder{make([]byte, 0)}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) GetData() []byte {
|
||||
return w.data
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushByte(b byte) {
|
||||
w.data = append(w.data, b)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushBytes(bytes []byte) {
|
||||
w.data = append(w.data, bytes...)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushIntN(value, n int, littleEndian bool) {
|
||||
for i := 0; i < n; i++ {
|
||||
var curShift int
|
||||
if littleEndian {
|
||||
curShift = i
|
||||
} else {
|
||||
curShift = n - i - 1
|
||||
}
|
||||
w.pushByte(byte((value >> uint(curShift*8)) & 0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt20(value int) {
|
||||
w.pushBytes([]byte{byte((value >> 16) & 0x0F), byte((value >> 8) & 0xFF), byte(value & 0xFF)})
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt8(value int) {
|
||||
w.pushIntN(value, 1, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt16(value int) {
|
||||
w.pushIntN(value, 2, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt32(value int) {
|
||||
w.pushIntN(value, 4, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt64(value int) {
|
||||
w.pushIntN(value, 8, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushString(value string) {
|
||||
w.pushBytes([]byte(value))
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeByteLength(length int) error {
|
||||
if length > math.MaxInt32 {
|
||||
return fmt.Errorf("length is too large: %d", length)
|
||||
} else if length >= (1 << 20) {
|
||||
w.pushByte(token.BINARY_32)
|
||||
w.pushInt32(length)
|
||||
} else if length >= 256 {
|
||||
w.pushByte(token.BINARY_20)
|
||||
w.pushInt20(length)
|
||||
} else {
|
||||
w.pushByte(token.BINARY_8)
|
||||
w.pushInt8(length)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) WriteNode(n Node) error {
|
||||
numAttributes := 0
|
||||
if n.Attributes != nil {
|
||||
numAttributes = len(n.Attributes)
|
||||
}
|
||||
|
||||
hasContent := 0
|
||||
if n.Content != nil {
|
||||
hasContent = 1
|
||||
}
|
||||
|
||||
w.writeListStart(2*numAttributes + 1 + hasContent)
|
||||
if err := w.writeString(n.Description, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeAttributes(n.Attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeChildren(n.Content); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeString(tok string, i bool) error {
|
||||
if !i && tok == "c.us" {
|
||||
if err := w.writeToken(token.IndexOfSingleToken("s.whatsapp.net")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tokenIndex := token.IndexOfSingleToken(tok)
|
||||
if tokenIndex == -1 {
|
||||
jidSepIndex := strings.Index(tok, "@")
|
||||
if jidSepIndex < 1 {
|
||||
w.writeStringRaw(tok)
|
||||
} else {
|
||||
w.writeJid(tok[:jidSepIndex], tok[jidSepIndex+1:])
|
||||
}
|
||||
} else {
|
||||
if tokenIndex < token.SINGLE_BYTE_MAX {
|
||||
if err := w.writeToken(tokenIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
singleByteOverflow := tokenIndex - token.SINGLE_BYTE_MAX
|
||||
dictionaryIndex := singleByteOverflow >> 8
|
||||
if dictionaryIndex < 0 || dictionaryIndex > 3 {
|
||||
return fmt.Errorf("double byte dictionary token out of range: %v", tok)
|
||||
}
|
||||
if err := w.writeToken(token.DICTIONARY_0 + dictionaryIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.writeToken(singleByteOverflow % 256); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeStringRaw(value string) error {
|
||||
if err := w.writeByteLength(len(value)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.pushString(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeJid(jidLeft, jidRight string) error {
|
||||
w.pushByte(token.JID_PAIR)
|
||||
|
||||
if jidLeft != "" {
|
||||
if err := w.writePackedBytes(jidLeft); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := w.writeToken(token.LIST_EMPTY); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.writeString(jidRight, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeToken(tok int) error {
|
||||
if tok < len(token.SingleByteTokens) {
|
||||
w.pushByte(byte(tok))
|
||||
} else if tok <= 500 {
|
||||
return fmt.Errorf("invalid token: %d", tok)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeAttributes(attributes map[string]string) error {
|
||||
if attributes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for key, val := range attributes {
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := w.writeString(key, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeString(val, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeChildren(children interface{}) error {
|
||||
if children == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch childs := children.(type) {
|
||||
case string:
|
||||
if err := w.writeString(childs, true); err != nil {
|
||||
return err
|
||||
}
|
||||
case []byte:
|
||||
if err := w.writeByteLength(len(childs)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.pushBytes(childs)
|
||||
case []Node:
|
||||
w.writeListStart(len(childs))
|
||||
for _, n := range childs {
|
||||
if err := w.WriteNode(n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot write child of type: %T", children)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeListStart(listSize int) {
|
||||
if listSize == 0 {
|
||||
w.pushByte(byte(token.LIST_EMPTY))
|
||||
} else if listSize < 256 {
|
||||
w.pushByte(byte(token.LIST_8))
|
||||
w.pushInt8(listSize)
|
||||
} else {
|
||||
w.pushByte(byte(token.LIST_16))
|
||||
w.pushInt16(listSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writePackedBytes(value string) error {
|
||||
if err := w.writePackedBytesImpl(value, token.NIBBLE_8); err != nil {
|
||||
if err := w.writePackedBytesImpl(value, token.HEX_8); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writePackedBytesImpl(value string, dataType int) error {
|
||||
numBytes := len(value)
|
||||
if numBytes > token.PACKED_MAX {
|
||||
return fmt.Errorf("too many bytes to pack: %d", numBytes)
|
||||
}
|
||||
|
||||
w.pushByte(byte(dataType))
|
||||
|
||||
x := 0
|
||||
if numBytes%2 != 0 {
|
||||
x = 128
|
||||
}
|
||||
w.pushByte(byte(x | int(math.Ceil(float64(numBytes)/2.0))))
|
||||
for i, l := 0, numBytes/2; i < l; i++ {
|
||||
b, err := w.packBytePair(dataType, value[2*i:2*i+1], value[2*i+1:2*i+2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.pushByte(byte(b))
|
||||
}
|
||||
|
||||
if (numBytes % 2) != 0 {
|
||||
b, err := w.packBytePair(dataType, value[numBytes-1:], "\x00")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.pushByte(byte(b))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) packBytePair(packType int, part1, part2 string) (int, error) {
|
||||
if packType == token.NIBBLE_8 {
|
||||
n1, err := packNibble(part1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n2, err := packNibble(part2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return (n1 << 4) | n2, nil
|
||||
} else if packType == token.HEX_8 {
|
||||
n1, err := packHex(part1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n2, err := packHex(part2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return (n1 << 4) | n2, nil
|
||||
} else {
|
||||
return 0, fmt.Errorf("invalid pack type (%d) for byte pair: %s / %s", packType, part1, part2)
|
||||
}
|
||||
}
|
||||
|
||||
func packNibble(value string) (int, error) {
|
||||
if value >= "0" && value <= "9" {
|
||||
return strconv.Atoi(value)
|
||||
} else if value == "-" {
|
||||
return 10, nil
|
||||
} else if value == "." {
|
||||
return 11, nil
|
||||
} else if value == "\x00" {
|
||||
return 15, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("invalid string to pack as nibble: %v", value)
|
||||
}
|
||||
|
||||
func packHex(value string) (int, error) {
|
||||
if (value >= "0" && value <= "9") || (value >= "A" && value <= "F") || (value >= "a" && value <= "f") {
|
||||
d, err := strconv.ParseInt(value, 16, 0)
|
||||
return int(d), err
|
||||
} else if value == "\x00" {
|
||||
return 15, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("invalid string to pack as hex: %v", value)
|
||||
}
|
||||
106
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
106
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
@@ -1,106 +0,0 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pb "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Description string
|
||||
Attributes map[string]string
|
||||
Content interface{}
|
||||
}
|
||||
|
||||
func Marshal(n Node) ([]byte, error) {
|
||||
if n.Attributes != nil && n.Content != nil {
|
||||
a, err := marshalMessageArray(n.Content.([]interface{}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Content = a
|
||||
}
|
||||
|
||||
w := NewEncoder()
|
||||
if err := w.WriteNode(n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w.GetData(), nil
|
||||
}
|
||||
|
||||
func marshalMessageArray(messages []interface{}) ([]Node, error) {
|
||||
ret := make([]Node, len(messages))
|
||||
|
||||
for i, m := range messages {
|
||||
if wmi, ok := m.(*pb.WebMessageInfo); ok {
|
||||
b, err := marshalWebMessageInfo(wmi)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
ret[i] = Node{"message", nil, b}
|
||||
} else {
|
||||
ret[i], ok = m.(Node)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid Node")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func marshalWebMessageInfo(p *pb.WebMessageInfo) ([]byte, error) {
|
||||
b, err := proto.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte) (*Node, error) {
|
||||
r := NewDecoder(data)
|
||||
n, err := r.ReadNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n != nil && n.Attributes != nil && n.Content != nil {
|
||||
nContent, ok := n.Content.([]Node)
|
||||
if ok {
|
||||
n.Content, err = unmarshalMessageArray(nContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func unmarshalMessageArray(messages []Node) ([]interface{}, error) {
|
||||
ret := make([]interface{}, len(messages))
|
||||
|
||||
for i, msg := range messages {
|
||||
if msg.Description == "message" {
|
||||
info, err := unmarshalWebMessageInfo(msg.Content.([]byte))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = info
|
||||
} else {
|
||||
ret[i] = msg
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func unmarshalWebMessageInfo(msg []byte) (*pb.WebMessageInfo, error) {
|
||||
message := &pb.WebMessageInfo{}
|
||||
err := proto.Unmarshal(msg, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return message, nil
|
||||
}
|
||||
12807
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
12807
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1050
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
generated
vendored
1050
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
generated
vendored
File diff suppressed because it is too large
Load Diff
81
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
81
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
@@ -1,81 +0,0 @@
|
||||
package token
|
||||
|
||||
import "fmt"
|
||||
|
||||
var SingleByteTokens = [...]string{"", "", "", "200", "400", "404", "500", "501", "502", "action", "add",
|
||||
"after", "archive", "author", "available", "battery", "before", "body",
|
||||
"broadcast", "chat", "clear", "code", "composing", "contacts", "count",
|
||||
"create", "debug", "delete", "demote", "duplicate", "encoding", "error",
|
||||
"false", "filehash", "from", "g.us", "group", "groups_v2", "height", "id",
|
||||
"image", "in", "index", "invis", "item", "jid", "kind", "last", "leave",
|
||||
"live", "log", "media", "message", "mimetype", "missing", "modify", "name",
|
||||
"notification", "notify", "out", "owner", "participant", "paused",
|
||||
"picture", "played", "presence", "preview", "promote", "query", "raw",
|
||||
"read", "receipt", "received", "recipient", "recording", "relay",
|
||||
"remove", "response", "resume", "retry", "s.whatsapp.net", "seconds",
|
||||
"set", "size", "status", "subject", "subscribe", "t", "text", "to", "true",
|
||||
"type", "unarchive", "unavailable", "url", "user", "value", "web", "width",
|
||||
"mute", "read_only", "admin", "creator", "short", "update", "powersave",
|
||||
"checksum", "epoch", "block", "previous", "409", "replaced", "reason",
|
||||
"spam", "modify_tag", "message_info", "delivery", "emoji", "title",
|
||||
"description", "canonical-url", "matched-text", "star", "unstar",
|
||||
"media_key", "filename", "identity", "unread", "page", "page_count",
|
||||
"search", "media_message", "security", "call_log", "profile", "ciphertext",
|
||||
"invite", "gif", "vcard", "frequent", "privacy", "blacklist", "whitelist",
|
||||
"verify", "location", "document", "elapsed", "revoke_invite", "expiration",
|
||||
"unsubscribe", "disable", "vname", "old_jid", "new_jid", "announcement",
|
||||
"locked", "prop", "label", "color", "call", "offer", "call-id",
|
||||
"quick_reply", "sticker", "pay_t", "accept", "reject", "sticker_pack",
|
||||
"invalid", "canceled", "missed", "connected", "result", "audio",
|
||||
"video", "recent"}
|
||||
|
||||
var doubleByteTokens = [...]string{}
|
||||
|
||||
func GetSingleToken(i int) (string, error) {
|
||||
if i < 3 || i >= len(SingleByteTokens) {
|
||||
return "", fmt.Errorf("index out of single byte token bounds %d", i)
|
||||
}
|
||||
|
||||
return SingleByteTokens[i], nil
|
||||
}
|
||||
|
||||
func GetDoubleToken(index1 int, index2 int) (string, error) {
|
||||
n := 256*index1 + index2
|
||||
if n < 0 || n >= len(doubleByteTokens) {
|
||||
return "", fmt.Errorf("index out of double byte token bounds %d", n)
|
||||
}
|
||||
|
||||
return doubleByteTokens[n], nil
|
||||
}
|
||||
|
||||
func IndexOfSingleToken(token string) int {
|
||||
for i, t := range SingleByteTokens {
|
||||
if t == token {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
const (
|
||||
LIST_EMPTY = 0
|
||||
STREAM_END = 2
|
||||
DICTIONARY_0 = 236
|
||||
DICTIONARY_1 = 237
|
||||
DICTIONARY_2 = 238
|
||||
DICTIONARY_3 = 239
|
||||
LIST_8 = 248
|
||||
LIST_16 = 249
|
||||
JID_PAIR = 250
|
||||
HEX_8 = 251
|
||||
BINARY_8 = 252
|
||||
BINARY_20 = 253
|
||||
BINARY_32 = 254
|
||||
NIBBLE_8 = 255
|
||||
)
|
||||
|
||||
const (
|
||||
PACKED_MAX = 254
|
||||
SINGLE_BYTE_MAX = 256
|
||||
)
|
||||
183
vendor/github.com/Rhymen/go-whatsapp/chat_history.go
generated
vendored
183
vendor/github.com/Rhymen/go-whatsapp/chat_history.go
generated
vendored
@@ -1,183 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MessageOffsetInfo struct {
|
||||
FirstMessageId string
|
||||
FirstMessageOwner bool
|
||||
}
|
||||
|
||||
func decodeMessages(n *binary.Node) []*proto.WebMessageInfo {
|
||||
|
||||
var messages = make([]*proto.WebMessageInfo, 0)
|
||||
|
||||
if n == nil || n.Attributes == nil || n.Content == nil {
|
||||
return messages
|
||||
}
|
||||
|
||||
for _, msg := range n.Content.([]interface{}) {
|
||||
switch msg.(type) {
|
||||
case *proto.WebMessageInfo:
|
||||
messages = append(messages, msg.(*proto.WebMessageInfo))
|
||||
default:
|
||||
log.Println("decodeMessages: Non WebMessage encountered")
|
||||
}
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
// LoadChatMessages is useful to "scroll" messages, loading by count at a time
|
||||
// if handlers == nil the func will use default handlers
|
||||
// if after == true LoadChatMessages will load messages after the specified messageId, otherwise it will return
|
||||
// message before the messageId
|
||||
func (wac *Conn) LoadChatMessages(jid string, count int, messageId string, owner bool, after bool, handlers ...Handler) error {
|
||||
if count <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if handlers == nil {
|
||||
handlers = wac.handler
|
||||
}
|
||||
|
||||
kind := "before"
|
||||
if after {
|
||||
kind = "after"
|
||||
}
|
||||
|
||||
node, err := wac.query("message", jid, messageId, kind,
|
||||
strconv.FormatBool(owner), "", count, 0)
|
||||
|
||||
if err != nil {
|
||||
wac.handleWithCustomHandlers(err, handlers)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, msg := range decodeMessages(node) {
|
||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
||||
wac.handleWithCustomHandlers(msg, handlers)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// LoadFullChatHistory loads full chat history for the given jid
|
||||
// chunkSize = how many messages to load with one query; if handlers == nil the func will use default handlers;
|
||||
// pauseBetweenQueries = how much time to sleep between queries
|
||||
func (wac *Conn) LoadFullChatHistory(jid string, chunkSize int,
|
||||
pauseBetweenQueries time.Duration, handlers ...Handler) {
|
||||
if chunkSize <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if handlers == nil {
|
||||
handlers = wac.handler
|
||||
}
|
||||
|
||||
beforeMsg := ""
|
||||
beforeMsgIsOwner := true
|
||||
|
||||
for {
|
||||
node, err := wac.query("message", jid, beforeMsg, "before",
|
||||
strconv.FormatBool(beforeMsgIsOwner), "", chunkSize, 0)
|
||||
|
||||
if err != nil {
|
||||
wac.handleWithCustomHandlers(err, handlers)
|
||||
} else {
|
||||
|
||||
msgs := decodeMessages(node)
|
||||
for _, msg := range msgs {
|
||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
||||
wac.handleWithCustomHandlers(msg, handlers)
|
||||
}
|
||||
|
||||
if len(msgs) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
beforeMsg = *msgs[0].Key.Id
|
||||
beforeMsgIsOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
|
||||
}
|
||||
|
||||
<-time.After(pauseBetweenQueries)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// LoadFullChatHistoryAfter loads all messages after the specified messageId
|
||||
// useful to "catch up" with the message history after some specified message
|
||||
func (wac *Conn) LoadFullChatHistoryAfter(jid string, messageId string, chunkSize int,
|
||||
pauseBetweenQueries time.Duration, handlers ...Handler) {
|
||||
|
||||
if chunkSize <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if handlers == nil {
|
||||
handlers = wac.handler
|
||||
}
|
||||
|
||||
msgOwner := true
|
||||
prevNotFound := false
|
||||
|
||||
for {
|
||||
node, err := wac.query("message", jid, messageId, "after",
|
||||
strconv.FormatBool(msgOwner), "", chunkSize, 0)
|
||||
|
||||
if err != nil {
|
||||
|
||||
// Whatsapp will return 404 status when there is wrong owner flag on the requested message id
|
||||
if err == ErrServerRespondedWith404 {
|
||||
|
||||
// this will detect two consecutive "not found" errors.
|
||||
// this is done to prevent infinite loop when wrong message id supplied
|
||||
if prevNotFound {
|
||||
log.Println("LoadFullChatHistoryAfter: could not retrieve any messages, wrong message id?")
|
||||
return
|
||||
}
|
||||
prevNotFound = true
|
||||
|
||||
// try to reverse the owner flag and retry
|
||||
if msgOwner {
|
||||
// reverse initial msgOwner value and retry
|
||||
msgOwner = false
|
||||
|
||||
<-time.After(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if the error isn't a 404 error, pass it to the error handler
|
||||
wac.handleWithCustomHandlers(err, handlers)
|
||||
} else {
|
||||
|
||||
msgs := decodeMessages(node)
|
||||
for _, msg := range msgs {
|
||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
||||
wac.handleWithCustomHandlers(msg, handlers)
|
||||
}
|
||||
|
||||
if len(msgs) != chunkSize {
|
||||
break
|
||||
}
|
||||
|
||||
messageId = *msgs[0].Key.Id
|
||||
msgOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
|
||||
}
|
||||
|
||||
// message was found
|
||||
prevNotFound = false
|
||||
|
||||
<-time.After(pauseBetweenQueries)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
302
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
302
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
@@ -1,302 +0,0 @@
|
||||
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type metric byte
|
||||
|
||||
const (
|
||||
debugLog metric = iota + 1
|
||||
queryResume
|
||||
queryReceipt
|
||||
queryMedia
|
||||
queryChat
|
||||
queryContacts
|
||||
queryMessages
|
||||
presence
|
||||
presenceSubscribe
|
||||
group
|
||||
read
|
||||
chat
|
||||
received
|
||||
pic
|
||||
status
|
||||
message
|
||||
queryActions
|
||||
block
|
||||
queryGroup
|
||||
queryPreview
|
||||
queryEmoji
|
||||
queryMessageInfo
|
||||
spam
|
||||
querySearch
|
||||
queryIdentity
|
||||
queryUrl
|
||||
profile
|
||||
contact
|
||||
queryVcard
|
||||
queryStatus
|
||||
queryStatusUpdate
|
||||
privacyStatus
|
||||
queryLiveLocations
|
||||
liveLocation
|
||||
queryVname
|
||||
queryLabels
|
||||
call
|
||||
queryCall
|
||||
queryQuickReplies
|
||||
)
|
||||
|
||||
type flag byte
|
||||
|
||||
const (
|
||||
ignore flag = 1 << (7 - iota)
|
||||
ackRequest
|
||||
available
|
||||
notAvailable
|
||||
expires
|
||||
skipOffline
|
||||
)
|
||||
|
||||
/*
|
||||
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
|
||||
It holds all necessary information to make the package work internally.
|
||||
*/
|
||||
type Conn struct {
|
||||
ws *websocketWrapper
|
||||
listener *listenerWrapper
|
||||
|
||||
connected bool
|
||||
loggedIn bool
|
||||
wg *sync.WaitGroup
|
||||
|
||||
session *Session
|
||||
sessionLock uint32
|
||||
handler []Handler
|
||||
msgCount int
|
||||
msgTimeout time.Duration
|
||||
Info *Info
|
||||
Store *Store
|
||||
ServerLastSeen time.Time
|
||||
|
||||
timeTag string // last 3 digits obtained after a successful login takeover
|
||||
|
||||
longClientName string
|
||||
shortClientName string
|
||||
clientVersion string
|
||||
|
||||
loginSessionLock sync.RWMutex
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
writerLock sync.RWMutex
|
||||
}
|
||||
|
||||
type websocketWrapper struct {
|
||||
sync.Mutex
|
||||
conn *websocket.Conn
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
type listenerWrapper struct {
|
||||
sync.RWMutex
|
||||
m map[string]chan string
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
|
||||
The goroutine for handling incoming messages is started
|
||||
*/
|
||||
func NewConn(timeout time.Duration) (*Conn, error) {
|
||||
return NewConnWithOptions(&Options{
|
||||
Timeout: timeout,
|
||||
})
|
||||
}
|
||||
|
||||
// NewConnWithProxy Create a new connect with a given timeout and a http proxy.
|
||||
func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) {
|
||||
return NewConnWithOptions(&Options{
|
||||
Timeout: timeout,
|
||||
Proxy: proxy,
|
||||
})
|
||||
}
|
||||
|
||||
// NewConnWithOptions Create a new connect with a given options.
|
||||
type Options struct {
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
Timeout time.Duration
|
||||
Handler []Handler
|
||||
ShortClientName string
|
||||
LongClientName string
|
||||
ClientVersion string
|
||||
Store *Store
|
||||
}
|
||||
func NewConnWithOptions(opt *Options) (*Conn, error) {
|
||||
if opt == nil {
|
||||
return nil, ErrOptionsNotProvided
|
||||
}
|
||||
wac := &Conn{
|
||||
handler: make([]Handler, 0),
|
||||
msgCount: 0,
|
||||
msgTimeout: opt.Timeout,
|
||||
Store: newStore(),
|
||||
longClientName: "github.com/Rhymen/go-whatsapp",
|
||||
shortClientName: "go-whatsapp",
|
||||
clientVersion: "0.1.0",
|
||||
}
|
||||
if opt.Handler != nil {
|
||||
wac.handler = opt.Handler
|
||||
}
|
||||
if opt.Store != nil {
|
||||
wac.Store = opt.Store
|
||||
}
|
||||
if opt.Proxy != nil {
|
||||
wac.Proxy = opt.Proxy
|
||||
}
|
||||
if len(opt.ShortClientName) != 0 {
|
||||
wac.shortClientName = opt.ShortClientName
|
||||
}
|
||||
if len(opt.LongClientName) != 0 {
|
||||
wac.longClientName = opt.LongClientName
|
||||
}
|
||||
if len(opt.ClientVersion) != 0 {
|
||||
wac.clientVersion = opt.ClientVersion
|
||||
}
|
||||
return wac, wac.connect()
|
||||
}
|
||||
|
||||
// connect should be guarded with wsWriteMutex
|
||||
func (wac *Conn) connect() (err error) {
|
||||
if wac.connected {
|
||||
return ErrAlreadyConnected
|
||||
}
|
||||
wac.connected = true
|
||||
defer func() { // set connected to false on error
|
||||
if err != nil {
|
||||
wac.connected = false
|
||||
}
|
||||
}()
|
||||
|
||||
dialer := &websocket.Dialer{
|
||||
ReadBufferSize: 0,
|
||||
WriteBufferSize: 0,
|
||||
HandshakeTimeout: wac.msgTimeout,
|
||||
Proxy: wac.Proxy,
|
||||
}
|
||||
|
||||
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
||||
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "couldn't dial whatsapp web websocket")
|
||||
}
|
||||
|
||||
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||
// from default CloseHandler
|
||||
message := websocket.FormatCloseMessage(code, "")
|
||||
err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
||||
|
||||
// our close handling
|
||||
_, _ = wac.Disconnect()
|
||||
wac.handle(&ErrConnectionClosed{Code: code, Text: text})
|
||||
return err
|
||||
})
|
||||
|
||||
wac.ws = &websocketWrapper{
|
||||
conn: wsConn,
|
||||
close: make(chan struct{}),
|
||||
}
|
||||
|
||||
wac.listener = &listenerWrapper{
|
||||
m: make(map[string]chan string),
|
||||
}
|
||||
|
||||
wac.wg = &sync.WaitGroup{}
|
||||
wac.wg.Add(2)
|
||||
go wac.readPump()
|
||||
go wac.keepAlive(20000, 60000)
|
||||
|
||||
wac.loggedIn = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) Disconnect() (Session, error) {
|
||||
if !wac.connected {
|
||||
return Session{}, ErrNotConnected
|
||||
}
|
||||
wac.connected = false
|
||||
wac.loggedIn = false
|
||||
|
||||
close(wac.ws.close) //signal close
|
||||
wac.wg.Wait() //wait for close
|
||||
|
||||
err := wac.ws.conn.Close()
|
||||
wac.ws = nil
|
||||
|
||||
if wac.session == nil {
|
||||
return Session{}, err
|
||||
}
|
||||
return *wac.session, err
|
||||
}
|
||||
|
||||
func (wac *Conn) AdminTest() (bool, error) {
|
||||
if !wac.connected {
|
||||
return false, ErrNotConnected
|
||||
}
|
||||
|
||||
if !wac.loggedIn {
|
||||
return false, ErrInvalidSession
|
||||
}
|
||||
|
||||
result, err := wac.sendAdminTest()
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
||||
defer wac.wg.Done()
|
||||
|
||||
for {
|
||||
err := wac.sendKeepAlive()
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "keepAlive failed"))
|
||||
//TODO: Consequences?
|
||||
}
|
||||
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
||||
select {
|
||||
case <-time.After(time.Duration(interval) * time.Millisecond):
|
||||
case <-wac.ws.close:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnected returns whether the server connection is established or not
|
||||
func (wac *Conn) IsConnected() bool {
|
||||
return wac.connected
|
||||
}
|
||||
|
||||
// GetConnected returns whether the server connection is established or not
|
||||
//
|
||||
// Deprecated: function name is not go idiomatic, use IsConnected instead
|
||||
func (wac *Conn) GetConnected() bool {
|
||||
return wac.connected
|
||||
}
|
||||
|
||||
//IsLoggedIn returns whether the you are logged in or not
|
||||
func (wac *Conn) IsLoggedIn() bool {
|
||||
return wac.loggedIn
|
||||
}
|
||||
|
||||
// GetLoggedIn returns whether the you are logged in or not
|
||||
//
|
||||
// Deprecated: function name is not go idiomatic, use IsLoggedIn instead.
|
||||
func (wac *Conn) GetLoggedIn() bool {
|
||||
return wac.loggedIn
|
||||
}
|
||||
322
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
322
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
@@ -1,322 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
)
|
||||
|
||||
type Presence string
|
||||
|
||||
const (
|
||||
PresenceAvailable Presence = "available"
|
||||
PresenceUnavailable Presence = "unavailable"
|
||||
PresenceComposing Presence = "composing"
|
||||
PresenceRecording Presence = "recording"
|
||||
PresencePaused Presence = "paused"
|
||||
)
|
||||
|
||||
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
||||
// functions probably shouldn't return a string, maybe build a struct / return json
|
||||
// check for further queries
|
||||
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "ProfilePicThumb", jid}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "Status", jid}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"action", "presence", "subscribe", jid}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
|
||||
return wac.query("search", "", "", "", "", search, count, page)
|
||||
}
|
||||
|
||||
func (wac *Conn) LoadMessages(jid, messageId string, count int) (*binary.Node, error) {
|
||||
return wac.query("message", jid, "", "before", "true", "", count, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) LoadMessagesBefore(jid, messageId string, count int) (*binary.Node, error) {
|
||||
return wac.query("message", jid, messageId, "before", "true", "", count, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) LoadMessagesAfter(jid, messageId string, count int) (*binary.Node, error) {
|
||||
return wac.query("message", jid, messageId, "after", "true", "", count, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) LoadMediaInfo(jid, messageId, owner string) (*binary.Node, error) {
|
||||
return wac.query("media", jid, messageId, "", owner, "", 0, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) {
|
||||
ts := time.Now().Unix()
|
||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
|
||||
content := binary.Node{
|
||||
Description: "presence",
|
||||
Attributes: map[string]string{
|
||||
"type": string(presence),
|
||||
},
|
||||
}
|
||||
switch presence {
|
||||
case PresenceComposing:
|
||||
fallthrough
|
||||
case PresenceRecording:
|
||||
fallthrough
|
||||
case PresencePaused:
|
||||
content.Attributes["to"] = jid
|
||||
}
|
||||
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{content},
|
||||
}
|
||||
|
||||
return wac.writeBinary(n, group, ignore, tag)
|
||||
}
|
||||
|
||||
func (wac *Conn) Exist(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "exist", jid}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) Emoji() (*binary.Node, error) {
|
||||
return wac.query("emoji", "", "", "", "", "", 0, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) Contacts() (*binary.Node, error) {
|
||||
return wac.query("contacts", "", "", "", "", "", 0, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) Chats() (*binary.Node, error) {
|
||||
return wac.query("chat", "", "", "", "", "", 0, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) Read(jid, id string) (<-chan string, error) {
|
||||
ts := time.Now().Unix()
|
||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{binary.Node{
|
||||
Description: "read",
|
||||
Attributes: map[string]string{
|
||||
"count": "1",
|
||||
"index": id,
|
||||
"jid": jid,
|
||||
"owner": "false",
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
return wac.writeBinary(n, group, ignore, tag)
|
||||
}
|
||||
|
||||
func (wac *Conn) query(t, jid, messageId, kind, owner, search string, count, page int) (*binary.Node, error) {
|
||||
ts := time.Now().Unix()
|
||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
|
||||
n := binary.Node{
|
||||
Description: "query",
|
||||
Attributes: map[string]string{
|
||||
"type": t,
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
}
|
||||
|
||||
if jid != "" {
|
||||
n.Attributes["jid"] = jid
|
||||
}
|
||||
|
||||
if messageId != "" {
|
||||
n.Attributes["index"] = messageId
|
||||
}
|
||||
|
||||
if kind != "" {
|
||||
n.Attributes["kind"] = kind
|
||||
}
|
||||
|
||||
if owner != "" {
|
||||
n.Attributes["owner"] = owner
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
n.Attributes["search"] = search
|
||||
}
|
||||
|
||||
if count != 0 {
|
||||
n.Attributes["count"] = strconv.Itoa(count)
|
||||
}
|
||||
|
||||
if page != 0 {
|
||||
n.Attributes["page"] = strconv.Itoa(page)
|
||||
}
|
||||
|
||||
metric := group
|
||||
if t == "media" {
|
||||
metric = queryMedia
|
||||
}
|
||||
|
||||
ch, err := wac.writeBinary(n, metric, ignore, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := wac.decryptBinaryMessage([]byte(<-ch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//TODO: use parseProtoMessage
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) setGroup(t, jid, subject string, participants []string) (<-chan string, error) {
|
||||
ts := time.Now().Unix()
|
||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
|
||||
//TODO: get proto or improve encoder to handle []interface{}
|
||||
|
||||
p := buildParticipantNodes(participants)
|
||||
|
||||
g := binary.Node{
|
||||
Description: "group",
|
||||
Attributes: map[string]string{
|
||||
"author": wac.session.Wid,
|
||||
"id": tag,
|
||||
"type": t,
|
||||
},
|
||||
Content: p,
|
||||
}
|
||||
|
||||
if jid != "" {
|
||||
g.Attributes["jid"] = jid
|
||||
}
|
||||
|
||||
if subject != "" {
|
||||
g.Attributes["subject"] = subject
|
||||
}
|
||||
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{g},
|
||||
}
|
||||
|
||||
return wac.writeBinary(n, group, ignore, tag)
|
||||
}
|
||||
|
||||
func buildParticipantNodes(participants []string) []binary.Node {
|
||||
l := len(participants)
|
||||
if participants == nil || l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := make([]binary.Node, len(participants))
|
||||
for i, participant := range participants {
|
||||
p[i] = binary.Node{
|
||||
Description: "participant",
|
||||
Attributes: map[string]string{
|
||||
"jid": participant,
|
||||
},
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (wac *Conn) BlockContact(jid string) (<-chan string, error) {
|
||||
return wac.handleBlockContact("add", jid)
|
||||
}
|
||||
|
||||
func (wac *Conn) UnblockContact(jid string) (<-chan string, error) {
|
||||
return wac.handleBlockContact("remove", jid)
|
||||
}
|
||||
|
||||
func (wac *Conn) handleBlockContact(action, jid string) (<-chan string, error) {
|
||||
ts := time.Now().Unix()
|
||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
|
||||
netsplit := strings.Split(jid, "@")
|
||||
cusjid := netsplit[0] + "@c.us"
|
||||
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{
|
||||
binary.Node{
|
||||
Description: "block",
|
||||
Attributes: map[string]string{
|
||||
"type": action,
|
||||
},
|
||||
Content: []binary.Node{
|
||||
{
|
||||
Description: "user",
|
||||
Attributes: map[string]string{
|
||||
"jid": cusjid,
|
||||
},
|
||||
Content: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return wac.writeBinary(n, contact, ignore, tag)
|
||||
}
|
||||
|
||||
// Search product details on order
|
||||
func (wac *Conn) SearchProductDetails(id, orderId, token string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "order", map[string]string{
|
||||
"id": id,
|
||||
"orderId": orderId,
|
||||
"imageHeight": strconv.Itoa(80),
|
||||
"imageWidth": strconv.Itoa(80),
|
||||
"token": token,
|
||||
}}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
// Order search and get product catalog reh
|
||||
func (wac *Conn) SearchOrder(catalogWid, stanzaId string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "bizCatalog", map[string]string{
|
||||
"catalogWid": catalogWid,
|
||||
"limit": strconv.Itoa(10),
|
||||
"height": strconv.Itoa(100),
|
||||
"width": strconv.Itoa(100),
|
||||
"stanza_id": stanzaId,
|
||||
"type": "get_product_catalog_reh",
|
||||
}}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
// Company details for Whatsapp Business
|
||||
func (wac *Conn) BusinessProfile(wid string) (<-chan string, error) {
|
||||
query := map[string]string{
|
||||
"wid": wid,
|
||||
}
|
||||
data := []interface{}{"query", "businessProfile", []map[string]string{query}}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
101
vendor/github.com/Rhymen/go-whatsapp/crypto/cbc/cbc.go
generated
vendored
101
vendor/github.com/Rhymen/go-whatsapp/crypto/cbc/cbc.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
CBC describes a block cipher mode. In cryptography, a block cipher mode of operation is an algorithm that uses a
|
||||
block cipher to provide an information service such as confidentiality or authenticity. A block cipher by itself
|
||||
is only suitable for the secure cryptographic transformation (encryption or decryption) of one fixed-length group of
|
||||
bits called a block. A mode of operation describes how to repeatedly apply a cipher's single-block operation to
|
||||
securely transform amounts of data larger than a block.
|
||||
|
||||
This package simplifies the usage of AES-256-CBC.
|
||||
*/
|
||||
package cbc
|
||||
|
||||
/*
|
||||
Some code is provided by the GitHub user locked (github.com/locked):
|
||||
https://gist.github.com/locked/b066aa1ddeb2b28e855e
|
||||
Thanks!
|
||||
*/
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
Decrypt is a function that decrypts a given cipher text with a provided key and initialization vector(iv).
|
||||
*/
|
||||
func Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, fmt.Errorf("ciphertext is shorter then block size: %d / %d", len(ciphertext), aes.BlockSize)
|
||||
}
|
||||
|
||||
if iv == nil {
|
||||
iv = ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
return unpad(ciphertext)
|
||||
}
|
||||
|
||||
/*
|
||||
Encrypt is a function that encrypts plaintext with a given key and an optional initialization vector(iv).
|
||||
*/
|
||||
func Encrypt(key, iv, plaintext []byte) ([]byte, error) {
|
||||
plaintext = pad(plaintext, aes.BlockSize)
|
||||
|
||||
if len(plaintext)%aes.BlockSize != 0 {
|
||||
return nil, fmt.Errorf("plaintext is not a multiple of the block size: %d / %d", len(plaintext), aes.BlockSize)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ciphertext []byte
|
||||
if iv == nil {
|
||||
ciphertext = make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||
} else {
|
||||
ciphertext = make([]byte, len(plaintext))
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext, plaintext)
|
||||
}
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func pad(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
func unpad(src []byte) ([]byte, error) {
|
||||
length := len(src)
|
||||
padLen := int(src[length-1])
|
||||
|
||||
if padLen > length {
|
||||
return nil, fmt.Errorf("padding is greater then the length: %d / %d", padLen, length)
|
||||
}
|
||||
|
||||
return src[:(length - padLen)], nil
|
||||
}
|
||||
44
vendor/github.com/Rhymen/go-whatsapp/crypto/curve25519/curve.go
generated
vendored
44
vendor/github.com/Rhymen/go-whatsapp/crypto/curve25519/curve.go
generated
vendored
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
In cryptography, Curve25519 is an elliptic curve offering 128 bits of security and designed for use with the elliptic
|
||||
curve Diffie–Hellman (ECDH) key agreement scheme. It is one of the fastest ECC curves and is not covered by any known
|
||||
patents. The reference implementation is public domain software. The original Curve25519 paper defined it
|
||||
as a Diffie–Hellman (DH) function.
|
||||
*/
|
||||
package curve25519
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
GenerateKey generates a public private key pair using Curve25519.
|
||||
*/
|
||||
func GenerateKey() (privateKey *[32]byte, publicKey *[32]byte, err error) {
|
||||
var pub, priv [32]byte
|
||||
|
||||
_, err = io.ReadFull(rand.Reader, priv[:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
priv[0] &= 248
|
||||
priv[31] &= 127
|
||||
priv[31] |= 64
|
||||
|
||||
curve25519.ScalarBaseMult(&pub, &priv)
|
||||
|
||||
return &priv, &pub, nil
|
||||
}
|
||||
|
||||
/*
|
||||
GenerateSharedSecret generates the shared secret with a given public private key pair.
|
||||
*/
|
||||
func GenerateSharedSecret(priv, pub [32]byte) []byte {
|
||||
var secret [32]byte
|
||||
|
||||
curve25519.ScalarMult(&secret, &priv, &pub)
|
||||
|
||||
return secret[:]
|
||||
}
|
||||
47
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
47
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
HKDF is a simple key derivation function (KDF) based on
|
||||
a hash-based message authentication code (HMAC). It was initially proposed by its authors as a building block in
|
||||
various protocols and applications, as well as to discourage the proliferation of multiple KDF mechanisms.
|
||||
The main approach HKDF follows is the "extract-then-expand" paradigm, where the KDF logically consists of two modules:
|
||||
the first stage takes the input keying material and "extracts" from it a fixed-length pseudorandom key, and then the
|
||||
second stage "expands" this key into several additional pseudorandom keys (the output of the KDF).
|
||||
*/
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
Expand expands a given key with the HKDF algorithm.
|
||||
*/
|
||||
func Expand(key []byte, length int, info string) ([]byte, error) {
|
||||
var h io.Reader
|
||||
if info == "" {
|
||||
/*
|
||||
Only used during initial login
|
||||
Pseudorandom Key is provided by server and has not to be created
|
||||
*/
|
||||
h = hkdf.Expand(sha256.New, key, []byte(info))
|
||||
} else {
|
||||
/*
|
||||
Used every other time
|
||||
Pseudorandom Key is created during kdf.New
|
||||
This is the normal that crypto/hkdf is used
|
||||
*/
|
||||
h = hkdf.New(sha256.New, key, nil, []byte(info))
|
||||
}
|
||||
out := make([]byte, length)
|
||||
n, err := io.ReadAtLeast(h, out, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != length {
|
||||
return nil, fmt.Errorf("new key to short")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
42
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
42
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAlreadyConnected = errors.New("already connected")
|
||||
ErrAlreadyLoggedIn = errors.New("already logged in")
|
||||
ErrInvalidSession = errors.New("invalid session")
|
||||
ErrLoginInProgress = errors.New("login or restore already running")
|
||||
ErrNotConnected = errors.New("not connected")
|
||||
ErrInvalidWsData = errors.New("received invalid data")
|
||||
ErrInvalidWsState = errors.New("can't handle binary data when not logged in")
|
||||
ErrConnectionTimeout = errors.New("connection timed out")
|
||||
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
|
||||
ErrInvalidHmac = errors.New("invalid hmac")
|
||||
ErrInvalidServerResponse = errors.New("invalid response received from server")
|
||||
ErrServerRespondedWith404 = errors.New("server responded with status 404")
|
||||
ErrInvalidWebsocket = errors.New("invalid websocket")
|
||||
ErrMessageTypeNotImplemented = errors.New("message type not implemented")
|
||||
ErrOptionsNotProvided = errors.New("new conn options not provided")
|
||||
)
|
||||
|
||||
type ErrConnectionFailed struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ErrConnectionFailed) Error() string {
|
||||
return fmt.Sprintf("connection to WhatsApp servers failed: %v", e.Err)
|
||||
}
|
||||
|
||||
type ErrConnectionClosed struct {
|
||||
Code int
|
||||
Text string
|
||||
}
|
||||
|
||||
func (e *ErrConnectionClosed) Error() string {
|
||||
return fmt.Sprintf("server closed connection,code: %d,text: %s", e.Code, e.Text)
|
||||
}
|
||||
90
vendor/github.com/Rhymen/go-whatsapp/group.go
generated
vendored
90
vendor/github.com/Rhymen/go-whatsapp/group.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "GroupMetadata", jid}
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) {
|
||||
return wac.setGroup("create", "", subject, participants)
|
||||
}
|
||||
|
||||
func (wac *Conn) UpdateGroupSubject(subject string, jid string) (<-chan string, error) {
|
||||
return wac.setGroup("subject", jid, subject, nil)
|
||||
}
|
||||
|
||||
func (wac *Conn) SetAdmin(jid string, participants []string) (<-chan string, error) {
|
||||
return wac.setGroup("promote", jid, "", participants)
|
||||
}
|
||||
|
||||
func (wac *Conn) RemoveAdmin(jid string, participants []string) (<-chan string, error) {
|
||||
return wac.setGroup("demote", jid, "", participants)
|
||||
}
|
||||
|
||||
func (wac *Conn) AddMember(jid string, participants []string) (<-chan string, error) {
|
||||
return wac.setGroup("add", jid, "", participants)
|
||||
}
|
||||
|
||||
func (wac *Conn) RemoveMember(jid string, participants []string) (<-chan string, error) {
|
||||
return wac.setGroup("remove", jid, "", participants)
|
||||
}
|
||||
|
||||
func (wac *Conn) LeaveGroup(jid string) (<-chan string, error) {
|
||||
return wac.setGroup("leave", jid, "", nil)
|
||||
}
|
||||
|
||||
func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
||||
request := []interface{}{"query", "inviteCode", jid}
|
||||
ch, err := wac.writeJson(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
|
||||
select {
|
||||
case r := <-ch:
|
||||
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
||||
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return "", fmt.Errorf("request timed out")
|
||||
}
|
||||
|
||||
if int(response["status"].(float64)) != 200 {
|
||||
return "", fmt.Errorf("request responded with %d", response["status"])
|
||||
}
|
||||
|
||||
return response["code"].(string), nil
|
||||
}
|
||||
|
||||
func (wac *Conn) GroupAcceptInviteCode(code string) (jid string, err error) {
|
||||
request := []interface{}{"action", "invite", code}
|
||||
ch, err := wac.writeJson(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
|
||||
select {
|
||||
case r := <-ch:
|
||||
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
||||
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return "", fmt.Errorf("request timed out")
|
||||
}
|
||||
|
||||
if int(response["status"].(float64)) != 200 {
|
||||
return "", fmt.Errorf("request responded with %d", response["status"])
|
||||
}
|
||||
|
||||
return response["gid"].(string), nil
|
||||
}
|
||||
481
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
481
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
@@ -1,481 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
)
|
||||
|
||||
/*
|
||||
The Handler interface is the minimal interface that needs to be implemented
|
||||
to be accepted as a valid handler for our dispatching system.
|
||||
The minimal handler is used to dispatch error messages. These errors occur on unexpected behavior by the websocket
|
||||
connection or if we are unable to handle or interpret an incoming message. Error produced by user actions are not
|
||||
dispatched through this handler. They are returned as an error on the specific function call.
|
||||
*/
|
||||
type Handler interface {
|
||||
HandleError(err error)
|
||||
}
|
||||
|
||||
type SyncHandler interface {
|
||||
Handler
|
||||
ShouldCallSynchronously() bool
|
||||
}
|
||||
|
||||
/*
|
||||
The TextMessageHandler interface needs to be implemented to receive text messages dispatched by the dispatcher.
|
||||
*/
|
||||
type TextMessageHandler interface {
|
||||
Handler
|
||||
HandleTextMessage(message TextMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The ImageMessageHandler interface needs to be implemented to receive image messages dispatched by the dispatcher.
|
||||
*/
|
||||
type ImageMessageHandler interface {
|
||||
Handler
|
||||
HandleImageMessage(message ImageMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The VideoMessageHandler interface needs to be implemented to receive video messages dispatched by the dispatcher.
|
||||
*/
|
||||
type VideoMessageHandler interface {
|
||||
Handler
|
||||
HandleVideoMessage(message VideoMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The AudioMessageHandler interface needs to be implemented to receive audio messages dispatched by the dispatcher.
|
||||
*/
|
||||
type AudioMessageHandler interface {
|
||||
Handler
|
||||
HandleAudioMessage(message AudioMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The DocumentMessageHandler interface needs to be implemented to receive document messages dispatched by the dispatcher.
|
||||
*/
|
||||
type DocumentMessageHandler interface {
|
||||
Handler
|
||||
HandleDocumentMessage(message DocumentMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The LiveLocationMessageHandler interface needs to be implemented to receive live location messages dispatched by the dispatcher.
|
||||
*/
|
||||
type LiveLocationMessageHandler interface {
|
||||
Handler
|
||||
HandleLiveLocationMessage(message LiveLocationMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The LocationMessageHandler interface needs to be implemented to receive location messages dispatched by the dispatcher.
|
||||
*/
|
||||
type LocationMessageHandler interface {
|
||||
Handler
|
||||
HandleLocationMessage(message LocationMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The StickerMessageHandler interface needs to be implemented to receive sticker messages dispatched by the dispatcher.
|
||||
*/
|
||||
type StickerMessageHandler interface {
|
||||
Handler
|
||||
HandleStickerMessage(message StickerMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The ContactMessageHandler interface needs to be implemented to receive contact messages dispatched by the dispatcher.
|
||||
*/
|
||||
type ContactMessageHandler interface {
|
||||
Handler
|
||||
HandleContactMessage(message ContactMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The ProductMessageHandler interface needs to be implemented to receive product messages dispatched by the dispatcher.
|
||||
*/
|
||||
type ProductMessageHandler interface {
|
||||
Handler
|
||||
HandleProductMessage(message ProductMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The OrderMessageHandler interface needs to be implemented to receive order messages dispatched by the dispatcher.
|
||||
*/
|
||||
type OrderMessageHandler interface {
|
||||
Handler
|
||||
HandleOrderMessage(message OrderMessage)
|
||||
}
|
||||
|
||||
/*
|
||||
The JsonMessageHandler interface needs to be implemented to receive json messages dispatched by the dispatcher.
|
||||
These json messages contain status updates of every kind sent by WhatsAppWeb servers. WhatsAppWeb uses these messages
|
||||
to built a Store, which is used to save these "secondary" information. These messages may contain
|
||||
presence (available, last see) information, or just the battery status of your phone.
|
||||
*/
|
||||
type JsonMessageHandler interface {
|
||||
Handler
|
||||
HandleJsonMessage(message string)
|
||||
}
|
||||
|
||||
/**
|
||||
The RawMessageHandler interface needs to be implemented to receive raw messages dispatched by the dispatcher.
|
||||
Raw messages are the raw protobuf structs instead of the easy-to-use structs in TextMessageHandler, ImageMessageHandler, etc..
|
||||
*/
|
||||
type RawMessageHandler interface {
|
||||
Handler
|
||||
HandleRawMessage(message *proto.WebMessageInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
The ContactListHandler interface needs to be implemented to applky custom actions to contact lists dispatched by the dispatcher.
|
||||
*/
|
||||
type ContactListHandler interface {
|
||||
Handler
|
||||
HandleContactList(contacts []Contact)
|
||||
}
|
||||
|
||||
/**
|
||||
The ChatListHandler interface needs to be implemented to apply custom actions to chat lists dispatched by the dispatcher.
|
||||
*/
|
||||
type ChatListHandler interface {
|
||||
Handler
|
||||
HandleChatList(contacts []Chat)
|
||||
}
|
||||
|
||||
/**
|
||||
The BatteryMessageHandler interface needs to be implemented to receive percentage the device connected dispatched by the dispatcher.
|
||||
*/
|
||||
type BatteryMessageHandler interface {
|
||||
Handler
|
||||
HandleBatteryMessage(battery BatteryMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
The NewContactHandler interface needs to be implemented to receive the contact's name for the first time.
|
||||
*/
|
||||
type NewContactHandler interface {
|
||||
Handler
|
||||
HandleNewContact(contact Contact)
|
||||
}
|
||||
|
||||
/*
|
||||
AddHandler adds an handler to the list of handler that receive dispatched messages.
|
||||
The provided handler must at least implement the Handler interface. Additionally implemented
|
||||
handlers(TextMessageHandler, ImageMessageHandler) are optional. At runtime it is checked if they are implemented
|
||||
and they are called if so and needed.
|
||||
*/
|
||||
func (wac *Conn) AddHandler(handler Handler) {
|
||||
wac.handler = append(wac.handler, handler)
|
||||
}
|
||||
|
||||
// RemoveHandler removes a handler from the list of handlers that receive dispatched messages.
|
||||
func (wac *Conn) RemoveHandler(handler Handler) bool {
|
||||
i := -1
|
||||
for k, v := range wac.handler {
|
||||
if v == handler {
|
||||
i = k
|
||||
break
|
||||
}
|
||||
}
|
||||
if i > -1 {
|
||||
wac.handler = append(wac.handler[:i], wac.handler[i+1:]...)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveHandlers empties the list of handlers that receive dispatched messages.
|
||||
func (wac *Conn) RemoveHandlers() {
|
||||
wac.handler = make([]Handler, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) shouldCallSynchronously(handler Handler) bool {
|
||||
sh, ok := handler.(SyncHandler)
|
||||
return ok && sh.ShouldCallSynchronously()
|
||||
}
|
||||
|
||||
func (wac *Conn) handle(message interface{}) {
|
||||
wac.handleWithCustomHandlers(message, wac.handler)
|
||||
}
|
||||
|
||||
func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handler) {
|
||||
switch m := message.(type) {
|
||||
case error:
|
||||
for _, h := range handlers {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
h.HandleError(m)
|
||||
} else {
|
||||
go h.HandleError(m)
|
||||
}
|
||||
}
|
||||
case string:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(JsonMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleJsonMessage(m)
|
||||
} else {
|
||||
go x.HandleJsonMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case TextMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(TextMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleTextMessage(m)
|
||||
} else {
|
||||
go x.HandleTextMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case ImageMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(ImageMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleImageMessage(m)
|
||||
} else {
|
||||
go x.HandleImageMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case VideoMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(VideoMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleVideoMessage(m)
|
||||
} else {
|
||||
go x.HandleVideoMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case AudioMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(AudioMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleAudioMessage(m)
|
||||
} else {
|
||||
go x.HandleAudioMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case DocumentMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(DocumentMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleDocumentMessage(m)
|
||||
} else {
|
||||
go x.HandleDocumentMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case LocationMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(LocationMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleLocationMessage(m)
|
||||
} else {
|
||||
go x.HandleLocationMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
case LiveLocationMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(LiveLocationMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleLiveLocationMessage(m)
|
||||
} else {
|
||||
go x.HandleLiveLocationMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case StickerMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(StickerMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleStickerMessage(m)
|
||||
} else {
|
||||
go x.HandleStickerMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case ContactMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(ContactMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleContactMessage(m)
|
||||
} else {
|
||||
go x.HandleContactMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case BatteryMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(BatteryMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleBatteryMessage(m)
|
||||
} else {
|
||||
go x.HandleBatteryMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case Contact:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(NewContactHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleNewContact(m)
|
||||
} else {
|
||||
go x.HandleNewContact(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case ProductMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(ProductMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleProductMessage(m)
|
||||
} else {
|
||||
go x.HandleProductMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case OrderMessage:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(OrderMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleOrderMessage(m)
|
||||
} else {
|
||||
go x.HandleOrderMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *proto.WebMessageInfo:
|
||||
for _, h := range handlers {
|
||||
if x, ok := h.(RawMessageHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleRawMessage(m)
|
||||
} else {
|
||||
go x.HandleRawMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (wac *Conn) handleContacts(contacts interface{}) {
|
||||
var contactList []Contact
|
||||
c, ok := contacts.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, contact := range c {
|
||||
contactNode, ok := contact.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
contactList = append(contactList, Contact{
|
||||
jid,
|
||||
contactNode.Attributes["notify"],
|
||||
contactNode.Attributes["name"],
|
||||
contactNode.Attributes["short"],
|
||||
})
|
||||
}
|
||||
for _, h := range wac.handler {
|
||||
if x, ok := h.(ContactListHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleContactList(contactList)
|
||||
} else {
|
||||
go x.HandleContactList(contactList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) handleChats(chats interface{}) {
|
||||
var chatList []Chat
|
||||
c, ok := chats.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, chat := range c {
|
||||
chatNode, ok := chat.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
chatList = append(chatList, Chat{
|
||||
jid,
|
||||
chatNode.Attributes["name"],
|
||||
chatNode.Attributes["count"],
|
||||
chatNode.Attributes["t"],
|
||||
chatNode.Attributes["mute"],
|
||||
chatNode.Attributes["spam"],
|
||||
})
|
||||
}
|
||||
for _, h := range wac.handler {
|
||||
if x, ok := h.(ChatListHandler); ok {
|
||||
if wac.shouldCallSynchronously(h) {
|
||||
x.HandleChatList(chatList)
|
||||
} else {
|
||||
go x.HandleChatList(chatList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) dispatch(msg interface{}) {
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch message := msg.(type) {
|
||||
case *binary.Node:
|
||||
if message.Description == "action" {
|
||||
if con, ok := message.Content.([]interface{}); ok {
|
||||
for a := range con {
|
||||
if v, ok := con[a].(*proto.WebMessageInfo); ok {
|
||||
wac.handle(v)
|
||||
wac.handle(ParseProtoMessage(v))
|
||||
}
|
||||
|
||||
if v, ok := con[a].(binary.Node); ok {
|
||||
wac.handle(ParseNodeMessage(v))
|
||||
}
|
||||
}
|
||||
} else if con, ok := message.Content.([]binary.Node); ok {
|
||||
for a := range con {
|
||||
wac.handle(ParseNodeMessage(con[a]))
|
||||
}
|
||||
}
|
||||
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
||||
wac.updateContacts(message.Content)
|
||||
wac.handleContacts(message.Content)
|
||||
} else if message.Description == "response" && message.Attributes["type"] == "chat" {
|
||||
wac.updateChats(message.Content)
|
||||
wac.handleChats(message.Content)
|
||||
}
|
||||
case error:
|
||||
wac.handle(message)
|
||||
case string:
|
||||
wac.handle(message)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown type in dipatcher chan: %T", msg)
|
||||
}
|
||||
}
|
||||
221
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
221
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
@@ -1,221 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||
)
|
||||
|
||||
func Download(url string, mediaKey []byte, appInfo MediaType, fileLength int) ([]byte, error) {
|
||||
if url == "" {
|
||||
return nil, fmt.Errorf("no url present")
|
||||
}
|
||||
file, mac, err := downloadMedia(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = validateMedia(iv, file, macKey, mac); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := cbc.Decrypt(cipherKey, iv, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) != fileLength {
|
||||
return nil, fmt.Errorf("file length does not match. Expected: %v, got: %v", fileLength, len(data))
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func validateMedia(iv []byte, file []byte, macKey []byte, mac []byte) error {
|
||||
h := hmac.New(sha256.New, macKey)
|
||||
n, err := h.Write(append(iv, file...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n < 10 {
|
||||
return fmt.Errorf("hash to short")
|
||||
}
|
||||
if !hmac.Equal(h.Sum(nil)[:10], mac) {
|
||||
return fmt.Errorf("invalid media hmac")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMediaKeys(mediaKey []byte, appInfo MediaType) (iv, cipherKey, macKey, refKey []byte, err error) {
|
||||
mediaKeyExpanded, err := hkdf.Expand(mediaKey, 112, string(appInfo))
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
return mediaKeyExpanded[:16], mediaKeyExpanded[16:48], mediaKeyExpanded[48:80], mediaKeyExpanded[80:], nil
|
||||
}
|
||||
|
||||
func downloadMedia(url string) (file []byte, mac []byte, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode)
|
||||
}
|
||||
if resp.ContentLength <= 10 {
|
||||
return nil, nil, fmt.Errorf("file to short")
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
n := len(data)
|
||||
return data[:n-10], data[n-10 : n], nil
|
||||
}
|
||||
|
||||
type MediaConn struct {
|
||||
Status int `json:"status"`
|
||||
MediaConn struct {
|
||||
Auth string `json:"auth"`
|
||||
TTL int `json:"ttl"`
|
||||
Hosts []struct {
|
||||
Hostname string `json:"hostname"`
|
||||
IPs []struct {
|
||||
IP4 net.IP `json:"ip4"`
|
||||
IP6 net.IP `json:"ip6"`
|
||||
} `json:"ips"`
|
||||
} `json:"hosts"`
|
||||
} `json:"media_conn"`
|
||||
}
|
||||
|
||||
func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
|
||||
queryReq := []interface{}{"query", "mediaConn"}
|
||||
ch, err := wac.writeJson(queryReq)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
var resp MediaConn
|
||||
select {
|
||||
case r := <-ch:
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return "", "", 0, fmt.Errorf("query media conn timed out")
|
||||
}
|
||||
|
||||
if resp.Status != http.StatusOK {
|
||||
return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
|
||||
}
|
||||
|
||||
for _, h := range resp.MediaConn.Hosts {
|
||||
if h.Hostname != "" {
|
||||
return h.Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", 0, fmt.Errorf("query media conn responded with no host")
|
||||
}
|
||||
|
||||
var mediaTypeMap = map[MediaType]string{
|
||||
MediaImage: "/mms/image",
|
||||
MediaVideo: "/mms/video",
|
||||
MediaDocument: "/mms/document",
|
||||
MediaAudio: "/mms/audio",
|
||||
}
|
||||
|
||||
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
mediaKey = make([]byte, 32)
|
||||
rand.Read(mediaKey)
|
||||
|
||||
iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
enc, err := cbc.Encrypt(cipherKey, iv, data)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
fileLength = uint64(len(data))
|
||||
|
||||
h := hmac.New(sha256.New, macKey)
|
||||
h.Write(append(iv, enc...))
|
||||
mac := h.Sum(nil)[:10]
|
||||
|
||||
sha := sha256.New()
|
||||
sha.Write(data)
|
||||
fileSha256 = sha.Sum(nil)
|
||||
|
||||
sha.Reset()
|
||||
sha.Write(append(enc, mac...))
|
||||
fileEncSha256 = sha.Sum(nil)
|
||||
|
||||
hostname, auth, _, err := wac.queryMediaConn()
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
token := base64.URLEncoding.EncodeToString(fileEncSha256)
|
||||
q := url.Values{
|
||||
"auth": []string{auth},
|
||||
"token": []string{token},
|
||||
}
|
||||
path := mediaTypeMap[appInfo]
|
||||
uploadURL := url.URL{
|
||||
Scheme: "https",
|
||||
Host: hostname,
|
||||
Path: fmt.Sprintf("%s/%s", path, token),
|
||||
RawQuery: q.Encode(),
|
||||
}
|
||||
|
||||
body := bytes.NewReader(append(enc, mac...))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, uploadURL.String(), body)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Origin", "https://web.whatsapp.com")
|
||||
req.Header.Set("Referer", "https://web.whatsapp.com/")
|
||||
|
||||
client := &http.Client{}
|
||||
// Submit the request
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return "", nil, nil, nil, 0, fmt.Errorf("upload failed with status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
var jsonRes map[string]string
|
||||
if err := json.NewDecoder(res.Body).Decode(&jsonRes); err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
|
||||
return jsonRes["url"], mediaKey, fileEncSha256, fileSha256, fileLength, nil
|
||||
}
|
||||
1005
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
1005
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
File diff suppressed because it is too large
Load Diff
65
vendor/github.com/Rhymen/go-whatsapp/profile.go
generated
vendored
65
vendor/github.com/Rhymen/go-whatsapp/profile.go
generated
vendored
@@ -1,65 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
)
|
||||
|
||||
// Pictures must be JPG 640x640 and 96x96, respectively
|
||||
func (wac *Conn) UploadProfilePic(image, preview []byte) (<-chan string, error) {
|
||||
tag := fmt.Sprintf("%d.--%d", time.Now().Unix(), wac.msgCount*19)
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{
|
||||
binary.Node{
|
||||
Description: "picture",
|
||||
Attributes: map[string]string{
|
||||
"id": tag,
|
||||
"jid": wac.Info.Wid,
|
||||
"type": "set",
|
||||
},
|
||||
Content: []binary.Node{
|
||||
{
|
||||
Description: "image",
|
||||
Attributes: nil,
|
||||
Content: image,
|
||||
},
|
||||
{
|
||||
Description: "preview",
|
||||
Attributes: nil,
|
||||
Content: preview,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return wac.writeBinary(n, profile, 136, tag)
|
||||
}
|
||||
|
||||
func (wac *Conn) UpdateProfileName(name string) (<-chan string, error) {
|
||||
tag := fmt.Sprintf("%d.--%d", time.Now().Unix(), wac.msgCount*19)
|
||||
n := binary.Node{
|
||||
Description: "action",
|
||||
Attributes: map[string]string{
|
||||
"type": "set",
|
||||
"epoch": strconv.Itoa(wac.msgCount),
|
||||
},
|
||||
Content: []interface{}{
|
||||
binary.Node{
|
||||
Description: "profile",
|
||||
Attributes: map[string]string{
|
||||
"name": name,
|
||||
},
|
||||
Content: []binary.Node{},
|
||||
},
|
||||
},
|
||||
}
|
||||
return wac.writeBinary(n, profile, ignore, tag)
|
||||
}
|
||||
142
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
142
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
@@ -1,142 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (wac *Conn) readPump() {
|
||||
defer func() {
|
||||
wac.wg.Done()
|
||||
_, _ = wac.Disconnect()
|
||||
}()
|
||||
|
||||
var readErr error
|
||||
var msgType int
|
||||
var reader io.Reader
|
||||
|
||||
for {
|
||||
readerFound := make(chan struct{})
|
||||
go func() {
|
||||
if wac.ws != nil {
|
||||
msgType, reader, readErr = wac.ws.conn.NextReader()
|
||||
}
|
||||
close(readerFound)
|
||||
}()
|
||||
select {
|
||||
case <-readerFound:
|
||||
if readErr != nil {
|
||||
wac.handle(&ErrConnectionFailed{Err: readErr})
|
||||
return
|
||||
}
|
||||
msg, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "error reading message from Reader"))
|
||||
continue
|
||||
}
|
||||
err = wac.processReadData(msgType, msg)
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "error processing data"))
|
||||
}
|
||||
case <-wac.ws.close:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) processReadData(msgType int, msg []byte) error {
|
||||
data := strings.SplitN(string(msg), ",", 2)
|
||||
|
||||
if data[0][0] == '!' { //Keep-Alive Timestamp
|
||||
data = append(data, data[0][1:]) //data[1]
|
||||
data[0] = "!"
|
||||
}
|
||||
|
||||
if len(data) == 2 && len(data[1]) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(data) != 2 || len(data[1]) == 0 {
|
||||
return ErrInvalidWsData
|
||||
}
|
||||
|
||||
wac.listener.RLock()
|
||||
listener, hasListener := wac.listener.m[data[0]]
|
||||
wac.listener.RUnlock()
|
||||
|
||||
if hasListener {
|
||||
// listener only exists for TextMessages query messages out of contact.go
|
||||
// If these binary query messages can be handled another way,
|
||||
// then the TextMessages, which are all JSON encoded, can directly
|
||||
// be unmarshalled. The listener chan could then be changed from type
|
||||
// chan string to something like chan map[string]interface{}. The unmarshalling
|
||||
// in several places, especially in session.go, would then be gone.
|
||||
listener <- data[1]
|
||||
close(listener)
|
||||
wac.removeListener(data[0])
|
||||
} else if msgType == websocket.BinaryMessage {
|
||||
wac.loginSessionLock.RLock()
|
||||
sess := wac.session
|
||||
wac.loginSessionLock.RUnlock()
|
||||
if sess == nil || sess.MacKey == nil || sess.EncKey == nil {
|
||||
return ErrInvalidWsState
|
||||
}
|
||||
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error decoding binary")
|
||||
}
|
||||
wac.dispatch(message)
|
||||
} else { //RAW json status updates
|
||||
wac.handle(string(data[1]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
||||
//message validation
|
||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||
if len(msg) < 33 {
|
||||
var response struct {
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(msg, &response); err == nil {
|
||||
if response.Status == http.StatusNotFound {
|
||||
return nil, ErrServerRespondedWith404
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("server responded with %d", response.Status))
|
||||
}
|
||||
|
||||
return nil, ErrInvalidServerResponse
|
||||
|
||||
}
|
||||
h2.Write([]byte(msg[32:]))
|
||||
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
||||
return nil, ErrInvalidHmac
|
||||
}
|
||||
|
||||
// message decrypt
|
||||
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decrypting message with AES-CBC failed")
|
||||
}
|
||||
|
||||
// message unmarshal
|
||||
message, err := binary.Unmarshal(d)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not decode binary")
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
532
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
532
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
@@ -1,532 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/curve25519"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||
)
|
||||
|
||||
//represents the WhatsAppWeb client version
|
||||
var waVersion = []int{2, 2142, 12}
|
||||
|
||||
/*
|
||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
|
||||
every time you should save the Session returned by Login and use RestoreWithSession the next time you want to login.
|
||||
Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after
|
||||
every re-login and should be saved every time.
|
||||
*/
|
||||
type Session struct {
|
||||
ClientId string
|
||||
ClientToken string
|
||||
ServerToken string
|
||||
EncKey []byte
|
||||
MacKey []byte
|
||||
Wid string
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
Battery int
|
||||
Platform string
|
||||
Connected bool
|
||||
Pushname string
|
||||
Wid string
|
||||
Lc string
|
||||
Phone *PhoneInfo
|
||||
Plugged bool
|
||||
Tos int
|
||||
Lg string
|
||||
Is24h bool
|
||||
}
|
||||
|
||||
type PhoneInfo struct {
|
||||
Mcc string
|
||||
Mnc string
|
||||
OsVersion string
|
||||
DeviceManufacturer string
|
||||
DeviceModel string
|
||||
OsBuildNumber string
|
||||
WaVersion string
|
||||
}
|
||||
|
||||
func newInfoFromReq(info map[string]interface{}) *Info {
|
||||
phoneInfo := info["phone"].(map[string]interface{})
|
||||
|
||||
ret := &Info{
|
||||
Battery: int(info["battery"].(float64)),
|
||||
Platform: info["platform"].(string),
|
||||
Connected: info["connected"].(bool),
|
||||
Pushname: info["pushname"].(string),
|
||||
Wid: info["wid"].(string),
|
||||
Lc: info["lc"].(string),
|
||||
Phone: &PhoneInfo{
|
||||
phoneInfo["mcc"].(string),
|
||||
phoneInfo["mnc"].(string),
|
||||
phoneInfo["os_version"].(string),
|
||||
phoneInfo["device_manufacturer"].(string),
|
||||
phoneInfo["device_model"].(string),
|
||||
phoneInfo["os_build_number"].(string),
|
||||
phoneInfo["wa_version"].(string),
|
||||
},
|
||||
Plugged: info["plugged"].(bool),
|
||||
Lg: info["lg"].(string),
|
||||
Tos: int(info["tos"].(float64)),
|
||||
}
|
||||
|
||||
if is24h, ok := info["is24h"]; ok {
|
||||
ret.Is24h = is24h.(bool)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
CheckCurrentServerVersion is based on the login method logic in order to establish the websocket connection and get
|
||||
the current version from the server with the `admin init` command. This can be very useful for automations in which
|
||||
you need to quickly perceive new versions (mostly patches) and update your application so it suddenly stops working.
|
||||
*/
|
||||
func CheckCurrentServerVersion() ([]int, error) {
|
||||
wac, err := NewConn(5 * time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to create connection")
|
||||
}
|
||||
|
||||
clientId := make([]byte, 16)
|
||||
if _, err = rand.Read(clientId); err != nil {
|
||||
return nil, fmt.Errorf("error creating random ClientId: %v", err)
|
||||
}
|
||||
|
||||
b64ClientId := base64.StdEncoding.EncodeToString(clientId)
|
||||
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
|
||||
loginChan, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing login: %s", err.Error())
|
||||
}
|
||||
|
||||
// Retrieve an answer from the websocket
|
||||
var r string
|
||||
select {
|
||||
case r = <-loginChan:
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return nil, fmt.Errorf("login connection timed out")
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return nil, fmt.Errorf("error decoding login: %s", err.Error())
|
||||
}
|
||||
|
||||
// Take the curr property as X.Y.Z and split it into as int slice
|
||||
curr := resp["curr"].(string)
|
||||
currArray := strings.Split(curr, ".")
|
||||
version := make([]int, len(currArray))
|
||||
for i := range version {
|
||||
version[i], _ = strconv.Atoi(currArray[i])
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
/*
|
||||
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
|
||||
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
|
||||
*/
|
||||
func (wac *Conn) SetClientName(long, short string, version string) error {
|
||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||
return fmt.Errorf("cannot change client name after logging in")
|
||||
}
|
||||
wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
SetClientVersion sets WhatsApp client version
|
||||
Default value is 0.4.2080
|
||||
*/
|
||||
func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
|
||||
waVersion = []int{major, minor, patch}
|
||||
}
|
||||
|
||||
// GetClientVersion returns WhatsApp client version
|
||||
func (wac *Conn) GetClientVersion() []int {
|
||||
return waVersion
|
||||
}
|
||||
|
||||
/*
|
||||
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
|
||||
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel
|
||||
as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data
|
||||
should be displayed as an qr code in a way you prefer. To print a qr code to console you can use:
|
||||
github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
||||
wac, err := whatsapp.NewConn(5 * time.Second)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
qr := make(chan string)
|
||||
go func() {
|
||||
terminal := qrcodeTerminal.New()
|
||||
terminal.Get(<-qr).Print()
|
||||
}()
|
||||
|
||||
session, err := wac.Login(qr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error during login: %v\n", err)
|
||||
}
|
||||
fmt.Printf("login successful, session: %v\n", session)
|
||||
*/
|
||||
func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||
session := Session{}
|
||||
//Makes sure that only a single Login or Restore can happen at the same time
|
||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||
return session, ErrLoginInProgress
|
||||
}
|
||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||
|
||||
if wac.loggedIn {
|
||||
return session, ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||
return session, err
|
||||
}
|
||||
|
||||
//logged in?!?
|
||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||
return session, fmt.Errorf("already logged in")
|
||||
}
|
||||
|
||||
clientId := make([]byte, 16)
|
||||
_, err := rand.Read(clientId)
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error creating random ClientId: %v", err)
|
||||
}
|
||||
|
||||
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
|
||||
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
|
||||
loginChan, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error writing login: %v\n", err)
|
||||
}
|
||||
|
||||
var r string
|
||||
select {
|
||||
case r = <-loginChan:
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return session, fmt.Errorf("login connection timed out")
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return session, fmt.Errorf("error decoding login resp: %v\n", err)
|
||||
}
|
||||
|
||||
var ref string
|
||||
if rref, ok := resp["ref"].(string); ok {
|
||||
ref = rref
|
||||
} else {
|
||||
return session, fmt.Errorf("error decoding login resp: invalid resp['ref']\n")
|
||||
}
|
||||
|
||||
priv, pub, err := curve25519.GenerateKey()
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error generating keys: %v\n", err)
|
||||
}
|
||||
|
||||
//listener for Login response
|
||||
s1 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s1"] = s1
|
||||
wac.listener.Unlock()
|
||||
|
||||
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
||||
|
||||
var resp2 []interface{}
|
||||
select {
|
||||
case r1 := <-s1:
|
||||
wac.loginSessionLock.Lock()
|
||||
defer wac.loginSessionLock.Unlock()
|
||||
if err := json.Unmarshal([]byte(r1), &resp2); err != nil {
|
||||
return session, fmt.Errorf("error decoding qr code resp: %v", err)
|
||||
}
|
||||
case <-time.After(time.Duration(resp["ttl"].(float64)) * time.Millisecond):
|
||||
return session, fmt.Errorf("qr code scan timed out")
|
||||
}
|
||||
|
||||
info := resp2[1].(map[string]interface{})
|
||||
|
||||
wac.Info = newInfoFromReq(info)
|
||||
|
||||
session.ClientToken = info["clientToken"].(string)
|
||||
session.ServerToken = info["serverToken"].(string)
|
||||
session.Wid = info["wid"].(string)
|
||||
s := info["secret"].(string)
|
||||
decodedSecret, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error decoding secret: %v", err)
|
||||
}
|
||||
|
||||
var pubKey [32]byte
|
||||
copy(pubKey[:], decodedSecret[:32])
|
||||
|
||||
sharedSecret := curve25519.GenerateSharedSecret(*priv, pubKey)
|
||||
|
||||
hash := sha256.New
|
||||
|
||||
nullKey := make([]byte, 32)
|
||||
h := hmac.New(hash, nullKey)
|
||||
h.Write(sharedSecret)
|
||||
|
||||
sharedSecretExtended, err := hkdf.Expand(h.Sum(nil), 80, "")
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("hkdf error: %v", err)
|
||||
}
|
||||
|
||||
//login validation
|
||||
checkSecret := make([]byte, 112)
|
||||
copy(checkSecret[:32], decodedSecret[:32])
|
||||
copy(checkSecret[32:], decodedSecret[64:])
|
||||
h2 := hmac.New(hash, sharedSecretExtended[32:64])
|
||||
h2.Write(checkSecret)
|
||||
if !hmac.Equal(h2.Sum(nil), decodedSecret[32:64]) {
|
||||
return session, fmt.Errorf("abort login")
|
||||
}
|
||||
|
||||
keysEncrypted := make([]byte, 96)
|
||||
copy(keysEncrypted[:16], sharedSecretExtended[64:])
|
||||
copy(keysEncrypted[16:], decodedSecret[64:])
|
||||
|
||||
keyDecrypted, err := cbc.Decrypt(sharedSecretExtended[:32], nil, keysEncrypted)
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error decryptAes: %v", err)
|
||||
}
|
||||
|
||||
session.EncKey = keyDecrypted[:32]
|
||||
session.MacKey = keyDecrypted[32:64]
|
||||
wac.session = &session
|
||||
wac.loggedIn = true
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
//TODO: GoDoc
|
||||
/*
|
||||
Basically the old RestoreSession functionality
|
||||
*/
|
||||
func (wac *Conn) RestoreWithSession(session Session) (_ Session, err error) {
|
||||
if wac.loggedIn {
|
||||
return Session{}, ErrAlreadyLoggedIn
|
||||
}
|
||||
old := wac.session
|
||||
defer func() {
|
||||
if err != nil {
|
||||
wac.session = old
|
||||
}
|
||||
}()
|
||||
wac.session = &session
|
||||
|
||||
if err = wac.Restore(); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, err
|
||||
}
|
||||
return *wac.session, nil
|
||||
}
|
||||
|
||||
/*//TODO: GoDoc
|
||||
RestoreWithSession is the function that restores a given session. It will try to reestablish the connection to the
|
||||
WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be
|
||||
saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not
|
||||
suggested. If so, a challenge has to be resolved which is just another possible point of failure.
|
||||
*/
|
||||
func (wac *Conn) Restore() error {
|
||||
//Makes sure that only a single Login or Restore can happen at the same time
|
||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||
return ErrLoginInProgress
|
||||
}
|
||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||
|
||||
if wac.session == nil {
|
||||
return ErrInvalidSession
|
||||
}
|
||||
|
||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||
return err
|
||||
}
|
||||
|
||||
if wac.loggedIn {
|
||||
return ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
//listener for Conn or challenge; s1 is not allowed to drop
|
||||
s1 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s1"] = s1
|
||||
wac.listener.Unlock()
|
||||
|
||||
//admin init
|
||||
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
|
||||
initChan, err := wac.writeJson(init)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing admin init: %v\n", err)
|
||||
}
|
||||
|
||||
//admin login with takeover
|
||||
login := []interface{}{"admin", "login", wac.session.ClientToken, wac.session.ServerToken, wac.session.ClientId, "takeover"}
|
||||
loginChan, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing admin login: %v\n", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-initChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("init responded with %d", resp["status"])
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("restore session init timed out")
|
||||
}
|
||||
|
||||
//wait for s1
|
||||
var connResp []interface{}
|
||||
select {
|
||||
case r1 := <-s1:
|
||||
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("error decoding s1 message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.timeTag = ""
|
||||
//check for an error message
|
||||
select {
|
||||
case r := <-loginChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
return fmt.Errorf("admin login responded with %d", int(resp["status"].(float64)))
|
||||
}
|
||||
default:
|
||||
// not even an error message – assume timeout
|
||||
return fmt.Errorf("restore session connection timed out")
|
||||
}
|
||||
}
|
||||
|
||||
//check if challenge is present
|
||||
if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" {
|
||||
s2 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s2"] = s2
|
||||
wac.listener.Unlock()
|
||||
|
||||
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("error resolving challenge: %v\n", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-s2:
|
||||
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("error decoding s2 message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("restore session challenge timed out")
|
||||
}
|
||||
}
|
||||
|
||||
//check for login 200 --> login success
|
||||
select {
|
||||
case r := <-loginChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("admin login responded with %d", resp["status"])
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.timeTag = ""
|
||||
return fmt.Errorf("restore session login timed out")
|
||||
}
|
||||
|
||||
info := connResp[1].(map[string]interface{})
|
||||
|
||||
wac.Info = newInfoFromReq(info)
|
||||
|
||||
//set new tokens
|
||||
wac.session.ClientToken = info["clientToken"].(string)
|
||||
wac.session.ServerToken = info["serverToken"].(string)
|
||||
wac.session.Wid = info["wid"].(string)
|
||||
wac.loggedIn = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) resolveChallenge(challenge string) error {
|
||||
decoded, err := base64.StdEncoding.DecodeString(challenge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h2.Write([]byte(decoded))
|
||||
|
||||
ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId}
|
||||
challengeChan, err := wac.writeJson(ch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing challenge: %v\n", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-challengeChan:
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return fmt.Errorf("error decoding login resp: %v\n", err)
|
||||
}
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
return fmt.Errorf("challenge responded with %d\n", resp["status"])
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return fmt.Errorf("connection timed out")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Logout is the function to logout from a WhatsApp session. Logging out means invalidating the current session.
|
||||
The session can not be resumed and will disappear on your phone in the WhatsAppWeb client list.
|
||||
*/
|
||||
func (wac *Conn) Logout() error {
|
||||
login := []interface{}{"admin", "Conn", "disconnect"}
|
||||
_, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing logout: %v\n", err)
|
||||
}
|
||||
|
||||
wac.loggedIn = false
|
||||
|
||||
return nil
|
||||
}
|
||||
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
@@ -1,80 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
Contacts map[string]Contact
|
||||
Chats map[string]Chat
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
Jid string
|
||||
Notify string
|
||||
Name string
|
||||
Short string
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
Jid string
|
||||
Name string
|
||||
Unread string
|
||||
LastMessageTime string
|
||||
IsMuted string
|
||||
IsMarkedSpam string
|
||||
}
|
||||
|
||||
func newStore() *Store {
|
||||
return &Store{
|
||||
make(map[string]Contact),
|
||||
make(map[string]Chat),
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) updateContacts(contacts interface{}) {
|
||||
c, ok := contacts.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, contact := range c {
|
||||
contactNode, ok := contact.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
wac.Store.Contacts[jid] = Contact{
|
||||
jid,
|
||||
contactNode.Attributes["notify"],
|
||||
contactNode.Attributes["name"],
|
||||
contactNode.Attributes["short"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) updateChats(chats interface{}) {
|
||||
c, ok := chats.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, chat := range c {
|
||||
chatNode, ok := chat.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
wac.Store.Chats[jid] = Chat{
|
||||
jid,
|
||||
chatNode.Attributes["name"],
|
||||
chatNode.Attributes["count"],
|
||||
chatNode.Attributes["t"],
|
||||
chatNode.Attributes["mute"],
|
||||
chatNode.Attributes["spam"],
|
||||
}
|
||||
}
|
||||
}
|
||||
195
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
195
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
@@ -1,195 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (wac *Conn) addListener(ch chan string, messageTag string) {
|
||||
wac.listener.Lock()
|
||||
wac.listener.m[messageTag] = ch
|
||||
wac.listener.Unlock()
|
||||
}
|
||||
|
||||
func (wac *Conn) removeListener(answerMessageTag string) {
|
||||
wac.listener.Lock()
|
||||
delete(wac.listener.m, answerMessageTag)
|
||||
wac.listener.Unlock()
|
||||
}
|
||||
|
||||
//writeJson enqueues a json message into the writeChan
|
||||
func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
|
||||
|
||||
ch := make(chan string, 1)
|
||||
|
||||
wac.writerLock.Lock()
|
||||
defer wac.writerLock.Unlock()
|
||||
|
||||
d, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
close(ch)
|
||||
return ch, err
|
||||
}
|
||||
|
||||
ts := time.Now().Unix()
|
||||
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d))
|
||||
|
||||
if wac.timeTag == "" {
|
||||
tss := fmt.Sprintf("%d", ts)
|
||||
wac.timeTag = tss[len(tss)-3:]
|
||||
}
|
||||
|
||||
wac.addListener(ch, messageTag)
|
||||
|
||||
err = wac.write(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
close(ch)
|
||||
wac.removeListener(messageTag)
|
||||
return ch, err
|
||||
}
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) {
|
||||
|
||||
ch := make(chan string, 1)
|
||||
|
||||
if len(messageTag) < 2 {
|
||||
close(ch)
|
||||
return ch, ErrMissingMessageTag
|
||||
}
|
||||
|
||||
wac.writerLock.Lock()
|
||||
defer wac.writerLock.Unlock()
|
||||
|
||||
data, err := wac.encryptBinaryMessage(node)
|
||||
if err != nil {
|
||||
close(ch)
|
||||
return ch, errors.Wrap(err, "encryptBinaryMessage(node) failed")
|
||||
}
|
||||
|
||||
bytes := []byte(messageTag + ",")
|
||||
bytes = append(bytes, byte(metric), byte(flag))
|
||||
bytes = append(bytes, data...)
|
||||
|
||||
wac.addListener(ch, messageTag)
|
||||
|
||||
err = wac.write(websocket.BinaryMessage, bytes)
|
||||
if err != nil {
|
||||
close(ch)
|
||||
wac.removeListener(messageTag)
|
||||
return ch, errors.Wrap(err, "failed to write message")
|
||||
}
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) sendKeepAlive() error {
|
||||
|
||||
respChan := make(chan string, 1)
|
||||
wac.addListener(respChan, "!")
|
||||
|
||||
bytes := []byte("?,,")
|
||||
err := wac.write(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
close(respChan)
|
||||
wac.removeListener("!")
|
||||
return errors.Wrap(err, "error sending keepAlive")
|
||||
}
|
||||
|
||||
select {
|
||||
case resp := <-respChan:
|
||||
msecs, err := strconv.ParseInt(resp, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error converting time string to uint")
|
||||
}
|
||||
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
||||
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return ErrConnectionTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
When phone is unreachable, WhatsAppWeb sends ["admin","test"] time after time to try a successful contact.
|
||||
Tested with Airplane mode and no connection at all.
|
||||
*/
|
||||
func (wac *Conn) sendAdminTest() (bool, error) {
|
||||
data := []interface{}{"admin", "test"}
|
||||
|
||||
r, err := wac.writeJson(data)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "error sending admin test")
|
||||
}
|
||||
|
||||
var response []interface{}
|
||||
|
||||
select {
|
||||
case resp := <-r:
|
||||
if err := json.Unmarshal([]byte(resp), &response); err != nil {
|
||||
return false, fmt.Errorf("error decoding response message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return false, ErrConnectionTimeout
|
||||
}
|
||||
|
||||
if len(response) == 2 && response[0].(string) == "Pong" && response[1].(bool) == true {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) write(messageType int, data []byte) error {
|
||||
|
||||
if wac == nil || wac.ws == nil {
|
||||
return ErrInvalidWebsocket
|
||||
}
|
||||
|
||||
wac.ws.Lock()
|
||||
err := wac.ws.conn.WriteMessage(messageType, data)
|
||||
wac.ws.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error writing to websocket")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) {
|
||||
b, err := binary.Marshal(node)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "binary node marshal failed")
|
||||
}
|
||||
|
||||
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "encrypt failed")
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h.Write(cipher)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
data = append(data, hash[:32]...)
|
||||
data = append(data, cipher...)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
19
vendor/github.com/kballard/go-shellquote/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kballard/go-shellquote/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2014 Kevin Ballard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
36
vendor/github.com/kballard/go-shellquote/README
generated
vendored
Normal file
36
vendor/github.com/kballard/go-shellquote/README
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
PACKAGE
|
||||
|
||||
package shellquote
|
||||
import "github.com/kballard/go-shellquote"
|
||||
|
||||
Shellquote provides utilities for joining/splitting strings using sh's
|
||||
word-splitting rules.
|
||||
|
||||
VARIABLES
|
||||
|
||||
var (
|
||||
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
|
||||
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
|
||||
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
|
||||
)
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
func Join(args ...string) string
|
||||
Join quotes each argument and joins them with a space. If passed to
|
||||
/bin/sh, the resulting string will be split back into the original
|
||||
arguments.
|
||||
|
||||
func Split(input string) (words []string, err error)
|
||||
Split splits a string according to /bin/sh's word-splitting rules. It
|
||||
supports backslash-escapes, single-quotes, and double-quotes. Notably it
|
||||
does not support the $'' style of quoting. It also doesn't attempt to
|
||||
perform any other sort of expansion, including brace expansion, shell
|
||||
expansion, or pathname expansion.
|
||||
|
||||
If the given input has an unterminated quoted string or ends in a
|
||||
backslash-escape, one of UnterminatedSingleQuoteError,
|
||||
UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
|
||||
|
||||
|
||||
3
vendor/github.com/kballard/go-shellquote/doc.go
generated
vendored
Normal file
3
vendor/github.com/kballard/go-shellquote/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Shellquote provides utilities for joining/splitting strings using sh's
|
||||
// word-splitting rules.
|
||||
package shellquote
|
||||
102
vendor/github.com/kballard/go-shellquote/quote.go
generated
vendored
Normal file
102
vendor/github.com/kballard/go-shellquote/quote.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package shellquote
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Join quotes each argument and joins them with a space.
|
||||
// If passed to /bin/sh, the resulting string will be split back into the
|
||||
// original arguments.
|
||||
func Join(args ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, arg := range args {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
quote(arg, &buf)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
const (
|
||||
specialChars = "\\'\"`${[|&;<>()*?!"
|
||||
extraSpecialChars = " \t\n"
|
||||
prefixChars = "~"
|
||||
)
|
||||
|
||||
func quote(word string, buf *bytes.Buffer) {
|
||||
// We want to try to produce a "nice" output. As such, we will
|
||||
// backslash-escape most characters, but if we encounter a space, or if we
|
||||
// encounter an extra-special char (which doesn't work with
|
||||
// backslash-escaping) we switch over to quoting the whole word. We do this
|
||||
// with a space because it's typically easier for people to read multi-word
|
||||
// arguments when quoted with a space rather than with ugly backslashes
|
||||
// everywhere.
|
||||
origLen := buf.Len()
|
||||
|
||||
if len(word) == 0 {
|
||||
// oops, no content
|
||||
buf.WriteString("''")
|
||||
return
|
||||
}
|
||||
|
||||
cur, prev := word, word
|
||||
atStart := true
|
||||
for len(cur) > 0 {
|
||||
c, l := utf8.DecodeRuneInString(cur)
|
||||
cur = cur[l:]
|
||||
if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) {
|
||||
// copy the non-special chars up to this point
|
||||
if len(cur) < len(prev) {
|
||||
buf.WriteString(prev[0 : len(prev)-len(cur)-l])
|
||||
}
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteRune(c)
|
||||
prev = cur
|
||||
} else if strings.ContainsRune(extraSpecialChars, c) {
|
||||
// start over in quote mode
|
||||
buf.Truncate(origLen)
|
||||
goto quote
|
||||
}
|
||||
atStart = false
|
||||
}
|
||||
if len(prev) > 0 {
|
||||
buf.WriteString(prev)
|
||||
}
|
||||
return
|
||||
|
||||
quote:
|
||||
// quote mode
|
||||
// Use single-quotes, but if we find a single-quote in the word, we need
|
||||
// to terminate the string, emit an escaped quote, and start the string up
|
||||
// again
|
||||
inQuote := false
|
||||
for len(word) > 0 {
|
||||
i := strings.IndexRune(word, '\'')
|
||||
if i == -1 {
|
||||
break
|
||||
}
|
||||
if i > 0 {
|
||||
if !inQuote {
|
||||
buf.WriteByte('\'')
|
||||
inQuote = true
|
||||
}
|
||||
buf.WriteString(word[0:i])
|
||||
}
|
||||
word = word[i+1:]
|
||||
if inQuote {
|
||||
buf.WriteByte('\'')
|
||||
inQuote = false
|
||||
}
|
||||
buf.WriteString("\\'")
|
||||
}
|
||||
if len(word) > 0 {
|
||||
if !inQuote {
|
||||
buf.WriteByte('\'')
|
||||
}
|
||||
buf.WriteString(word)
|
||||
buf.WriteByte('\'')
|
||||
}
|
||||
}
|
||||
156
vendor/github.com/kballard/go-shellquote/unquote.go
generated
vendored
Normal file
156
vendor/github.com/kballard/go-shellquote/unquote.go
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
package shellquote
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
|
||||
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
|
||||
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
|
||||
)
|
||||
|
||||
var (
|
||||
splitChars = " \n\t"
|
||||
singleChar = '\''
|
||||
doubleChar = '"'
|
||||
escapeChar = '\\'
|
||||
doubleEscapeChars = "$`\"\n\\"
|
||||
)
|
||||
|
||||
// Split splits a string according to /bin/sh's word-splitting rules. It
|
||||
// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
|
||||
// not support the $'' style of quoting. It also doesn't attempt to perform any
|
||||
// other sort of expansion, including brace expansion, shell expansion, or
|
||||
// pathname expansion.
|
||||
//
|
||||
// If the given input has an unterminated quoted string or ends in a
|
||||
// backslash-escape, one of UnterminatedSingleQuoteError,
|
||||
// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
|
||||
func Split(input string) (words []string, err error) {
|
||||
var buf bytes.Buffer
|
||||
words = make([]string, 0)
|
||||
|
||||
for len(input) > 0 {
|
||||
// skip any splitChars at the start
|
||||
c, l := utf8.DecodeRuneInString(input)
|
||||
if strings.ContainsRune(splitChars, c) {
|
||||
input = input[l:]
|
||||
continue
|
||||
} else if c == escapeChar {
|
||||
// Look ahead for escaped newline so we can skip over it
|
||||
next := input[l:]
|
||||
if len(next) == 0 {
|
||||
err = UnterminatedEscapeError
|
||||
return
|
||||
}
|
||||
c2, l2 := utf8.DecodeRuneInString(next)
|
||||
if c2 == '\n' {
|
||||
input = next[l2:]
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var word string
|
||||
word, input, err = splitWord(input, &buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
words = append(words, word)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
|
||||
buf.Reset()
|
||||
|
||||
raw:
|
||||
{
|
||||
cur := input
|
||||
for len(cur) > 0 {
|
||||
c, l := utf8.DecodeRuneInString(cur)
|
||||
cur = cur[l:]
|
||||
if c == singleChar {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||
input = cur
|
||||
goto single
|
||||
} else if c == doubleChar {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||
input = cur
|
||||
goto double
|
||||
} else if c == escapeChar {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||
input = cur
|
||||
goto escape
|
||||
} else if strings.ContainsRune(splitChars, c) {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||
return buf.String(), cur, nil
|
||||
}
|
||||
}
|
||||
if len(input) > 0 {
|
||||
buf.WriteString(input)
|
||||
input = ""
|
||||
}
|
||||
goto done
|
||||
}
|
||||
|
||||
escape:
|
||||
{
|
||||
if len(input) == 0 {
|
||||
return "", "", UnterminatedEscapeError
|
||||
}
|
||||
c, l := utf8.DecodeRuneInString(input)
|
||||
if c == '\n' {
|
||||
// a backslash-escaped newline is elided from the output entirely
|
||||
} else {
|
||||
buf.WriteString(input[:l])
|
||||
}
|
||||
input = input[l:]
|
||||
}
|
||||
goto raw
|
||||
|
||||
single:
|
||||
{
|
||||
i := strings.IndexRune(input, singleChar)
|
||||
if i == -1 {
|
||||
return "", "", UnterminatedSingleQuoteError
|
||||
}
|
||||
buf.WriteString(input[0:i])
|
||||
input = input[i+1:]
|
||||
goto raw
|
||||
}
|
||||
|
||||
double:
|
||||
{
|
||||
cur := input
|
||||
for len(cur) > 0 {
|
||||
c, l := utf8.DecodeRuneInString(cur)
|
||||
cur = cur[l:]
|
||||
if c == doubleChar {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||
input = cur
|
||||
goto raw
|
||||
} else if c == escapeChar {
|
||||
// bash only supports certain escapes in double-quoted strings
|
||||
c2, l2 := utf8.DecodeRuneInString(cur)
|
||||
cur = cur[l2:]
|
||||
if strings.ContainsRune(doubleEscapeChars, c2) {
|
||||
buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
|
||||
if c2 == '\n' {
|
||||
// newline is special, skip the backslash entirely
|
||||
} else {
|
||||
buf.WriteRune(c2)
|
||||
}
|
||||
input = cur
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", "", UnterminatedDoubleQuoteError
|
||||
}
|
||||
|
||||
done:
|
||||
return buf.String(), input, nil
|
||||
}
|
||||
4
vendor/github.com/mdp/qrterminal/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/mdp/qrterminal/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
11
vendor/github.com/mdp/qrterminal/CHANGELOG.md
generated
vendored
Normal file
11
vendor/github.com/mdp/qrterminal/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
## 1.0.0
|
||||
|
||||
Update to add a quiet zone border to the QR Code - #5 and fixed by [WindomZ](https://github.com/WindomZ) #8
|
||||
|
||||
- This can be configured with the `QuietZone int` option
|
||||
- Defaults to 4 'pixels' wide to match the QR Code spec
|
||||
- This alters the size of the barcode considerably and is therefore a breaking change, resulting in a bump to v1.0.0
|
||||
|
||||
## 0.2.1
|
||||
|
||||
Fix direction of the qr code #6 by (https://github.com/mattn)
|
||||
78
vendor/github.com/mdp/qrterminal/README.md
generated
vendored
Normal file
78
vendor/github.com/mdp/qrterminal/README.md
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
# QRCode Terminal
|
||||
|
||||
[](https://travis-ci.org/mdp/qrterminal)
|
||||
|
||||
A golang library for generating QR codes in the terminal.
|
||||
|
||||
Originally this was a port of the [NodeJS version](https://github.com/gtanner/qrcode-terminal). Recently it's been updated to allow for smaller code generation using ASCII 'half blocks'
|
||||
|
||||
## Example
|
||||
Full size ASCII block QR Code:
|
||||
<img src="https://user-images.githubusercontent.com/2868/37992336-0ba06b56-31d1-11e8-9d32-5c6bb008dc74.png" alt="alt text" width="225" height="225">
|
||||
|
||||
Smaller 'half blocks' in the terminal:
|
||||
<img src="https://user-images.githubusercontent.com/2868/37992371-243d4238-31d1-11e8-92f8-e34a794b21af.png" alt="alt text" width="225" height="225">
|
||||
|
||||
## Install
|
||||
|
||||
`go get github.com/mdp/qrterminal`
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/mdp/qrterminal"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Generate a 'dense' qrcode with the 'Low' level error correction and write it to Stdout
|
||||
qrterminal.Generate("https://github.com/mdp/qrterminal", qrterminal.L, os.Stdout)
|
||||
}
|
||||
```
|
||||
|
||||
### More complicated
|
||||
|
||||
Large Inverted barcode with medium redundancy and a 1 pixel border
|
||||
```go
|
||||
import (
|
||||
"github.com/mdp/qrterminal"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := qrterminal.Config{
|
||||
Level: qrterminal.M,
|
||||
Writer: os.Stdout,
|
||||
BlackChar: qrterminal.WHITE,
|
||||
WhiteChar: qrterminal.BLACK,
|
||||
QuietZone: 1,
|
||||
}
|
||||
qrterminal.GenerateWithConfig("https://github.com/mdp/qrterminal", config)
|
||||
}
|
||||
```
|
||||
|
||||
HalfBlock barcode with medium redundancy
|
||||
```go
|
||||
import (
|
||||
"github.com/mdp/qrterminal"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := qrterminal.Config{
|
||||
HalfBlocks: true,
|
||||
Level: qrterminal.M,
|
||||
Writer: os.Stdout,
|
||||
}
|
||||
qrterminal.Generate("https://github.com/mdp/qrterminal", config)
|
||||
}
|
||||
```
|
||||
|
||||
Credits:
|
||||
|
||||
Mark Percival m@mdp.im
|
||||
[Matthew Kennerly](https://github.com/mtkennerly)
|
||||
[Viric](https://github.com/viric)
|
||||
[WindomZ](https://github.com/WindomZ)
|
||||
[mattn](https://github.com/mattn)
|
||||
153
vendor/github.com/mdp/qrterminal/qrterminal.go
generated
vendored
Normal file
153
vendor/github.com/mdp/qrterminal/qrterminal.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package qrterminal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"rsc.io/qr"
|
||||
)
|
||||
|
||||
const WHITE = "\033[47m \033[0m"
|
||||
const BLACK = "\033[40m \033[0m"
|
||||
|
||||
// Use ascii blocks to form the QR Code
|
||||
const BLACK_WHITE = "▄"
|
||||
const BLACK_BLACK = " "
|
||||
const WHITE_BLACK = "▀"
|
||||
const WHITE_WHITE = "█"
|
||||
|
||||
// Level - the QR Code's redundancy level
|
||||
const H = qr.H
|
||||
const M = qr.M
|
||||
const L = qr.L
|
||||
|
||||
// default is 4-pixel-wide white quiet zone
|
||||
const QUIET_ZONE = 4
|
||||
|
||||
//Config for generating a barcode
|
||||
type Config struct {
|
||||
Level qr.Level
|
||||
Writer io.Writer
|
||||
HalfBlocks bool
|
||||
BlackChar string
|
||||
BlackWhiteChar string
|
||||
WhiteChar string
|
||||
WhiteBlackChar string
|
||||
QuietZone int
|
||||
}
|
||||
|
||||
func (c *Config) writeFullBlocks(w io.Writer, code *qr.Code) {
|
||||
white := c.WhiteChar
|
||||
black := c.BlackChar
|
||||
|
||||
// Frame the barcode in a 1 pixel border
|
||||
w.Write([]byte(stringRepeat(stringRepeat(white,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone))) // top border
|
||||
for i := 0; i <= code.Size; i++ {
|
||||
w.Write([]byte(stringRepeat(white, c.QuietZone))) // left border
|
||||
for j := 0; j <= code.Size; j++ {
|
||||
if code.Black(j, i) {
|
||||
w.Write([]byte(black))
|
||||
} else {
|
||||
w.Write([]byte(white))
|
||||
}
|
||||
}
|
||||
w.Write([]byte(stringRepeat(white, c.QuietZone-1) + "\n")) // right border
|
||||
}
|
||||
w.Write([]byte(stringRepeat(stringRepeat(white,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone-1))) // bottom border
|
||||
}
|
||||
|
||||
func (c *Config) writeHalfBlocks(w io.Writer, code *qr.Code) {
|
||||
ww := c.WhiteChar
|
||||
bb := c.BlackChar
|
||||
wb := c.WhiteBlackChar
|
||||
bw := c.BlackWhiteChar
|
||||
// Frame the barcode in a 4 pixel border
|
||||
// top border
|
||||
if c.QuietZone%2 != 0 {
|
||||
w.Write([]byte(stringRepeat(bw, code.Size+c.QuietZone*2) + "\n"))
|
||||
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||
} else {
|
||||
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||
}
|
||||
for i := 0; i <= code.Size; i += 2 {
|
||||
w.Write([]byte(stringRepeat(ww, c.QuietZone))) // left border
|
||||
for j := 0; j <= code.Size; j++ {
|
||||
next_black := false
|
||||
if i+1 < code.Size {
|
||||
next_black = code.Black(j, i+1)
|
||||
}
|
||||
curr_black := code.Black(j, i)
|
||||
if curr_black && next_black {
|
||||
w.Write([]byte(bb))
|
||||
} else if curr_black && !next_black {
|
||||
w.Write([]byte(bw))
|
||||
} else if !curr_black && !next_black {
|
||||
w.Write([]byte(ww))
|
||||
} else {
|
||||
w.Write([]byte(wb))
|
||||
}
|
||||
}
|
||||
w.Write([]byte(stringRepeat(ww, c.QuietZone-1) + "\n")) // right border
|
||||
}
|
||||
// bottom border
|
||||
if c.QuietZone%2 == 0 {
|
||||
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2-1)))
|
||||
w.Write([]byte(stringRepeat(wb, code.Size+c.QuietZone*2) + "\n"))
|
||||
} else {
|
||||
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||
}
|
||||
}
|
||||
|
||||
func stringRepeat(s string, count int) string {
|
||||
if count <= 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Repeat(s, count)
|
||||
}
|
||||
|
||||
// GenerateWithConfig expects a string to encode and a config
|
||||
func GenerateWithConfig(text string, config Config) {
|
||||
if config.QuietZone < 1 {
|
||||
config.QuietZone = 1 // at least 1-pixel-wide white quiet zone
|
||||
}
|
||||
w := config.Writer
|
||||
code, _ := qr.Encode(text, config.Level)
|
||||
if config.HalfBlocks {
|
||||
config.writeHalfBlocks(w, code)
|
||||
} else {
|
||||
config.writeFullBlocks(w, code)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a QR Code and write it out to io.Writer
|
||||
func Generate(text string, l qr.Level, w io.Writer) {
|
||||
config := Config{
|
||||
Level: l,
|
||||
Writer: w,
|
||||
BlackChar: BLACK,
|
||||
WhiteChar: WHITE,
|
||||
QuietZone: QUIET_ZONE,
|
||||
}
|
||||
GenerateWithConfig(text, config)
|
||||
}
|
||||
|
||||
// Generate a QR Code with half blocks and write it out to io.Writer
|
||||
func GenerateHalfBlock(text string, l qr.Level, w io.Writer) {
|
||||
config := Config{
|
||||
Level: l,
|
||||
Writer: w,
|
||||
HalfBlocks: true,
|
||||
BlackChar: BLACK_BLACK,
|
||||
WhiteBlackChar: WHITE_BLACK,
|
||||
WhiteChar: WHITE_WHITE,
|
||||
BlackWhiteChar: BLACK_WHITE,
|
||||
QuietZone: QUIET_ZONE,
|
||||
}
|
||||
GenerateWithConfig(text, config)
|
||||
}
|
||||
27
vendor/github.com/remyoudompheng/bigfft/LICENSE
generated
vendored
Normal file
27
vendor/github.com/remyoudompheng/bigfft/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
43
vendor/github.com/remyoudompheng/bigfft/README
generated
vendored
Normal file
43
vendor/github.com/remyoudompheng/bigfft/README
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
Benchmarking math/big vs. bigfft
|
||||
|
||||
Number size old ns/op new ns/op delta
|
||||
1kb 1599 1640 +2.56%
|
||||
10kb 61533 62170 +1.04%
|
||||
50kb 833693 831051 -0.32%
|
||||
100kb 2567995 2693864 +4.90%
|
||||
1Mb 105237800 28446400 -72.97%
|
||||
5Mb 1272947000 168554600 -86.76%
|
||||
10Mb 3834354000 405120200 -89.43%
|
||||
20Mb 11514488000 845081600 -92.66%
|
||||
50Mb 49199945000 2893950000 -94.12%
|
||||
100Mb 147599836000 5921594000 -95.99%
|
||||
|
||||
Benchmarking GMP vs bigfft
|
||||
|
||||
Number size GMP ns/op Go ns/op delta
|
||||
1kb 536 1500 +179.85%
|
||||
10kb 26669 50777 +90.40%
|
||||
50kb 252270 658534 +161.04%
|
||||
100kb 686813 2127534 +209.77%
|
||||
1Mb 12100000 22391830 +85.06%
|
||||
5Mb 111731843 133550600 +19.53%
|
||||
10Mb 212314000 318595800 +50.06%
|
||||
20Mb 490196000 671512800 +36.99%
|
||||
50Mb 1280000000 2451476000 +91.52%
|
||||
100Mb 2673000000 5228991000 +95.62%
|
||||
|
||||
Benchmarks were run on a Core 2 Quad Q8200 (2.33GHz).
|
||||
FFT is enabled when input numbers are over 200kbits.
|
||||
|
||||
Scanning large decimal number from strings.
|
||||
(math/big [n^2 complexity] vs bigfft [n^1.6 complexity], Core i5-4590)
|
||||
|
||||
Digits old ns/op new ns/op delta
|
||||
1e3 9995 10876 +8.81%
|
||||
1e4 175356 243806 +39.03%
|
||||
1e5 9427422 6780545 -28.08%
|
||||
1e6 1776707489 144867502 -91.85%
|
||||
2e6 6865499995 346540778 -94.95%
|
||||
5e6 42641034189 1069878799 -97.49%
|
||||
10e6 151975273589 2693328580 -98.23%
|
||||
|
||||
36
vendor/github.com/remyoudompheng/bigfft/arith_386.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_386.s
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
JMP math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addMulVVW(SB)
|
||||
|
||||
38
vendor/github.com/remyoudompheng/bigfft/arith_amd64.s
generated
vendored
Normal file
38
vendor/github.com/remyoudompheng/bigfft/arith_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
JMP math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addMulVVW(SB)
|
||||
|
||||
36
vendor/github.com/remyoudompheng/bigfft/arith_arm.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_arm.s
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
B math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
B math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
B math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
B math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
B math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
B math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
B math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
B math∕big·addMulVVW(SB)
|
||||
|
||||
36
vendor/github.com/remyoudompheng/bigfft/arith_arm64.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
B math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
B math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
B math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
B math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
B math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
B math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
B math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
B math∕big·addMulVVW(SB)
|
||||
|
||||
16
vendor/github.com/remyoudompheng/bigfft/arith_decl.go
generated
vendored
Normal file
16
vendor/github.com/remyoudompheng/bigfft/arith_decl.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bigfft
|
||||
|
||||
import . "math/big"
|
||||
|
||||
// implemented in arith_$GOARCH.s
|
||||
func addVV(z, x, y []Word) (c Word)
|
||||
func subVV(z, x, y []Word) (c Word)
|
||||
func addVW(z, x []Word, y Word) (c Word)
|
||||
func subVW(z, x []Word, y Word) (c Word)
|
||||
func shlVU(z, x []Word, s uint) (c Word)
|
||||
func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
40
vendor/github.com/remyoudompheng/bigfft/arith_mips64x.s
generated
vendored
Normal file
40
vendor/github.com/remyoudompheng/bigfft/arith_mips64x.s
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
// +build mips64 mips64le
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
JMP math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addMulVVW(SB)
|
||||
|
||||
40
vendor/github.com/remyoudompheng/bigfft/arith_mipsx.s
generated
vendored
Normal file
40
vendor/github.com/remyoudompheng/bigfft/arith_mipsx.s
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
// +build mips mipsle
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
JMP math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
JMP math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
JMP math∕big·addMulVVW(SB)
|
||||
|
||||
38
vendor/github.com/remyoudompheng/bigfft/arith_ppc64x.s
generated
vendored
Normal file
38
vendor/github.com/remyoudompheng/bigfft/arith_ppc64x.s
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
// +build ppc64 ppc64le
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
BR math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
BR math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
BR math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
BR math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
BR math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
BR math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
BR math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
BR math∕big·addMulVVW(SB)
|
||||
|
||||
37
vendor/github.com/remyoudompheng/bigfft/arith_s390x.s
generated
vendored
Normal file
37
vendor/github.com/remyoudompheng/bigfft/arith_s390x.s
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
// Trampolines to math/big assembly implementations.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func addVV(z, x, y []Word) (c Word)
|
||||
TEXT ·addVV(SB),NOSPLIT,$0
|
||||
BR math∕big·addVV(SB)
|
||||
|
||||
// func subVV(z, x, y []Word) (c Word)
|
||||
TEXT ·subVV(SB),NOSPLIT,$0
|
||||
BR math∕big·subVV(SB)
|
||||
|
||||
// func addVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addVW(SB),NOSPLIT,$0
|
||||
BR math∕big·addVW(SB)
|
||||
|
||||
// func subVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·subVW(SB),NOSPLIT,$0
|
||||
BR math∕big·subVW(SB)
|
||||
|
||||
// func shlVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||
BR math∕big·shlVU(SB)
|
||||
|
||||
// func shrVU(z, x []Word, s uint) (c Word)
|
||||
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||
BR math∕big·shrVU(SB)
|
||||
|
||||
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||
BR math∕big·mulAddVWW(SB)
|
||||
|
||||
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||
BR math∕big·addMulVVW(SB)
|
||||
|
||||
216
vendor/github.com/remyoudompheng/bigfft/fermat.go
generated
vendored
Normal file
216
vendor/github.com/remyoudompheng/bigfft/fermat.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
package bigfft
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Arithmetic modulo 2^n+1.
|
||||
|
||||
// A fermat of length w+1 represents a number modulo 2^(w*_W) + 1. The last
|
||||
// word is zero or one. A number has at most two representatives satisfying the
|
||||
// 0-1 last word constraint.
|
||||
type fermat nat
|
||||
|
||||
func (n fermat) String() string { return nat(n).String() }
|
||||
|
||||
func (z fermat) norm() {
|
||||
n := len(z) - 1
|
||||
c := z[n]
|
||||
if c == 0 {
|
||||
return
|
||||
}
|
||||
if z[0] >= c {
|
||||
z[n] = 0
|
||||
z[0] -= c
|
||||
return
|
||||
}
|
||||
// z[0] < z[n].
|
||||
subVW(z, z, c) // Substract c
|
||||
if c > 1 {
|
||||
z[n] -= c - 1
|
||||
c = 1
|
||||
}
|
||||
// Add back c.
|
||||
if z[n] == 1 {
|
||||
z[n] = 0
|
||||
return
|
||||
} else {
|
||||
addVW(z, z, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Shift computes (x << k) mod (2^n+1).
|
||||
func (z fermat) Shift(x fermat, k int) {
|
||||
if len(z) != len(x) {
|
||||
panic("len(z) != len(x) in Shift")
|
||||
}
|
||||
n := len(x) - 1
|
||||
// Shift by n*_W is taking the opposite.
|
||||
k %= 2 * n * _W
|
||||
if k < 0 {
|
||||
k += 2 * n * _W
|
||||
}
|
||||
neg := false
|
||||
if k >= n*_W {
|
||||
k -= n * _W
|
||||
neg = true
|
||||
}
|
||||
|
||||
kw, kb := k/_W, k%_W
|
||||
|
||||
z[n] = 1 // Add (-1)
|
||||
if !neg {
|
||||
for i := 0; i < kw; i++ {
|
||||
z[i] = 0
|
||||
}
|
||||
// Shift left by kw words.
|
||||
// x = a·2^(n-k) + b
|
||||
// x<<k = (b<<k) - a
|
||||
copy(z[kw:], x[:n-kw])
|
||||
b := subVV(z[:kw+1], z[:kw+1], x[n-kw:])
|
||||
if z[kw+1] > 0 {
|
||||
z[kw+1] -= b
|
||||
} else {
|
||||
subVW(z[kw+1:], z[kw+1:], b)
|
||||
}
|
||||
} else {
|
||||
for i := kw + 1; i < n; i++ {
|
||||
z[i] = 0
|
||||
}
|
||||
// Shift left and negate, by kw words.
|
||||
copy(z[:kw+1], x[n-kw:n+1]) // z_low = x_high
|
||||
b := subVV(z[kw:n], z[kw:n], x[:n-kw]) // z_high -= x_low
|
||||
z[n] -= b
|
||||
}
|
||||
// Add back 1.
|
||||
if z[n] > 0 {
|
||||
z[n]--
|
||||
} else if z[0] < ^big.Word(0) {
|
||||
z[0]++
|
||||
} else {
|
||||
addVW(z, z, 1)
|
||||
}
|
||||
// Shift left by kb bits
|
||||
shlVU(z, z, uint(kb))
|
||||
z.norm()
|
||||
}
|
||||
|
||||
// ShiftHalf shifts x by k/2 bits the left. Shifting by 1/2 bit
|
||||
// is multiplication by sqrt(2) mod 2^n+1 which is 2^(3n/4) - 2^(n/4).
|
||||
// A temporary buffer must be provided in tmp.
|
||||
func (z fermat) ShiftHalf(x fermat, k int, tmp fermat) {
|
||||
n := len(z) - 1
|
||||
if k%2 == 0 {
|
||||
z.Shift(x, k/2)
|
||||
return
|
||||
}
|
||||
u := (k - 1) / 2
|
||||
a := u + (3*_W/4)*n
|
||||
b := u + (_W/4)*n
|
||||
z.Shift(x, a)
|
||||
tmp.Shift(x, b)
|
||||
z.Sub(z, tmp)
|
||||
}
|
||||
|
||||
// Add computes addition mod 2^n+1.
|
||||
func (z fermat) Add(x, y fermat) fermat {
|
||||
if len(z) != len(x) {
|
||||
panic("Add: len(z) != len(x)")
|
||||
}
|
||||
addVV(z, x, y) // there cannot be a carry here.
|
||||
z.norm()
|
||||
return z
|
||||
}
|
||||
|
||||
// Sub computes substraction mod 2^n+1.
|
||||
func (z fermat) Sub(x, y fermat) fermat {
|
||||
if len(z) != len(x) {
|
||||
panic("Add: len(z) != len(x)")
|
||||
}
|
||||
n := len(y) - 1
|
||||
b := subVV(z[:n], x[:n], y[:n])
|
||||
b += y[n]
|
||||
// If b > 0, we need to subtract b<<n, which is the same as adding b.
|
||||
z[n] = x[n]
|
||||
if z[0] <= ^big.Word(0)-b {
|
||||
z[0] += b
|
||||
} else {
|
||||
addVW(z, z, b)
|
||||
}
|
||||
z.norm()
|
||||
return z
|
||||
}
|
||||
|
||||
func (z fermat) Mul(x, y fermat) fermat {
|
||||
if len(x) != len(y) {
|
||||
panic("Mul: len(x) != len(y)")
|
||||
}
|
||||
n := len(x) - 1
|
||||
if n < 30 {
|
||||
z = z[:2*n+2]
|
||||
basicMul(z, x, y)
|
||||
z = z[:2*n+1]
|
||||
} else {
|
||||
var xi, yi, zi big.Int
|
||||
xi.SetBits(x)
|
||||
yi.SetBits(y)
|
||||
zi.SetBits(z)
|
||||
zb := zi.Mul(&xi, &yi).Bits()
|
||||
if len(zb) <= n {
|
||||
// Short product.
|
||||
copy(z, zb)
|
||||
for i := len(zb); i < len(z); i++ {
|
||||
z[i] = 0
|
||||
}
|
||||
return z
|
||||
}
|
||||
z = zb
|
||||
}
|
||||
// len(z) is at most 2n+1.
|
||||
if len(z) > 2*n+1 {
|
||||
panic("len(z) > 2n+1")
|
||||
}
|
||||
// We now have
|
||||
// z = z[:n] + 1<<(n*W) * z[n:2n+1]
|
||||
// which normalizes to:
|
||||
// z = z[:n] - z[n:2n] + z[2n]
|
||||
c1 := big.Word(0)
|
||||
if len(z) > 2*n {
|
||||
c1 = addVW(z[:n], z[:n], z[2*n])
|
||||
}
|
||||
c2 := big.Word(0)
|
||||
if len(z) >= 2*n {
|
||||
c2 = subVV(z[:n], z[:n], z[n:2*n])
|
||||
} else {
|
||||
m := len(z) - n
|
||||
c2 = subVV(z[:m], z[:m], z[n:])
|
||||
c2 = subVW(z[m:n], z[m:n], c2)
|
||||
}
|
||||
// Restore carries.
|
||||
// Substracting z[n] -= c2 is the same
|
||||
// as z[0] += c2
|
||||
z = z[:n+1]
|
||||
z[n] = c1
|
||||
c := addVW(z, z, c2)
|
||||
if c != 0 {
|
||||
panic("impossible")
|
||||
}
|
||||
z.norm()
|
||||
return z
|
||||
}
|
||||
|
||||
// copied from math/big
|
||||
//
|
||||
// basicMul multiplies x and y and leaves the result in z.
|
||||
// The (non-normalized) result is placed in z[0 : len(x) + len(y)].
|
||||
func basicMul(z, x, y fermat) {
|
||||
// initialize z
|
||||
for i := 0; i < len(z); i++ {
|
||||
z[i] = 0
|
||||
}
|
||||
for i, d := range y {
|
||||
if d != 0 {
|
||||
z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
370
vendor/github.com/remyoudompheng/bigfft/fft.go
generated
vendored
Normal file
370
vendor/github.com/remyoudompheng/bigfft/fft.go
generated
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
// Package bigfft implements multiplication of big.Int using FFT.
|
||||
//
|
||||
// The implementation is based on the Schönhage-Strassen method
|
||||
// using integer FFT modulo 2^n+1.
|
||||
package bigfft
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const _W = int(unsafe.Sizeof(big.Word(0)) * 8)
|
||||
|
||||
type nat []big.Word
|
||||
|
||||
func (n nat) String() string {
|
||||
v := new(big.Int)
|
||||
v.SetBits(n)
|
||||
return v.String()
|
||||
}
|
||||
|
||||
// fftThreshold is the size (in words) above which FFT is used over
|
||||
// Karatsuba from math/big.
|
||||
//
|
||||
// TestCalibrate seems to indicate a threshold of 60kbits on 32-bit
|
||||
// arches and 110kbits on 64-bit arches.
|
||||
var fftThreshold = 1800
|
||||
|
||||
// Mul computes the product x*y and returns z.
|
||||
// It can be used instead of the Mul method of
|
||||
// *big.Int from math/big package.
|
||||
func Mul(x, y *big.Int) *big.Int {
|
||||
xwords := len(x.Bits())
|
||||
ywords := len(y.Bits())
|
||||
if xwords > fftThreshold && ywords > fftThreshold {
|
||||
return mulFFT(x, y)
|
||||
}
|
||||
return new(big.Int).Mul(x, y)
|
||||
}
|
||||
|
||||
func mulFFT(x, y *big.Int) *big.Int {
|
||||
var xb, yb nat = x.Bits(), y.Bits()
|
||||
zb := fftmul(xb, yb)
|
||||
z := new(big.Int)
|
||||
z.SetBits(zb)
|
||||
if x.Sign()*y.Sign() < 0 {
|
||||
z.Neg(z)
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// A FFT size of K=1<<k is adequate when K is about 2*sqrt(N) where
|
||||
// N = x.Bitlen() + y.Bitlen().
|
||||
|
||||
func fftmul(x, y nat) nat {
|
||||
k, m := fftSize(x, y)
|
||||
xp := polyFromNat(x, k, m)
|
||||
yp := polyFromNat(y, k, m)
|
||||
rp := xp.Mul(&yp)
|
||||
return rp.Int()
|
||||
}
|
||||
|
||||
// fftSizeThreshold[i] is the maximal size (in bits) where we should use
|
||||
// fft size i.
|
||||
var fftSizeThreshold = [...]int64{0, 0, 0,
|
||||
4 << 10, 8 << 10, 16 << 10, // 5
|
||||
32 << 10, 64 << 10, 1 << 18, 1 << 20, 3 << 20, // 10
|
||||
8 << 20, 30 << 20, 100 << 20, 300 << 20, 600 << 20,
|
||||
}
|
||||
|
||||
// returns the FFT length k, m the number of words per chunk
|
||||
// such that m << k is larger than the number of words
|
||||
// in x*y.
|
||||
func fftSize(x, y nat) (k uint, m int) {
|
||||
words := len(x) + len(y)
|
||||
bits := int64(words) * int64(_W)
|
||||
k = uint(len(fftSizeThreshold))
|
||||
for i := range fftSizeThreshold {
|
||||
if fftSizeThreshold[i] > bits {
|
||||
k = uint(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
// The 1<<k chunks of m words must have N bits so that
|
||||
// 2^N-1 is larger than x*y. That is, m<<k > words
|
||||
m = words>>k + 1
|
||||
return
|
||||
}
|
||||
|
||||
// valueSize returns the length (in words) to use for polynomial
|
||||
// coefficients, to compute a correct product of polynomials P*Q
|
||||
// where deg(P*Q) < K (== 1<<k) and where coefficients of P and Q are
|
||||
// less than b^m (== 1 << (m*_W)).
|
||||
// The chosen length (in bits) must be a multiple of 1 << (k-extra).
|
||||
func valueSize(k uint, m int, extra uint) int {
|
||||
// The coefficients of P*Q are less than b^(2m)*K
|
||||
// so we need W * valueSize >= 2*m*W+K
|
||||
n := 2*m*_W + int(k) // necessary bits
|
||||
K := 1 << (k - extra)
|
||||
if K < _W {
|
||||
K = _W
|
||||
}
|
||||
n = ((n / K) + 1) * K // round to a multiple of K
|
||||
return n / _W
|
||||
}
|
||||
|
||||
// poly represents an integer via a polynomial in Z[x]/(x^K+1)
|
||||
// where K is the FFT length and b^m is the computation basis 1<<(m*_W).
|
||||
// If P = a[0] + a[1] x + ... a[n] x^(K-1), the associated natural number
|
||||
// is P(b^m).
|
||||
type poly struct {
|
||||
k uint // k is such that K = 1<<k.
|
||||
m int // the m such that P(b^m) is the original number.
|
||||
a []nat // a slice of at most K m-word coefficients.
|
||||
}
|
||||
|
||||
// polyFromNat slices the number x into a polynomial
|
||||
// with 1<<k coefficients made of m words.
|
||||
func polyFromNat(x nat, k uint, m int) poly {
|
||||
p := poly{k: k, m: m}
|
||||
length := len(x)/m + 1
|
||||
p.a = make([]nat, length)
|
||||
for i := range p.a {
|
||||
if len(x) < m {
|
||||
p.a[i] = make(nat, m)
|
||||
copy(p.a[i], x)
|
||||
break
|
||||
}
|
||||
p.a[i] = x[:m]
|
||||
x = x[m:]
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Int evaluates back a poly to its integer value.
|
||||
func (p *poly) Int() nat {
|
||||
length := len(p.a)*p.m + 1
|
||||
if na := len(p.a); na > 0 {
|
||||
length += len(p.a[na-1])
|
||||
}
|
||||
n := make(nat, length)
|
||||
m := p.m
|
||||
np := n
|
||||
for i := range p.a {
|
||||
l := len(p.a[i])
|
||||
c := addVV(np[:l], np[:l], p.a[i])
|
||||
if np[l] < ^big.Word(0) {
|
||||
np[l] += c
|
||||
} else {
|
||||
addVW(np[l:], np[l:], c)
|
||||
}
|
||||
np = np[m:]
|
||||
}
|
||||
n = trim(n)
|
||||
return n
|
||||
}
|
||||
|
||||
func trim(n nat) nat {
|
||||
for i := range n {
|
||||
if n[len(n)-1-i] != 0 {
|
||||
return n[:len(n)-i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mul multiplies p and q modulo X^K-1, where K = 1<<p.k.
|
||||
// The product is done via a Fourier transform.
|
||||
func (p *poly) Mul(q *poly) poly {
|
||||
// extra=2 because:
|
||||
// * some power of 2 is a K-th root of unity when n is a multiple of K/2.
|
||||
// * 2 itself is a square (see fermat.ShiftHalf)
|
||||
n := valueSize(p.k, p.m, 2)
|
||||
|
||||
pv, qv := p.Transform(n), q.Transform(n)
|
||||
rv := pv.Mul(&qv)
|
||||
r := rv.InvTransform()
|
||||
r.m = p.m
|
||||
return r
|
||||
}
|
||||
|
||||
// A polValues represents the value of a poly at the powers of a
|
||||
// K-th root of unity θ=2^(l/2) in Z/(b^n+1)Z, where b^n = 2^(K/4*l).
|
||||
type polValues struct {
|
||||
k uint // k is such that K = 1<<k.
|
||||
n int // the length of coefficients, n*_W a multiple of K/4.
|
||||
values []fermat // a slice of K (n+1)-word values
|
||||
}
|
||||
|
||||
// Transform evaluates p at θ^i for i = 0...K-1, where
|
||||
// θ is a K-th primitive root of unity in Z/(b^n+1)Z.
|
||||
func (p *poly) Transform(n int) polValues {
|
||||
k := p.k
|
||||
inputbits := make([]big.Word, (n+1)<<k)
|
||||
input := make([]fermat, 1<<k)
|
||||
// Now computed q(ω^i) for i = 0 ... K-1
|
||||
valbits := make([]big.Word, (n+1)<<k)
|
||||
values := make([]fermat, 1<<k)
|
||||
for i := range values {
|
||||
input[i] = inputbits[i*(n+1) : (i+1)*(n+1)]
|
||||
if i < len(p.a) {
|
||||
copy(input[i], p.a[i])
|
||||
}
|
||||
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
|
||||
}
|
||||
fourier(values, input, false, n, k)
|
||||
return polValues{k, n, values}
|
||||
}
|
||||
|
||||
// InvTransform reconstructs p (modulo X^K - 1) from its
|
||||
// values at θ^i for i = 0..K-1.
|
||||
func (v *polValues) InvTransform() poly {
|
||||
k, n := v.k, v.n
|
||||
|
||||
// Perform an inverse Fourier transform to recover p.
|
||||
pbits := make([]big.Word, (n+1)<<k)
|
||||
p := make([]fermat, 1<<k)
|
||||
for i := range p {
|
||||
p[i] = fermat(pbits[i*(n+1) : (i+1)*(n+1)])
|
||||
}
|
||||
fourier(p, v.values, true, n, k)
|
||||
// Divide by K, and untwist q to recover p.
|
||||
u := make(fermat, n+1)
|
||||
a := make([]nat, 1<<k)
|
||||
for i := range p {
|
||||
u.Shift(p[i], -int(k))
|
||||
copy(p[i], u)
|
||||
a[i] = nat(p[i])
|
||||
}
|
||||
return poly{k: k, m: 0, a: a}
|
||||
}
|
||||
|
||||
// NTransform evaluates p at θω^i for i = 0...K-1, where
|
||||
// θ is a (2K)-th primitive root of unity in Z/(b^n+1)Z
|
||||
// and ω = θ².
|
||||
func (p *poly) NTransform(n int) polValues {
|
||||
k := p.k
|
||||
if len(p.a) >= 1<<k {
|
||||
panic("Transform: len(p.a) >= 1<<k")
|
||||
}
|
||||
// θ is represented as a shift.
|
||||
θshift := (n * _W) >> k
|
||||
// p(x) = a_0 + a_1 x + ... + a_{K-1} x^(K-1)
|
||||
// p(θx) = q(x) where
|
||||
// q(x) = a_0 + θa_1 x + ... + θ^(K-1) a_{K-1} x^(K-1)
|
||||
//
|
||||
// Twist p by θ to obtain q.
|
||||
tbits := make([]big.Word, (n+1)<<k)
|
||||
twisted := make([]fermat, 1<<k)
|
||||
src := make(fermat, n+1)
|
||||
for i := range twisted {
|
||||
twisted[i] = fermat(tbits[i*(n+1) : (i+1)*(n+1)])
|
||||
if i < len(p.a) {
|
||||
for i := range src {
|
||||
src[i] = 0
|
||||
}
|
||||
copy(src, p.a[i])
|
||||
twisted[i].Shift(src, θshift*i)
|
||||
}
|
||||
}
|
||||
|
||||
// Now computed q(ω^i) for i = 0 ... K-1
|
||||
valbits := make([]big.Word, (n+1)<<k)
|
||||
values := make([]fermat, 1<<k)
|
||||
for i := range values {
|
||||
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
|
||||
}
|
||||
fourier(values, twisted, false, n, k)
|
||||
return polValues{k, n, values}
|
||||
}
|
||||
|
||||
// InvTransform reconstructs a polynomial from its values at
|
||||
// roots of x^K+1. The m field of the returned polynomial
|
||||
// is unspecified.
|
||||
func (v *polValues) InvNTransform() poly {
|
||||
k := v.k
|
||||
n := v.n
|
||||
θshift := (n * _W) >> k
|
||||
|
||||
// Perform an inverse Fourier transform to recover q.
|
||||
qbits := make([]big.Word, (n+1)<<k)
|
||||
q := make([]fermat, 1<<k)
|
||||
for i := range q {
|
||||
q[i] = fermat(qbits[i*(n+1) : (i+1)*(n+1)])
|
||||
}
|
||||
fourier(q, v.values, true, n, k)
|
||||
|
||||
// Divide by K, and untwist q to recover p.
|
||||
u := make(fermat, n+1)
|
||||
a := make([]nat, 1<<k)
|
||||
for i := range q {
|
||||
u.Shift(q[i], -int(k)-i*θshift)
|
||||
copy(q[i], u)
|
||||
a[i] = nat(q[i])
|
||||
}
|
||||
return poly{k: k, m: 0, a: a}
|
||||
}
|
||||
|
||||
// fourier performs an unnormalized Fourier transform
|
||||
// of src, a length 1<<k vector of numbers modulo b^n+1
|
||||
// where b = 1<<_W.
|
||||
func fourier(dst []fermat, src []fermat, backward bool, n int, k uint) {
|
||||
var rec func(dst, src []fermat, size uint)
|
||||
tmp := make(fermat, n+1) // pre-allocate temporary variables.
|
||||
tmp2 := make(fermat, n+1) // pre-allocate temporary variables.
|
||||
|
||||
// The recursion function of the FFT.
|
||||
// The root of unity used in the transform is ω=1<<(ω2shift/2).
|
||||
// The source array may use shifted indices (i.e. the i-th
|
||||
// element is src[i << idxShift]).
|
||||
rec = func(dst, src []fermat, size uint) {
|
||||
idxShift := k - size
|
||||
ω2shift := (4 * n * _W) >> size
|
||||
if backward {
|
||||
ω2shift = -ω2shift
|
||||
}
|
||||
|
||||
// Easy cases.
|
||||
if len(src[0]) != n+1 || len(dst[0]) != n+1 {
|
||||
panic("len(src[0]) != n+1 || len(dst[0]) != n+1")
|
||||
}
|
||||
switch size {
|
||||
case 0:
|
||||
copy(dst[0], src[0])
|
||||
return
|
||||
case 1:
|
||||
dst[0].Add(src[0], src[1<<idxShift]) // dst[0] = src[0] + src[1]
|
||||
dst[1].Sub(src[0], src[1<<idxShift]) // dst[1] = src[0] - src[1]
|
||||
return
|
||||
}
|
||||
|
||||
// Let P(x) = src[0] + src[1<<idxShift] * x + ... + src[K-1 << idxShift] * x^(K-1)
|
||||
// The P(x) = Q1(x²) + x*Q2(x²)
|
||||
// where Q1's coefficients are src with indices shifted by 1
|
||||
// where Q2's coefficients are src[1<<idxShift:] with indices shifted by 1
|
||||
|
||||
// Split destination vectors in halves.
|
||||
dst1 := dst[:1<<(size-1)]
|
||||
dst2 := dst[1<<(size-1):]
|
||||
// Transform Q1 and Q2 in the halves.
|
||||
rec(dst1, src, size-1)
|
||||
rec(dst2, src[1<<idxShift:], size-1)
|
||||
|
||||
// Reconstruct P's transform from transforms of Q1 and Q2.
|
||||
// dst[i] is dst1[i] + ω^i * dst2[i]
|
||||
// dst[i + 1<<(k-1)] is dst1[i] + ω^(i+K/2) * dst2[i]
|
||||
//
|
||||
for i := range dst1 {
|
||||
tmp.ShiftHalf(dst2[i], i*ω2shift, tmp2) // ω^i * dst2[i]
|
||||
dst2[i].Sub(dst1[i], tmp)
|
||||
dst1[i].Add(dst1[i], tmp)
|
||||
}
|
||||
}
|
||||
rec(dst, src, k)
|
||||
}
|
||||
|
||||
// Mul returns the pointwise product of p and q.
|
||||
func (p *polValues) Mul(q *polValues) (r polValues) {
|
||||
n := p.n
|
||||
r.k, r.n = p.k, p.n
|
||||
r.values = make([]fermat, len(p.values))
|
||||
bits := make([]big.Word, len(p.values)*(n+1))
|
||||
buf := make(fermat, 8*n)
|
||||
for i := range r.values {
|
||||
r.values[i] = bits[i*(n+1) : (i+1)*(n+1)]
|
||||
z := buf.Mul(p.values[i], q.values[i])
|
||||
copy(r.values[i], z)
|
||||
}
|
||||
return
|
||||
}
|
||||
70
vendor/github.com/remyoudompheng/bigfft/scan.go
generated
vendored
Normal file
70
vendor/github.com/remyoudompheng/bigfft/scan.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package bigfft
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// FromDecimalString converts the base 10 string
|
||||
// representation of a natural (non-negative) number
|
||||
// into a *big.Int.
|
||||
// Its asymptotic complexity is less than quadratic.
|
||||
func FromDecimalString(s string) *big.Int {
|
||||
var sc scanner
|
||||
z := new(big.Int)
|
||||
sc.scan(z, s)
|
||||
return z
|
||||
}
|
||||
|
||||
type scanner struct {
|
||||
// powers[i] is 10^(2^i * quadraticScanThreshold).
|
||||
powers []*big.Int
|
||||
}
|
||||
|
||||
func (s *scanner) chunkSize(size int) (int, *big.Int) {
|
||||
if size <= quadraticScanThreshold {
|
||||
panic("size < quadraticScanThreshold")
|
||||
}
|
||||
pow := uint(0)
|
||||
for n := size; n > quadraticScanThreshold; n /= 2 {
|
||||
pow++
|
||||
}
|
||||
// threshold * 2^(pow-1) <= size < threshold * 2^pow
|
||||
return quadraticScanThreshold << (pow - 1), s.power(pow - 1)
|
||||
}
|
||||
|
||||
func (s *scanner) power(k uint) *big.Int {
|
||||
for i := len(s.powers); i <= int(k); i++ {
|
||||
z := new(big.Int)
|
||||
if i == 0 {
|
||||
if quadraticScanThreshold%14 != 0 {
|
||||
panic("quadraticScanThreshold % 14 != 0")
|
||||
}
|
||||
z.Exp(big.NewInt(1e14), big.NewInt(quadraticScanThreshold/14), nil)
|
||||
} else {
|
||||
z.Mul(s.powers[i-1], s.powers[i-1])
|
||||
}
|
||||
s.powers = append(s.powers, z)
|
||||
}
|
||||
return s.powers[k]
|
||||
}
|
||||
|
||||
func (s *scanner) scan(z *big.Int, str string) {
|
||||
if len(str) <= quadraticScanThreshold {
|
||||
z.SetString(str, 10)
|
||||
return
|
||||
}
|
||||
sz, pow := s.chunkSize(len(str))
|
||||
// Scan the left half.
|
||||
s.scan(z, str[:len(str)-sz])
|
||||
// FIXME: reuse temporaries.
|
||||
left := Mul(z, pow)
|
||||
// Scan the right half
|
||||
s.scan(z, str[len(str)-sz:])
|
||||
z.Add(z, left)
|
||||
}
|
||||
|
||||
// quadraticScanThreshold is the number of digits
|
||||
// below which big.Int.SetString is more efficient
|
||||
// than subquadratic algorithms.
|
||||
// 1232 digits fit in 4096 bits.
|
||||
const quadraticScanThreshold = 1232
|
||||
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
@@ -1,4 +0,0 @@
|
||||
*.sw*
|
||||
*.png
|
||||
*.directory
|
||||
qrcode/qrcode
|
||||
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
|
||||
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2014 Tom Harwood
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
@@ -1,86 +0,0 @@
|
||||
# go-qrcode #
|
||||
|
||||
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'>
|
||||
|
||||
Package qrcode implements a QR Code encoder. [](https://travis-ci.org/skip2/go-qrcode)
|
||||
|
||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :)
|
||||
|
||||
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.
|
||||
|
||||
## Install
|
||||
|
||||
go get -u github.com/skip2/go-qrcode/...
|
||||
|
||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
||||
|
||||
## Usage
|
||||
|
||||
import qrcode "github.com/skip2/go-qrcode"
|
||||
|
||||
- **Create a 256x256 PNG image:**
|
||||
|
||||
var png []byte
|
||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
||||
|
||||
- **Create a 256x256 PNG image and write to a file:**
|
||||
|
||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
||||
|
||||
- **Create a 256x256 PNG image with custom colors and write to file:**
|
||||
|
||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
||||
|
||||
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.
|
||||
|
||||
## Documentation
|
||||
|
||||
[](https://godoc.org/github.com/skip2/go-qrcode)
|
||||
|
||||
## Demoapp
|
||||
|
||||
[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com)
|
||||
|
||||
## CLI
|
||||
|
||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
||||
|
||||
```
|
||||
qrcode -- QR Code encoder in Go
|
||||
https://github.com/skip2/go-qrcode
|
||||
|
||||
Flags:
|
||||
-d disable QR Code border
|
||||
-i invert black and white
|
||||
-o string
|
||||
out PNG file prefix, empty for stdout
|
||||
-s int
|
||||
image size (pixel) (default 256)
|
||||
-t print as text-art on stdout
|
||||
|
||||
Usage:
|
||||
1. Arguments except for flags are joined by " " and used to generate QR code.
|
||||
Default output is STDOUT, pipe to imagemagick command "display" to display
|
||||
on any X server.
|
||||
|
||||
qrcode hello word | display
|
||||
|
||||
2. Save to file if "display" not available:
|
||||
|
||||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png
|
||||
|
||||
```
|
||||
## Maximum capacity
|
||||
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.
|
||||
|
||||
## Borderless QR Codes
|
||||
|
||||
To aid QR Code reading software, QR codes have a built in whitespace border.
|
||||
|
||||
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.
|
||||
|
||||
## Links
|
||||
|
||||
- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code)
|
||||
- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)<br>
|
||||
- [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)
|
||||
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
@@ -1,273 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
// Package bitset implements an append only bit array.
|
||||
//
|
||||
// To create a Bitset and append some bits:
|
||||
// // Bitset Contents
|
||||
// b := bitset.New() // {}
|
||||
// b.AppendBools(true, true, false) // {1, 1, 0}
|
||||
// b.AppendBools(true) // {1, 1, 0, 1}
|
||||
// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0}
|
||||
//
|
||||
// To read values:
|
||||
//
|
||||
// len := b.Len() // 8
|
||||
// v := b.At(0) // 1
|
||||
// v = b.At(1) // 1
|
||||
// v = b.At(2) // 0
|
||||
// v = b.At(8) // 0
|
||||
package bitset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
b0 = false
|
||||
b1 = true
|
||||
)
|
||||
|
||||
// Bitset stores an array of bits.
|
||||
type Bitset struct {
|
||||
// The number of bits stored.
|
||||
numBits int
|
||||
|
||||
// Storage for individual bits.
|
||||
bits []byte
|
||||
}
|
||||
|
||||
// New returns an initialised Bitset with optional initial bits v.
|
||||
func New(v ...bool) *Bitset {
|
||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
||||
b.AppendBools(v...)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Clone returns a copy.
|
||||
func Clone(from *Bitset) *Bitset {
|
||||
return &Bitset{numBits: from.numBits, bits: from.bits[:]}
|
||||
}
|
||||
|
||||
// Substr returns a substring, consisting of the bits from indexes start to end.
|
||||
func (b *Bitset) Substr(start int, end int) *Bitset {
|
||||
if start > end || end > b.numBits {
|
||||
log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits)
|
||||
}
|
||||
|
||||
result := New()
|
||||
result.ensureCapacity(end - start)
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
if b.At(i) {
|
||||
result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8)
|
||||
}
|
||||
result.numBits++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// NewFromBase2String constructs and returns a Bitset from a string. The string
|
||||
// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0'
|
||||
// characters represent true/false bits respectively, and ' ' characters are
|
||||
// ignored.
|
||||
//
|
||||
// The function panics if the input string contains other characters.
|
||||
func NewFromBase2String(b2string string) *Bitset {
|
||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
||||
|
||||
for _, c := range b2string {
|
||||
switch c {
|
||||
case '1':
|
||||
b.AppendBools(true)
|
||||
case '0':
|
||||
b.AppendBools(false)
|
||||
case ' ':
|
||||
default:
|
||||
log.Panicf("Invalid char %c in NewFromBase2String", c)
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendBytes appends a list of whole bytes.
|
||||
func (b *Bitset) AppendBytes(data []byte) {
|
||||
for _, d := range data {
|
||||
b.AppendByte(d, 8)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendByte appends the numBits least significant bits from value.
|
||||
func (b *Bitset) AppendByte(value byte, numBits int) {
|
||||
b.ensureCapacity(numBits)
|
||||
|
||||
if numBits > 8 {
|
||||
log.Panicf("numBits %d out of range 0-8", numBits)
|
||||
}
|
||||
|
||||
for i := numBits - 1; i >= 0; i-- {
|
||||
if value&(1<<uint(i)) != 0 {
|
||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||
}
|
||||
|
||||
b.numBits++
|
||||
}
|
||||
}
|
||||
|
||||
// AppendUint32 appends the numBits least significant bits from value.
|
||||
func (b *Bitset) AppendUint32(value uint32, numBits int) {
|
||||
b.ensureCapacity(numBits)
|
||||
|
||||
if numBits > 32 {
|
||||
log.Panicf("numBits %d out of range 0-32", numBits)
|
||||
}
|
||||
|
||||
for i := numBits - 1; i >= 0; i-- {
|
||||
if value&(1<<uint(i)) != 0 {
|
||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||
}
|
||||
|
||||
b.numBits++
|
||||
}
|
||||
}
|
||||
|
||||
// ensureCapacity ensures the Bitset can store an additional |numBits|.
|
||||
//
|
||||
// The underlying array is expanded if necessary. To prevent frequent
|
||||
// reallocation, expanding the underlying array at least doubles its capacity.
|
||||
func (b *Bitset) ensureCapacity(numBits int) {
|
||||
numBits += b.numBits
|
||||
|
||||
newNumBytes := numBits / 8
|
||||
if numBits%8 != 0 {
|
||||
newNumBytes++
|
||||
}
|
||||
|
||||
if len(b.bits) >= newNumBytes {
|
||||
return
|
||||
}
|
||||
|
||||
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...)
|
||||
}
|
||||
|
||||
// Append bits copied from |other|.
|
||||
//
|
||||
// The new length is b.Len() + other.Len().
|
||||
func (b *Bitset) Append(other *Bitset) {
|
||||
b.ensureCapacity(other.numBits)
|
||||
|
||||
for i := 0; i < other.numBits; i++ {
|
||||
if other.At(i) {
|
||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||
}
|
||||
b.numBits++
|
||||
}
|
||||
}
|
||||
|
||||
// AppendBools appends bits to the Bitset.
|
||||
func (b *Bitset) AppendBools(bits ...bool) {
|
||||
b.ensureCapacity(len(bits))
|
||||
|
||||
for _, v := range bits {
|
||||
if v {
|
||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
||||
}
|
||||
b.numBits++
|
||||
}
|
||||
}
|
||||
|
||||
// AppendNumBools appends num bits of value value.
|
||||
func (b *Bitset) AppendNumBools(num int, value bool) {
|
||||
for i := 0; i < num; i++ {
|
||||
b.AppendBools(value)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a human readable representation of the Bitset's contents.
|
||||
func (b *Bitset) String() string {
|
||||
var bitString string
|
||||
for i := 0; i < b.numBits; i++ {
|
||||
if (i % 8) == 0 {
|
||||
bitString += " "
|
||||
}
|
||||
|
||||
if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 {
|
||||
bitString += "1"
|
||||
} else {
|
||||
bitString += "0"
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString)
|
||||
}
|
||||
|
||||
// Len returns the length of the Bitset in bits.
|
||||
func (b *Bitset) Len() int {
|
||||
return b.numBits
|
||||
}
|
||||
|
||||
// Bits returns the contents of the Bitset.
|
||||
func (b *Bitset) Bits() []bool {
|
||||
result := make([]bool, b.numBits)
|
||||
|
||||
var i int
|
||||
for i = 0; i < b.numBits; i++ {
|
||||
result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// At returns the value of the bit at |index|.
|
||||
func (b *Bitset) At(index int) bool {
|
||||
if index >= b.numBits {
|
||||
log.Panicf("Index %d out of range", index)
|
||||
}
|
||||
|
||||
return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0
|
||||
}
|
||||
|
||||
// Equals returns true if the Bitset equals other.
|
||||
func (b *Bitset) Equals(other *Bitset) bool {
|
||||
if b.numBits != other.numBits {
|
||||
return false
|
||||
}
|
||||
|
||||
if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 8 * (b.numBits / 8); i < b.numBits; i++ {
|
||||
a := (b.bits[i/8] & (0x80 >> byte(i%8)))
|
||||
b := (other.bits[i/8] & (0x80 >> byte(i%8)))
|
||||
|
||||
if a != b {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ByteAt returns a byte consisting of upto 8 bits starting at index.
|
||||
func (b *Bitset) ByteAt(index int) byte {
|
||||
if index < 0 || index >= b.numBits {
|
||||
log.Panicf("Index %d out of range", index)
|
||||
}
|
||||
|
||||
var result byte
|
||||
|
||||
for i := index; i < index+8 && i < b.numBits; i++ {
|
||||
result <<= 1
|
||||
if b.At(i) {
|
||||
result |= 1
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
@@ -1,486 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
bitset "github.com/skip2/go-qrcode/bitset"
|
||||
)
|
||||
|
||||
// Data encoding.
|
||||
//
|
||||
// The main data portion of a QR Code consists of one or more segments of data.
|
||||
// A segment consists of:
|
||||
//
|
||||
// - The segment Data Mode: numeric, alphanumeric, or byte.
|
||||
// - The length of segment in bits.
|
||||
// - Encoded data.
|
||||
//
|
||||
// For example, the string "123ZZ#!#!" may be represented as:
|
||||
//
|
||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]
|
||||
//
|
||||
// Multiple data modes exist to minimise the size of encoded data. For example,
|
||||
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be
|
||||
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits.
|
||||
//
|
||||
// Some data can be represented in multiple modes. Numeric data can be
|
||||
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be
|
||||
// represented in alphanumeric and byte mode.
|
||||
//
|
||||
// Starting a new segment (to use a different Data Mode) has a cost, the bits to
|
||||
// state the new segment Data Mode and length. To minimise each QR Code's symbol
|
||||
// size, an optimisation routine coalesces segment types where possible, to
|
||||
// reduce the encoded data length.
|
||||
//
|
||||
// There are several other data modes available (e.g. Kanji mode) which are not
|
||||
// implemented here.
|
||||
|
||||
// A segment encoding mode.
|
||||
type dataMode uint8
|
||||
|
||||
const (
|
||||
// Each dataMode is a subset of the subsequent dataMode:
|
||||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
||||
//
|
||||
// This ordering is important for determining which data modes a character can
|
||||
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and
|
||||
// dataModeByte.
|
||||
dataModeNone dataMode = 1 << iota
|
||||
dataModeNumeric
|
||||
dataModeAlphanumeric
|
||||
dataModeByte
|
||||
)
|
||||
|
||||
// dataModeString returns d as a short printable string.
|
||||
func dataModeString(d dataMode) string {
|
||||
switch d {
|
||||
case dataModeNone:
|
||||
return "none"
|
||||
case dataModeNumeric:
|
||||
return "numeric"
|
||||
case dataModeAlphanumeric:
|
||||
return "alphanumeric"
|
||||
case dataModeByte:
|
||||
return "byte"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type dataEncoderType uint8
|
||||
|
||||
const (
|
||||
dataEncoderType1To9 dataEncoderType = iota
|
||||
dataEncoderType10To26
|
||||
dataEncoderType27To40
|
||||
)
|
||||
|
||||
// segment is a single segment of data.
|
||||
type segment struct {
|
||||
// Data Mode (e.g. numeric).
|
||||
dataMode dataMode
|
||||
|
||||
// segment data (e.g. "abc").
|
||||
data []byte
|
||||
}
|
||||
|
||||
// A dataEncoder encodes data for a particular QR Code version.
|
||||
type dataEncoder struct {
|
||||
// Minimum & maximum versions supported.
|
||||
minVersion int
|
||||
maxVersion int
|
||||
|
||||
// Mode indicator bit sequences.
|
||||
numericModeIndicator *bitset.Bitset
|
||||
alphanumericModeIndicator *bitset.Bitset
|
||||
byteModeIndicator *bitset.Bitset
|
||||
|
||||
// Character count lengths.
|
||||
numNumericCharCountBits int
|
||||
numAlphanumericCharCountBits int
|
||||
numByteCharCountBits int
|
||||
|
||||
// The raw input data.
|
||||
data []byte
|
||||
|
||||
// The data classified into unoptimised segments.
|
||||
actual []segment
|
||||
|
||||
// The data classified into optimised segments.
|
||||
optimised []segment
|
||||
}
|
||||
|
||||
// newDataEncoder constructs a dataEncoder.
|
||||
func newDataEncoder(t dataEncoderType) *dataEncoder {
|
||||
d := &dataEncoder{}
|
||||
|
||||
switch t {
|
||||
case dataEncoderType1To9:
|
||||
d = &dataEncoder{
|
||||
minVersion: 1,
|
||||
maxVersion: 9,
|
||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||
numNumericCharCountBits: 10,
|
||||
numAlphanumericCharCountBits: 9,
|
||||
numByteCharCountBits: 8,
|
||||
}
|
||||
case dataEncoderType10To26:
|
||||
d = &dataEncoder{
|
||||
minVersion: 10,
|
||||
maxVersion: 26,
|
||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||
numNumericCharCountBits: 12,
|
||||
numAlphanumericCharCountBits: 11,
|
||||
numByteCharCountBits: 16,
|
||||
}
|
||||
case dataEncoderType27To40:
|
||||
d = &dataEncoder{
|
||||
minVersion: 27,
|
||||
maxVersion: 40,
|
||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
||||
numNumericCharCountBits: 14,
|
||||
numAlphanumericCharCountBits: 13,
|
||||
numByteCharCountBits: 16,
|
||||
}
|
||||
default:
|
||||
log.Panic("Unknown dataEncoderType")
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// encode data as one or more segments and return the encoded data.
|
||||
//
|
||||
// The returned data does not include the terminator bit sequence.
|
||||
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
|
||||
d.data = data
|
||||
d.actual = nil
|
||||
d.optimised = nil
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("no data to encode")
|
||||
}
|
||||
|
||||
// Classify data into unoptimised segments.
|
||||
highestRequiredMode := d.classifyDataModes()
|
||||
|
||||
// Optimise segments.
|
||||
err := d.optimiseDataModes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if a single byte encoded segment would be more efficient.
|
||||
optimizedLength := 0
|
||||
for _, s := range d.optimised {
|
||||
length, err := d.encodedLength(s.dataMode, len(s.data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optimizedLength += length
|
||||
}
|
||||
|
||||
singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if singleByteSegmentLength <= optimizedLength {
|
||||
d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}}
|
||||
}
|
||||
|
||||
// Encode data.
|
||||
encoded := bitset.New()
|
||||
for _, s := range d.optimised {
|
||||
d.encodeDataRaw(s.data, s.dataMode, encoded)
|
||||
}
|
||||
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
// classifyDataModes classifies the raw data into unoptimised segments.
|
||||
// e.g. "123ZZ#!#!" =>
|
||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
|
||||
//
|
||||
// Returns the highest data mode needed to encode the data. e.g. for a mixed
|
||||
// numeric/alphanumeric input, the highest is alphanumeric.
|
||||
//
|
||||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
||||
func (d *dataEncoder) classifyDataModes() dataMode {
|
||||
var start int
|
||||
mode := dataModeNone
|
||||
highestRequiredMode := mode
|
||||
|
||||
for i, v := range d.data {
|
||||
newMode := dataModeNone
|
||||
switch {
|
||||
case v >= 0x30 && v <= 0x39:
|
||||
newMode = dataModeNumeric
|
||||
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v ==
|
||||
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a):
|
||||
newMode = dataModeAlphanumeric
|
||||
default:
|
||||
newMode = dataModeByte
|
||||
}
|
||||
|
||||
if newMode != mode {
|
||||
if i > 0 {
|
||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]})
|
||||
|
||||
start = i
|
||||
}
|
||||
|
||||
mode = newMode
|
||||
}
|
||||
|
||||
if newMode > highestRequiredMode {
|
||||
highestRequiredMode = newMode
|
||||
}
|
||||
}
|
||||
|
||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
|
||||
|
||||
return highestRequiredMode
|
||||
}
|
||||
|
||||
// optimiseDataModes optimises the list of segments to reduce the overall output
|
||||
// encoded data length.
|
||||
//
|
||||
// The algorithm coalesces adjacent segments. segments are only coalesced when
|
||||
// the Data Modes are compatible, and when the coalesced segment has a shorter
|
||||
// encoded length than separate segments.
|
||||
//
|
||||
// Multiple segments may be coalesced. For example a string of alternating
|
||||
// alphanumeric/numeric segments ANANANANA can be optimised to just A.
|
||||
func (d *dataEncoder) optimiseDataModes() error {
|
||||
for i := 0; i < len(d.actual); {
|
||||
mode := d.actual[i].dataMode
|
||||
numChars := len(d.actual[i].data)
|
||||
|
||||
j := i + 1
|
||||
for j < len(d.actual) {
|
||||
nextNumChars := len(d.actual[j].data)
|
||||
nextMode := d.actual[j].dataMode
|
||||
|
||||
if nextMode > mode {
|
||||
break
|
||||
}
|
||||
|
||||
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seperateLength1, err := d.encodedLength(mode, numChars)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seperateLength2, err := d.encodedLength(nextMode, nextNumChars)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if coalescedLength < seperateLength1+seperateLength2 {
|
||||
j++
|
||||
numChars += nextNumChars
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
optimised := segment{dataMode: mode,
|
||||
data: make([]byte, 0, numChars)}
|
||||
|
||||
for k := i; k < j; k++ {
|
||||
optimised.data = append(optimised.data, d.actual[k].data...)
|
||||
}
|
||||
|
||||
d.optimised = append(d.optimised, optimised)
|
||||
|
||||
i = j
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeDataRaw encodes data in dataMode. The encoded data is appended to
|
||||
// encoded.
|
||||
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) {
|
||||
modeIndicator := d.modeIndicator(dataMode)
|
||||
charCountBits := d.charCountBits(dataMode)
|
||||
|
||||
// Append mode indicator.
|
||||
encoded.Append(modeIndicator)
|
||||
|
||||
// Append character count.
|
||||
encoded.AppendUint32(uint32(len(data)), charCountBits)
|
||||
|
||||
// Append data.
|
||||
switch dataMode {
|
||||
case dataModeNumeric:
|
||||
for i := 0; i < len(data); i += 3 {
|
||||
charsRemaining := len(data) - i
|
||||
|
||||
var value uint32
|
||||
bitsUsed := 1
|
||||
|
||||
for j := 0; j < charsRemaining && j < 3; j++ {
|
||||
value *= 10
|
||||
value += uint32(data[i+j] - 0x30)
|
||||
bitsUsed += 3
|
||||
}
|
||||
encoded.AppendUint32(value, bitsUsed)
|
||||
}
|
||||
case dataModeAlphanumeric:
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
charsRemaining := len(data) - i
|
||||
|
||||
var value uint32
|
||||
for j := 0; j < charsRemaining && j < 2; j++ {
|
||||
value *= 45
|
||||
value += encodeAlphanumericCharacter(data[i+j])
|
||||
}
|
||||
|
||||
bitsUsed := 6
|
||||
if charsRemaining > 1 {
|
||||
bitsUsed = 11
|
||||
}
|
||||
|
||||
encoded.AppendUint32(value, bitsUsed)
|
||||
}
|
||||
case dataModeByte:
|
||||
for _, b := range data {
|
||||
encoded.AppendByte(b, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// modeIndicator returns the segment header bits for a segment of type dataMode.
|
||||
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset {
|
||||
switch dataMode {
|
||||
case dataModeNumeric:
|
||||
return d.numericModeIndicator
|
||||
case dataModeAlphanumeric:
|
||||
return d.alphanumericModeIndicator
|
||||
case dataModeByte:
|
||||
return d.byteModeIndicator
|
||||
default:
|
||||
log.Panic("Unknown data mode")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// charCountBits returns the number of bits used to encode the length of a data
|
||||
// segment of type dataMode.
|
||||
func (d *dataEncoder) charCountBits(dataMode dataMode) int {
|
||||
switch dataMode {
|
||||
case dataModeNumeric:
|
||||
return d.numNumericCharCountBits
|
||||
case dataModeAlphanumeric:
|
||||
return d.numAlphanumericCharCountBits
|
||||
case dataModeByte:
|
||||
return d.numByteCharCountBits
|
||||
default:
|
||||
log.Panic("Unknown data mode")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// encodedLength returns the number of bits required to encode n symbols in
|
||||
// dataMode.
|
||||
//
|
||||
// The number of bits required is affected by:
|
||||
// - QR code type - Mode Indicator length.
|
||||
// - Data mode - number of bits used to represent data length.
|
||||
// - Data mode - how the data is encoded.
|
||||
// - Number of symbols encoded.
|
||||
//
|
||||
// An error is returned if the mode is not supported, or the length requested is
|
||||
// too long to be represented.
|
||||
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) {
|
||||
modeIndicator := d.modeIndicator(dataMode)
|
||||
charCountBits := d.charCountBits(dataMode)
|
||||
|
||||
if modeIndicator == nil {
|
||||
return 0, errors.New("mode not supported")
|
||||
}
|
||||
|
||||
maxLength := (1 << uint8(charCountBits)) - 1
|
||||
|
||||
if n > maxLength {
|
||||
return 0, errors.New("length too long to be represented")
|
||||
}
|
||||
|
||||
length := modeIndicator.Len() + charCountBits
|
||||
|
||||
switch dataMode {
|
||||
case dataModeNumeric:
|
||||
length += 10 * (n / 3)
|
||||
|
||||
if n%3 != 0 {
|
||||
length += 1 + 3*(n%3)
|
||||
}
|
||||
case dataModeAlphanumeric:
|
||||
length += 11 * (n / 2)
|
||||
length += 6 * (n % 2)
|
||||
case dataModeByte:
|
||||
length += 8 * n
|
||||
}
|
||||
|
||||
return length, nil
|
||||
}
|
||||
|
||||
// encodeAlphanumericChar returns the QR Code encoded value of v.
|
||||
//
|
||||
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or
|
||||
// :. The characters are mapped to values in the range 0-44 respectively.
|
||||
func encodeAlphanumericCharacter(v byte) uint32 {
|
||||
c := uint32(v)
|
||||
|
||||
switch {
|
||||
case c >= '0' && c <= '9':
|
||||
// 0-9 encoded as 0-9.
|
||||
return c - '0'
|
||||
case c >= 'A' && c <= 'Z':
|
||||
// A-Z encoded as 10-35.
|
||||
return c - 'A' + 10
|
||||
case c == ' ':
|
||||
return 36
|
||||
case c == '$':
|
||||
return 37
|
||||
case c == '%':
|
||||
return 38
|
||||
case c == '*':
|
||||
return 39
|
||||
case c == '+':
|
||||
return 40
|
||||
case c == '-':
|
||||
return 41
|
||||
case c == '.':
|
||||
return 42
|
||||
case c == '/':
|
||||
return 43
|
||||
case c == ':':
|
||||
return 44
|
||||
default:
|
||||
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
@@ -1,608 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
/*
|
||||
Package qrcode implements a QR Code encoder.
|
||||
|
||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
|
||||
encoded.
|
||||
|
||||
A QR Code contains error recovery information to aid reading damaged or
|
||||
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
|
||||
High, Highest}. QR Codes with a higher recovery level are more robust to damage,
|
||||
at the cost of being physically larger.
|
||||
|
||||
Three functions cover most use cases:
|
||||
|
||||
- Create a PNG image:
|
||||
|
||||
var png []byte
|
||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
||||
|
||||
- Create a PNG image and write to a file:
|
||||
|
||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
||||
|
||||
- Create a PNG image with custom colors and write to file:
|
||||
|
||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
||||
|
||||
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.
|
||||
|
||||
To generate a variable sized image instead, specify a negative size (in place of
|
||||
the 256 above), such as -4 or -5. Larger negative numbers create larger images:
|
||||
A size of -5 sets each module (QR Code "pixel") to be 5px wide/high.
|
||||
|
||||
- Create a PNG image (variable size, with minimum white padding) and write to a file:
|
||||
|
||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png")
|
||||
|
||||
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.
|
||||
|
||||
This package implements a subset of QR Code 2005, as defined in ISO/IEC
|
||||
18004:2006.
|
||||
*/
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
bitset "github.com/skip2/go-qrcode/bitset"
|
||||
reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
|
||||
)
|
||||
|
||||
// Encode a QR Code and return a raw PNG image.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently returned. Negative values for size cause a
|
||||
// variable sized image to be returned: See the documentation for Image().
|
||||
//
|
||||
// To serve over HTTP, remember to send a Content-Type: image/png header.
|
||||
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
|
||||
var q *QRCode
|
||||
|
||||
q, err := New(content, level)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return q.PNG(size)
|
||||
}
|
||||
|
||||
// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently written. Negative values for size cause a variable
|
||||
// sized image to be written: See the documentation for Image().
|
||||
func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
|
||||
var q *QRCode
|
||||
|
||||
q, err := New(content, level)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.WriteFile(size, filename)
|
||||
}
|
||||
|
||||
// WriteColorFile encodes, then writes a QR Code to the given filename in PNG format.
|
||||
// With WriteColorFile you can also specify the colors you want to use.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently written. Negative values for size cause a variable
|
||||
// sized image to be written: See the documentation for Image().
|
||||
func WriteColorFile(content string, level RecoveryLevel, size int, background,
|
||||
foreground color.Color, filename string) error {
|
||||
|
||||
var q *QRCode
|
||||
|
||||
q, err := New(content, level)
|
||||
|
||||
q.BackgroundColor = background
|
||||
q.ForegroundColor = foreground
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.WriteFile(size, filename)
|
||||
}
|
||||
|
||||
// A QRCode represents a valid encoded QRCode.
|
||||
type QRCode struct {
|
||||
// Original content encoded.
|
||||
Content string
|
||||
|
||||
// QR Code type.
|
||||
Level RecoveryLevel
|
||||
VersionNumber int
|
||||
|
||||
// User settable drawing options.
|
||||
ForegroundColor color.Color
|
||||
BackgroundColor color.Color
|
||||
|
||||
// Disable the QR Code border.
|
||||
DisableBorder bool
|
||||
|
||||
encoder *dataEncoder
|
||||
version qrCodeVersion
|
||||
|
||||
data *bitset.Bitset
|
||||
symbol *symbol
|
||||
mask int
|
||||
}
|
||||
|
||||
// New constructs a QRCode.
|
||||
//
|
||||
// var q *qrcode.QRCode
|
||||
// q, err := qrcode.New("my content", qrcode.Medium)
|
||||
//
|
||||
// An error occurs if the content is too long.
|
||||
func New(content string, level RecoveryLevel) (*QRCode, error) {
|
||||
encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
|
||||
dataEncoderType27To40}
|
||||
|
||||
var encoder *dataEncoder
|
||||
var encoded *bitset.Bitset
|
||||
var chosenVersion *qrCodeVersion
|
||||
var err error
|
||||
|
||||
for _, t := range encoders {
|
||||
encoder = newDataEncoder(t)
|
||||
encoded, err = encoder.encode([]byte(content))
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())
|
||||
|
||||
if chosenVersion != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if chosenVersion == nil {
|
||||
return nil, errors.New("content too long to encode")
|
||||
}
|
||||
|
||||
q := &QRCode{
|
||||
Content: content,
|
||||
|
||||
Level: level,
|
||||
VersionNumber: chosenVersion.version,
|
||||
|
||||
ForegroundColor: color.Black,
|
||||
BackgroundColor: color.White,
|
||||
|
||||
encoder: encoder,
|
||||
data: encoded,
|
||||
version: *chosenVersion,
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
// NewWithForcedVersion constructs a QRCode of a specific version.
|
||||
//
|
||||
// var q *qrcode.QRCode
|
||||
// q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium)
|
||||
//
|
||||
// An error occurs in case of invalid version.
|
||||
func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
|
||||
var encoder *dataEncoder
|
||||
|
||||
switch {
|
||||
case version >= 1 && version <= 9:
|
||||
encoder = newDataEncoder(dataEncoderType1To9)
|
||||
case version >= 10 && version <= 26:
|
||||
encoder = newDataEncoder(dataEncoderType10To26)
|
||||
case version >= 27 && version <= 40:
|
||||
encoder = newDataEncoder(dataEncoderType27To40)
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid version %d (expected 1-40 inclusive)", version)
|
||||
}
|
||||
|
||||
var encoded *bitset.Bitset
|
||||
encoded, err := encoder.encode([]byte(content))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chosenVersion := getQRCodeVersion(level, version)
|
||||
|
||||
if chosenVersion == nil {
|
||||
return nil, errors.New("cannot find QR Code version")
|
||||
}
|
||||
|
||||
if encoded.Len() > chosenVersion.numDataBits() {
|
||||
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)",
|
||||
version,
|
||||
encoded.Len(),
|
||||
chosenVersion.numDataBits())
|
||||
}
|
||||
|
||||
q := &QRCode{
|
||||
Content: content,
|
||||
|
||||
Level: level,
|
||||
VersionNumber: chosenVersion.version,
|
||||
|
||||
ForegroundColor: color.Black,
|
||||
BackgroundColor: color.White,
|
||||
|
||||
encoder: encoder,
|
||||
data: encoded,
|
||||
version: *chosenVersion,
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
|
||||
//
|
||||
// bitmap[y][x] is true if the pixel at (x, y) is set.
|
||||
//
|
||||
// The bitmap includes the required "quiet zone" around the QR Code to aid
|
||||
// decoding.
|
||||
func (q *QRCode) Bitmap() [][]bool {
|
||||
// Build QR code.
|
||||
q.encode()
|
||||
|
||||
return q.symbol.bitmap()
|
||||
}
|
||||
|
||||
// Image returns the QR Code as an image.Image.
|
||||
//
|
||||
// A positive size sets a fixed image width and height (e.g. 256 yields an
|
||||
// 256x256px image).
|
||||
//
|
||||
// Depending on the amount of data encoded, fixed size images can have different
|
||||
// amounts of padding (white space around the QR Code). As an alternative, a
|
||||
// variable sized image can be generated instead:
|
||||
//
|
||||
// A negative size causes a variable sized image to be returned. The image
|
||||
// returned is the minimum size required for the QR Code. Choose a larger
|
||||
// negative number to increase the scale of the image. e.g. a size of -5 causes
|
||||
// each module (QR Code "pixel") to be 5px in size.
|
||||
func (q *QRCode) Image(size int) image.Image {
|
||||
// Build QR code.
|
||||
q.encode()
|
||||
|
||||
// Minimum pixels (both width and height) required.
|
||||
realSize := q.symbol.size
|
||||
|
||||
// Variable size support.
|
||||
if size < 0 {
|
||||
size = size * -1 * realSize
|
||||
}
|
||||
|
||||
// Actual pixels available to draw the symbol. Automatically increase the
|
||||
// image size if it's not large enough.
|
||||
if size < realSize {
|
||||
size = realSize
|
||||
}
|
||||
|
||||
// Output image.
|
||||
rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}
|
||||
|
||||
// Saves a few bytes to have them in this order
|
||||
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor})
|
||||
img := image.NewPaletted(rect, p)
|
||||
fgClr := uint8(img.Palette.Index(q.ForegroundColor))
|
||||
|
||||
// QR code bitmap.
|
||||
bitmap := q.symbol.bitmap()
|
||||
|
||||
// Map each image pixel to the nearest QR code module.
|
||||
modulesPerPixel := float64(realSize) / float64(size)
|
||||
for y := 0; y < size; y++ {
|
||||
y2 := int(float64(y) * modulesPerPixel)
|
||||
for x := 0; x < size; x++ {
|
||||
x2 := int(float64(x) * modulesPerPixel)
|
||||
|
||||
v := bitmap[y2][x2]
|
||||
|
||||
if v {
|
||||
pos := img.PixOffset(x, y)
|
||||
img.Pix[pos] = fgClr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
// PNG returns the QR Code as a PNG image.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently returned. Negative values for size cause a
|
||||
// variable sized image to be returned: See the documentation for Image().
|
||||
func (q *QRCode) PNG(size int) ([]byte, error) {
|
||||
img := q.Image(size)
|
||||
|
||||
encoder := png.Encoder{CompressionLevel: png.BestCompression}
|
||||
|
||||
var b bytes.Buffer
|
||||
err := encoder.Encode(&b, img)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// Write writes the QR Code as a PNG image to io.Writer.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently written. Negative values for size cause a
|
||||
// variable sized image to be written: See the documentation for Image().
|
||||
func (q *QRCode) Write(size int, out io.Writer) error {
|
||||
var png []byte
|
||||
|
||||
png, err := q.PNG(size)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = out.Write(png)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteFile writes the QR Code as a PNG image to the specified file.
|
||||
//
|
||||
// size is both the image width and height in pixels. If size is too small then
|
||||
// a larger image is silently written. Negative values for size cause a
|
||||
// variable sized image to be written: See the documentation for Image().
|
||||
func (q *QRCode) WriteFile(size int, filename string) error {
|
||||
var png []byte
|
||||
|
||||
png, err := q.PNG(size)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, png, os.FileMode(0644))
|
||||
}
|
||||
|
||||
// encode completes the steps required to encode the QR Code. These include
|
||||
// adding the terminator bits and padding, splitting the data into blocks and
|
||||
// applying the error correction, and selecting the best data mask.
|
||||
func (q *QRCode) encode() {
|
||||
numTerminatorBits := q.version.numTerminatorBitsRequired(q.data.Len())
|
||||
|
||||
q.addTerminatorBits(numTerminatorBits)
|
||||
q.addPadding()
|
||||
|
||||
encoded := q.encodeBlocks()
|
||||
|
||||
const numMasks int = 8
|
||||
penalty := 0
|
||||
|
||||
for mask := 0; mask < numMasks; mask++ {
|
||||
var s *symbol
|
||||
var err error
|
||||
|
||||
s, err = buildRegularSymbol(q.version, mask, encoded, !q.DisableBorder)
|
||||
|
||||
if err != nil {
|
||||
log.Panic(err.Error())
|
||||
}
|
||||
|
||||
numEmptyModules := s.numEmptyModules()
|
||||
if numEmptyModules != 0 {
|
||||
log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
|
||||
numEmptyModules, q.VersionNumber)
|
||||
}
|
||||
|
||||
p := s.penaltyScore()
|
||||
|
||||
//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())
|
||||
|
||||
if q.symbol == nil || p < penalty {
|
||||
q.symbol = s
|
||||
q.mask = mask
|
||||
penalty = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addTerminatorBits adds final terminator bits to the encoded data.
|
||||
//
|
||||
// The number of terminator bits required is determined when the QR Code version
|
||||
// is chosen (which itself depends on the length of the data encoded). The
|
||||
// terminator bits are thus added after the QR Code version
|
||||
// is chosen, rather than at the data encoding stage.
|
||||
func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
|
||||
q.data.AppendNumBools(numTerminatorBits, false)
|
||||
}
|
||||
|
||||
// encodeBlocks takes the completed (terminated & padded) encoded data, splits
|
||||
// the data into blocks (as specified by the QR Code version), applies error
|
||||
// correction to each block, then interleaves the blocks together.
|
||||
//
|
||||
// The QR Code's final data sequence is returned.
|
||||
func (q *QRCode) encodeBlocks() *bitset.Bitset {
|
||||
// Split into blocks.
|
||||
type dataBlock struct {
|
||||
data *bitset.Bitset
|
||||
ecStartOffset int
|
||||
}
|
||||
|
||||
block := make([]dataBlock, q.version.numBlocks())
|
||||
|
||||
start := 0
|
||||
end := 0
|
||||
blockID := 0
|
||||
|
||||
for _, b := range q.version.block {
|
||||
for j := 0; j < b.numBlocks; j++ {
|
||||
start = end
|
||||
end = start + b.numDataCodewords*8
|
||||
|
||||
// Apply error correction to each block.
|
||||
numErrorCodewords := b.numCodewords - b.numDataCodewords
|
||||
block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
|
||||
block[blockID].ecStartOffset = end - start
|
||||
|
||||
blockID++
|
||||
}
|
||||
}
|
||||
|
||||
// Interleave the blocks.
|
||||
|
||||
result := bitset.New()
|
||||
|
||||
// Combine data blocks.
|
||||
working := true
|
||||
for i := 0; working; i += 8 {
|
||||
working = false
|
||||
|
||||
for j, b := range block {
|
||||
if i >= block[j].ecStartOffset {
|
||||
continue
|
||||
}
|
||||
|
||||
result.Append(b.data.Substr(i, i+8))
|
||||
|
||||
working = true
|
||||
}
|
||||
}
|
||||
|
||||
// Combine error correction blocks.
|
||||
working = true
|
||||
for i := 0; working; i += 8 {
|
||||
working = false
|
||||
|
||||
for j, b := range block {
|
||||
offset := i + block[j].ecStartOffset
|
||||
if offset >= block[j].data.Len() {
|
||||
continue
|
||||
}
|
||||
|
||||
result.Append(b.data.Substr(offset, offset+8))
|
||||
|
||||
working = true
|
||||
}
|
||||
}
|
||||
|
||||
// Append remainder bits.
|
||||
result.AppendNumBools(q.version.numRemainderBits, false)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// max returns the maximum of a and b.
|
||||
func max(a int, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// addPadding pads the encoded data upto the full length required.
|
||||
func (q *QRCode) addPadding() {
|
||||
numDataBits := q.version.numDataBits()
|
||||
|
||||
if q.data.Len() == numDataBits {
|
||||
return
|
||||
}
|
||||
|
||||
// Pad to the nearest codeword boundary.
|
||||
q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)
|
||||
|
||||
// Pad codewords 0b11101100 and 0b00010001.
|
||||
padding := [2]*bitset.Bitset{
|
||||
bitset.New(true, true, true, false, true, true, false, false),
|
||||
bitset.New(false, false, false, true, false, false, false, true),
|
||||
}
|
||||
|
||||
// Insert pad codewords alternately.
|
||||
i := 0
|
||||
for numDataBits-q.data.Len() >= 8 {
|
||||
q.data.Append(padding[i])
|
||||
|
||||
i = 1 - i // Alternate between 0 and 1.
|
||||
}
|
||||
|
||||
if q.data.Len() != numDataBits {
|
||||
log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
|
||||
}
|
||||
}
|
||||
|
||||
// ToString produces a multi-line string that forms a QR-code image.
|
||||
func (q *QRCode) ToString(inverseColor bool) string {
|
||||
bits := q.Bitmap()
|
||||
var buf bytes.Buffer
|
||||
for y := range bits {
|
||||
for x := range bits[y] {
|
||||
if bits[y][x] != inverseColor {
|
||||
buf.WriteString(" ")
|
||||
} else {
|
||||
buf.WriteString("██")
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ToSmallString produces a multi-line string that forms a QR-code image, a
|
||||
// factor two smaller in x and y then ToString.
|
||||
func (q *QRCode) ToSmallString(inverseColor bool) string {
|
||||
bits := q.Bitmap()
|
||||
var buf bytes.Buffer
|
||||
// if there is an odd number of rows, the last one needs special treatment
|
||||
for y := 0; y < len(bits)-1; y += 2 {
|
||||
for x := range bits[y] {
|
||||
if bits[y][x] == bits[y+1][x] {
|
||||
if bits[y][x] != inverseColor {
|
||||
buf.WriteString(" ")
|
||||
} else {
|
||||
buf.WriteString("█")
|
||||
}
|
||||
} else {
|
||||
if bits[y][x] != inverseColor {
|
||||
buf.WriteString("▄")
|
||||
} else {
|
||||
buf.WriteString("▀")
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
// special treatment for the last row if odd
|
||||
if len(bits)%2 == 1 {
|
||||
y := len(bits) - 1
|
||||
for x := range bits[y] {
|
||||
if bits[y][x] != inverseColor {
|
||||
buf.WriteString(" ")
|
||||
} else {
|
||||
buf.WriteString("▀")
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
@@ -1,387 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
package reedsolomon
|
||||
|
||||
// Addition, subtraction, multiplication, and division in GF(2^8).
|
||||
// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1.
|
||||
|
||||
// http://en.wikipedia.org/wiki/Finite_field_arithmetic
|
||||
|
||||
import "log"
|
||||
|
||||
const (
|
||||
gfZero = gfElement(0)
|
||||
gfOne = gfElement(1)
|
||||
)
|
||||
|
||||
var (
|
||||
gfExpTable = [256]gfElement{
|
||||
/* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58,
|
||||
/* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90,
|
||||
/* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48,
|
||||
/* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53,
|
||||
/* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140,
|
||||
/* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210,
|
||||
/* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47,
|
||||
/* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
|
||||
/* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225,
|
||||
/* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134,
|
||||
/* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189,
|
||||
/* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147,
|
||||
/* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23,
|
||||
/* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66,
|
||||
/* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164,
|
||||
/* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
|
||||
/* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229,
|
||||
/* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75,
|
||||
/* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
|
||||
/* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14,
|
||||
/* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
|
||||
/* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86,
|
||||
/* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122,
|
||||
/* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
|
||||
/* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54,
|
||||
/* 250 - 255 */ 108, 216, 173, 71, 142, 1}
|
||||
|
||||
gfLogTable = [256]int{
|
||||
/* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223,
|
||||
/* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14,
|
||||
/* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8,
|
||||
/* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33,
|
||||
/* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181,
|
||||
/* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120,
|
||||
/* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221,
|
||||
/* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
|
||||
/* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210,
|
||||
/* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163,
|
||||
/* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133,
|
||||
/* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43,
|
||||
/* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112,
|
||||
/* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237,
|
||||
/* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184,
|
||||
/* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
|
||||
/* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135,
|
||||
/* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171,
|
||||
/* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109,
|
||||
/* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118,
|
||||
/* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161,
|
||||
/* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177,
|
||||
/* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169,
|
||||
/* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
|
||||
/* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214,
|
||||
/* 250 - 255 */ 244, 234, 168, 80, 88, 175}
|
||||
)
|
||||
|
||||
// gfElement is an element in GF(2^8).
|
||||
type gfElement uint8
|
||||
|
||||
// newGFElement creates and returns a new gfElement.
|
||||
func newGFElement(data byte) gfElement {
|
||||
return gfElement(data)
|
||||
}
|
||||
|
||||
// gfAdd returns a + b.
|
||||
func gfAdd(a, b gfElement) gfElement {
|
||||
return a ^ b
|
||||
}
|
||||
|
||||
// gfSub returns a - b.
|
||||
//
|
||||
// Note addition is equivalent to subtraction in GF(2).
|
||||
func gfSub(a, b gfElement) gfElement {
|
||||
return a ^ b
|
||||
}
|
||||
|
||||
// gfMultiply returns a * b.
|
||||
func gfMultiply(a, b gfElement) gfElement {
|
||||
if a == gfZero || b == gfZero {
|
||||
return gfZero
|
||||
}
|
||||
|
||||
return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255]
|
||||
}
|
||||
|
||||
// gfDivide returns a / b.
|
||||
//
|
||||
// Divide by zero results in a panic.
|
||||
func gfDivide(a, b gfElement) gfElement {
|
||||
if a == gfZero {
|
||||
return gfZero
|
||||
} else if b == gfZero {
|
||||
log.Panicln("Divide by zero")
|
||||
}
|
||||
|
||||
return gfMultiply(a, gfInverse(b))
|
||||
}
|
||||
|
||||
// gfInverse returns the multiplicative inverse of a, a^-1.
|
||||
//
|
||||
// a * a^-1 = 1
|
||||
func gfInverse(a gfElement) gfElement {
|
||||
if a == gfZero {
|
||||
log.Panicln("No multiplicative inverse of 0")
|
||||
}
|
||||
|
||||
return gfExpTable[255-gfLogTable[a]]
|
||||
}
|
||||
|
||||
// a^i | bits | polynomial | decimal
|
||||
// --------------------------------------------------------------------------
|
||||
// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0
|
||||
// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
||||
// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2
|
||||
// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4
|
||||
// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8
|
||||
// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16
|
||||
// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32
|
||||
// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64
|
||||
// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128
|
||||
// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29
|
||||
// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58
|
||||
// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116
|
||||
// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232
|
||||
// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205
|
||||
// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135
|
||||
// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19
|
||||
// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38
|
||||
// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76
|
||||
// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152
|
||||
// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45
|
||||
// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90
|
||||
// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180
|
||||
// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117
|
||||
// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234
|
||||
// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201
|
||||
// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143
|
||||
// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3
|
||||
// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6
|
||||
// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12
|
||||
// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24
|
||||
// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48
|
||||
// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96
|
||||
// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192
|
||||
// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157
|
||||
// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39
|
||||
// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78
|
||||
// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156
|
||||
// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37
|
||||
// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74
|
||||
// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148
|
||||
// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53
|
||||
// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106
|
||||
// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212
|
||||
// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181
|
||||
// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119
|
||||
// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238
|
||||
// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193
|
||||
// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159
|
||||
// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35
|
||||
// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70
|
||||
// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140
|
||||
// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5
|
||||
// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10
|
||||
// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20
|
||||
// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40
|
||||
// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80
|
||||
// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160
|
||||
// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93
|
||||
// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186
|
||||
// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105
|
||||
// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210
|
||||
// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185
|
||||
// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111
|
||||
// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222
|
||||
// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161
|
||||
// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95
|
||||
// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190
|
||||
// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97
|
||||
// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194
|
||||
// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153
|
||||
// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47
|
||||
// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94
|
||||
// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188
|
||||
// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101
|
||||
// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202
|
||||
// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137
|
||||
// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15
|
||||
// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30
|
||||
// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60
|
||||
// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120
|
||||
// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240
|
||||
// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253
|
||||
// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231
|
||||
// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211
|
||||
// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187
|
||||
// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107
|
||||
// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214
|
||||
// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177
|
||||
// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127
|
||||
// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254
|
||||
// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225
|
||||
// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223
|
||||
// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163
|
||||
// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91
|
||||
// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182
|
||||
// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113
|
||||
// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226
|
||||
// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217
|
||||
// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175
|
||||
// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67
|
||||
// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134
|
||||
// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17
|
||||
// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34
|
||||
// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68
|
||||
// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136
|
||||
// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13
|
||||
// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26
|
||||
// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52
|
||||
// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104
|
||||
// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208
|
||||
// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189
|
||||
// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103
|
||||
// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206
|
||||
// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129
|
||||
// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31
|
||||
// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62
|
||||
// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124
|
||||
// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248
|
||||
// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237
|
||||
// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199
|
||||
// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147
|
||||
// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59
|
||||
// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118
|
||||
// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236
|
||||
// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197
|
||||
// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151
|
||||
// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51
|
||||
// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102
|
||||
// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204
|
||||
// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133
|
||||
// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23
|
||||
// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46
|
||||
// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92
|
||||
// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184
|
||||
// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109
|
||||
// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218
|
||||
// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169
|
||||
// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79
|
||||
// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158
|
||||
// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33
|
||||
// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66
|
||||
// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132
|
||||
// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21
|
||||
// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42
|
||||
// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84
|
||||
// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168
|
||||
// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77
|
||||
// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154
|
||||
// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41
|
||||
// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82
|
||||
// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164
|
||||
// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85
|
||||
// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170
|
||||
// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73
|
||||
// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146
|
||||
// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57
|
||||
// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114
|
||||
// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228
|
||||
// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213
|
||||
// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183
|
||||
// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115
|
||||
// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230
|
||||
// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209
|
||||
// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191
|
||||
// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99
|
||||
// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198
|
||||
// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145
|
||||
// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63
|
||||
// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126
|
||||
// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252
|
||||
// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229
|
||||
// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215
|
||||
// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179
|
||||
// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123
|
||||
// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246
|
||||
// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241
|
||||
// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255
|
||||
// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227
|
||||
// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219
|
||||
// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171
|
||||
// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75
|
||||
// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150
|
||||
// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49
|
||||
// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98
|
||||
// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196
|
||||
// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149
|
||||
// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55
|
||||
// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110
|
||||
// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220
|
||||
// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165
|
||||
// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87
|
||||
// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174
|
||||
// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65
|
||||
// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130
|
||||
// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25
|
||||
// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50
|
||||
// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100
|
||||
// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200
|
||||
// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141
|
||||
// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7
|
||||
// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14
|
||||
// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28
|
||||
// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56
|
||||
// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112
|
||||
// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224
|
||||
// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221
|
||||
// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167
|
||||
// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83
|
||||
// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166
|
||||
// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81
|
||||
// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162
|
||||
// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89
|
||||
// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178
|
||||
// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121
|
||||
// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242
|
||||
// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249
|
||||
// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239
|
||||
// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195
|
||||
// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155
|
||||
// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43
|
||||
// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86
|
||||
// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172
|
||||
// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69
|
||||
// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138
|
||||
// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9
|
||||
// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18
|
||||
// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36
|
||||
// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72
|
||||
// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144
|
||||
// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61
|
||||
// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122
|
||||
// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244
|
||||
// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245
|
||||
// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247
|
||||
// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243
|
||||
// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251
|
||||
// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235
|
||||
// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203
|
||||
// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139
|
||||
// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11
|
||||
// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22
|
||||
// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44
|
||||
// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88
|
||||
// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176
|
||||
// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125
|
||||
// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250
|
||||
// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233
|
||||
// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207
|
||||
// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131
|
||||
// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27
|
||||
// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54
|
||||
// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108
|
||||
// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216
|
||||
// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173
|
||||
// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71
|
||||
// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142
|
||||
// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
||||
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
@@ -1,216 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
bitset "github.com/skip2/go-qrcode/bitset"
|
||||
)
|
||||
|
||||
// gfPoly is a polynomial over GF(2^8).
|
||||
type gfPoly struct {
|
||||
// The ith value is the coefficient of the ith degree of x.
|
||||
// term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ...
|
||||
term []gfElement
|
||||
}
|
||||
|
||||
// newGFPolyFromData returns |data| as a polynomial over GF(2^8).
|
||||
//
|
||||
// Each data byte becomes the coefficient of an x term.
|
||||
//
|
||||
// For an n byte input the polynomial is:
|
||||
// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0).
|
||||
func newGFPolyFromData(data *bitset.Bitset) gfPoly {
|
||||
numTotalBytes := data.Len() / 8
|
||||
if data.Len()%8 != 0 {
|
||||
numTotalBytes++
|
||||
}
|
||||
|
||||
result := gfPoly{term: make([]gfElement, numTotalBytes)}
|
||||
|
||||
i := numTotalBytes - 1
|
||||
for j := 0; j < data.Len(); j += 8 {
|
||||
result.term[i] = gfElement(data.ByteAt(j))
|
||||
i--
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// newGFPolyMonomial returns term*(x^degree).
|
||||
func newGFPolyMonomial(term gfElement, degree int) gfPoly {
|
||||
if term == gfZero {
|
||||
return gfPoly{}
|
||||
}
|
||||
|
||||
result := gfPoly{term: make([]gfElement, degree+1)}
|
||||
result.term[degree] = term
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (e gfPoly) data(numTerms int) []byte {
|
||||
result := make([]byte, numTerms)
|
||||
|
||||
i := numTerms - len(e.term)
|
||||
for j := len(e.term) - 1; j >= 0; j-- {
|
||||
result[i] = byte(e.term[j])
|
||||
i++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// numTerms returns the number of
|
||||
func (e gfPoly) numTerms() int {
|
||||
return len(e.term)
|
||||
}
|
||||
|
||||
// gfPolyMultiply returns a * b.
|
||||
func gfPolyMultiply(a, b gfPoly) gfPoly {
|
||||
numATerms := a.numTerms()
|
||||
numBTerms := b.numTerms()
|
||||
|
||||
result := gfPoly{term: make([]gfElement, numATerms+numBTerms)}
|
||||
|
||||
for i := 0; i < numATerms; i++ {
|
||||
for j := 0; j < numBTerms; j++ {
|
||||
if a.term[i] != 0 && b.term[j] != 0 {
|
||||
monomial := gfPoly{term: make([]gfElement, i+j+1)}
|
||||
monomial.term[i+j] = gfMultiply(a.term[i], b.term[j])
|
||||
|
||||
result = gfPolyAdd(result, monomial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.normalised()
|
||||
}
|
||||
|
||||
// gfPolyRemainder return the remainder of numerator / denominator.
|
||||
func gfPolyRemainder(numerator, denominator gfPoly) gfPoly {
|
||||
if denominator.equals(gfPoly{}) {
|
||||
log.Panicln("Remainder by zero")
|
||||
}
|
||||
|
||||
remainder := numerator
|
||||
|
||||
for remainder.numTerms() >= denominator.numTerms() {
|
||||
degree := remainder.numTerms() - denominator.numTerms()
|
||||
coefficient := gfDivide(remainder.term[remainder.numTerms()-1],
|
||||
denominator.term[denominator.numTerms()-1])
|
||||
|
||||
divisor := gfPolyMultiply(denominator,
|
||||
newGFPolyMonomial(coefficient, degree))
|
||||
|
||||
remainder = gfPolyAdd(remainder, divisor)
|
||||
}
|
||||
|
||||
return remainder.normalised()
|
||||
}
|
||||
|
||||
// gfPolyAdd returns a + b.
|
||||
func gfPolyAdd(a, b gfPoly) gfPoly {
|
||||
numATerms := a.numTerms()
|
||||
numBTerms := b.numTerms()
|
||||
|
||||
numTerms := numATerms
|
||||
if numBTerms > numTerms {
|
||||
numTerms = numBTerms
|
||||
}
|
||||
|
||||
result := gfPoly{term: make([]gfElement, numTerms)}
|
||||
|
||||
for i := 0; i < numTerms; i++ {
|
||||
switch {
|
||||
case numATerms > i && numBTerms > i:
|
||||
result.term[i] = gfAdd(a.term[i], b.term[i])
|
||||
case numATerms > i:
|
||||
result.term[i] = a.term[i]
|
||||
default:
|
||||
result.term[i] = b.term[i]
|
||||
}
|
||||
}
|
||||
|
||||
return result.normalised()
|
||||
}
|
||||
|
||||
func (e gfPoly) normalised() gfPoly {
|
||||
numTerms := e.numTerms()
|
||||
maxNonzeroTerm := numTerms - 1
|
||||
|
||||
for i := numTerms - 1; i >= 0; i-- {
|
||||
if e.term[i] != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
maxNonzeroTerm = i - 1
|
||||
}
|
||||
|
||||
if maxNonzeroTerm < 0 {
|
||||
return gfPoly{}
|
||||
} else if maxNonzeroTerm < numTerms-1 {
|
||||
e.term = e.term[0 : maxNonzeroTerm+1]
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e gfPoly) string(useIndexForm bool) string {
|
||||
var str string
|
||||
numTerms := e.numTerms()
|
||||
|
||||
for i := numTerms - 1; i >= 0; i-- {
|
||||
if e.term[i] > 0 {
|
||||
if len(str) > 0 {
|
||||
str += " + "
|
||||
}
|
||||
|
||||
if !useIndexForm {
|
||||
str += fmt.Sprintf("%dx^%d", e.term[i], i)
|
||||
} else {
|
||||
str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(str) == 0 {
|
||||
str = "0"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// equals returns true if e == other.
|
||||
func (e gfPoly) equals(other gfPoly) bool {
|
||||
var minecPoly *gfPoly
|
||||
var maxecPoly *gfPoly
|
||||
|
||||
if e.numTerms() > other.numTerms() {
|
||||
minecPoly = &other
|
||||
maxecPoly = &e
|
||||
} else {
|
||||
minecPoly = &e
|
||||
maxecPoly = &other
|
||||
}
|
||||
|
||||
numMinTerms := minecPoly.numTerms()
|
||||
numMaxTerms := maxecPoly.numTerms()
|
||||
|
||||
for i := 0; i < numMinTerms; i++ {
|
||||
if e.term[i] != other.term[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := numMinTerms; i < numMaxTerms; i++ {
|
||||
if maxecPoly.term[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
// Package reedsolomon provides error correction encoding for QR Code 2005.
|
||||
//
|
||||
// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct
|
||||
// errors encountered during decoding.
|
||||
//
|
||||
// The generated RS codes are systematic, and consist of the input data with
|
||||
// error correction bytes appended.
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
bitset "github.com/skip2/go-qrcode/bitset"
|
||||
)
|
||||
|
||||
// Encode data for QR Code 2005 using the appropriate Reed-Solomon code.
|
||||
//
|
||||
// numECBytes is the number of error correction bytes to append, and is
|
||||
// determined by the target QR Code's version and error correction level.
|
||||
//
|
||||
// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has
|
||||
// numECBytes=7.
|
||||
func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset {
|
||||
// Create a polynomial representing |data|.
|
||||
//
|
||||
// The bytes are interpreted as the sequence of coefficients of a polynomial.
|
||||
// The last byte's value becomes the x^0 coefficient, the second to last
|
||||
// becomes the x^1 coefficient and so on.
|
||||
ecpoly := newGFPolyFromData(data)
|
||||
ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes))
|
||||
|
||||
// Pick the generator polynomial.
|
||||
generator := rsGeneratorPoly(numECBytes)
|
||||
|
||||
// Generate the error correction bytes.
|
||||
remainder := gfPolyRemainder(ecpoly, generator)
|
||||
|
||||
// Combine the data & error correcting bytes.
|
||||
// The mathematically correct answer is:
|
||||
//
|
||||
// result := gfPolyAdd(ecpoly, remainder).
|
||||
//
|
||||
// The encoding used by QR Code 2005 is slightly different this result: To
|
||||
// preserve the original |data| bit sequence exactly, the data and remainder
|
||||
// are combined manually below. This ensures any most significant zero bits
|
||||
// are preserved (and not optimised away).
|
||||
result := bitset.Clone(data)
|
||||
result.AppendBytes(remainder.data(numECBytes))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|.
|
||||
//
|
||||
// The generator polynomial is calculated as:
|
||||
// (x + a^0)(x + a^1)...(x + a^degree-1)
|
||||
func rsGeneratorPoly(degree int) gfPoly {
|
||||
if degree < 2 {
|
||||
log.Panic("degree < 2")
|
||||
}
|
||||
|
||||
generator := gfPoly{term: []gfElement{1}}
|
||||
|
||||
for i := 0; i < degree; i++ {
|
||||
nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}}
|
||||
generator = gfPolyMultiply(generator, nextPoly)
|
||||
}
|
||||
|
||||
return generator
|
||||
}
|
||||
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
@@ -1,315 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
bitset "github.com/skip2/go-qrcode/bitset"
|
||||
)
|
||||
|
||||
type regularSymbol struct {
|
||||
version qrCodeVersion
|
||||
mask int
|
||||
|
||||
data *bitset.Bitset
|
||||
|
||||
symbol *symbol
|
||||
size int
|
||||
}
|
||||
|
||||
// Abbreviated true/false.
|
||||
const (
|
||||
b0 = false
|
||||
b1 = true
|
||||
)
|
||||
|
||||
var (
|
||||
alignmentPatternCenter = [][]int{
|
||||
{}, // Version 0 doesn't exist.
|
||||
{}, // Version 1 doesn't use alignment patterns.
|
||||
{6, 18},
|
||||
{6, 22},
|
||||
{6, 26},
|
||||
{6, 30},
|
||||
{6, 34},
|
||||
{6, 22, 38},
|
||||
{6, 24, 42},
|
||||
{6, 26, 46},
|
||||
{6, 28, 50},
|
||||
{6, 30, 54},
|
||||
{6, 32, 58},
|
||||
{6, 34, 62},
|
||||
{6, 26, 46, 66},
|
||||
{6, 26, 48, 70},
|
||||
{6, 26, 50, 74},
|
||||
{6, 30, 54, 78},
|
||||
{6, 30, 56, 82},
|
||||
{6, 30, 58, 86},
|
||||
{6, 34, 62, 90},
|
||||
{6, 28, 50, 72, 94},
|
||||
{6, 26, 50, 74, 98},
|
||||
{6, 30, 54, 78, 102},
|
||||
{6, 28, 54, 80, 106},
|
||||
{6, 32, 58, 84, 110},
|
||||
{6, 30, 58, 86, 114},
|
||||
{6, 34, 62, 90, 118},
|
||||
{6, 26, 50, 74, 98, 122},
|
||||
{6, 30, 54, 78, 102, 126},
|
||||
{6, 26, 52, 78, 104, 130},
|
||||
{6, 30, 56, 82, 108, 134},
|
||||
{6, 34, 60, 86, 112, 138},
|
||||
{6, 30, 58, 86, 114, 142},
|
||||
{6, 34, 62, 90, 118, 146},
|
||||
{6, 30, 54, 78, 102, 126, 150},
|
||||
{6, 24, 50, 76, 102, 128, 154},
|
||||
{6, 28, 54, 80, 106, 132, 158},
|
||||
{6, 32, 58, 84, 110, 136, 162},
|
||||
{6, 26, 54, 82, 110, 138, 166},
|
||||
{6, 30, 58, 86, 114, 142, 170},
|
||||
}
|
||||
|
||||
finderPattern = [][]bool{
|
||||
{b1, b1, b1, b1, b1, b1, b1},
|
||||
{b1, b0, b0, b0, b0, b0, b1},
|
||||
{b1, b0, b1, b1, b1, b0, b1},
|
||||
{b1, b0, b1, b1, b1, b0, b1},
|
||||
{b1, b0, b1, b1, b1, b0, b1},
|
||||
{b1, b0, b0, b0, b0, b0, b1},
|
||||
{b1, b1, b1, b1, b1, b1, b1},
|
||||
}
|
||||
|
||||
finderPatternSize = 7
|
||||
|
||||
finderPatternHorizontalBorder = [][]bool{
|
||||
{b0, b0, b0, b0, b0, b0, b0, b0},
|
||||
}
|
||||
|
||||
finderPatternVerticalBorder = [][]bool{
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
{b0},
|
||||
}
|
||||
|
||||
alignmentPattern = [][]bool{
|
||||
{b1, b1, b1, b1, b1},
|
||||
{b1, b0, b0, b0, b1},
|
||||
{b1, b0, b1, b0, b1},
|
||||
{b1, b0, b0, b0, b1},
|
||||
{b1, b1, b1, b1, b1},
|
||||
}
|
||||
)
|
||||
|
||||
func buildRegularSymbol(version qrCodeVersion, mask int,
|
||||
data *bitset.Bitset, includeQuietZone bool) (*symbol, error) {
|
||||
|
||||
quietZoneSize := 0
|
||||
if includeQuietZone {
|
||||
quietZoneSize = version.quietZoneSize()
|
||||
}
|
||||
|
||||
m := ®ularSymbol{
|
||||
version: version,
|
||||
mask: mask,
|
||||
data: data,
|
||||
|
||||
symbol: newSymbol(version.symbolSize(), quietZoneSize),
|
||||
size: version.symbolSize(),
|
||||
}
|
||||
|
||||
m.addFinderPatterns()
|
||||
m.addAlignmentPatterns()
|
||||
m.addTimingPatterns()
|
||||
m.addFormatInfo()
|
||||
m.addVersionInfo()
|
||||
|
||||
ok, err := m.addData()
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.symbol, nil
|
||||
}
|
||||
|
||||
func (m *regularSymbol) addFinderPatterns() {
|
||||
fpSize := finderPatternSize
|
||||
fp := finderPattern
|
||||
fpHBorder := finderPatternHorizontalBorder
|
||||
fpVBorder := finderPatternVerticalBorder
|
||||
|
||||
// Top left Finder Pattern.
|
||||
m.symbol.set2dPattern(0, 0, fp)
|
||||
m.symbol.set2dPattern(0, fpSize, fpHBorder)
|
||||
m.symbol.set2dPattern(fpSize, 0, fpVBorder)
|
||||
|
||||
// Top right Finder Pattern.
|
||||
m.symbol.set2dPattern(m.size-fpSize, 0, fp)
|
||||
m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
|
||||
m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
|
||||
|
||||
// Bottom left Finder Pattern.
|
||||
m.symbol.set2dPattern(0, m.size-fpSize, fp)
|
||||
m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
|
||||
m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
|
||||
}
|
||||
|
||||
func (m *regularSymbol) addAlignmentPatterns() {
|
||||
for _, x := range alignmentPatternCenter[m.version.version] {
|
||||
for _, y := range alignmentPatternCenter[m.version.version] {
|
||||
if !m.symbol.empty(x, y) {
|
||||
continue
|
||||
}
|
||||
|
||||
m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *regularSymbol) addTimingPatterns() {
|
||||
value := true
|
||||
|
||||
for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
|
||||
m.symbol.set(i, finderPatternSize-1, value)
|
||||
m.symbol.set(finderPatternSize-1, i, value)
|
||||
|
||||
value = !value
|
||||
}
|
||||
}
|
||||
|
||||
func (m *regularSymbol) addFormatInfo() {
|
||||
fpSize := finderPatternSize
|
||||
l := formatInfoLengthBits - 1
|
||||
|
||||
f := m.version.formatInfo(m.mask)
|
||||
|
||||
// Bits 0-7, under the top right finder pattern.
|
||||
for i := 0; i <= 7; i++ {
|
||||
m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
|
||||
}
|
||||
|
||||
// Bits 0-5, right of the top left finder pattern.
|
||||
for i := 0; i <= 5; i++ {
|
||||
m.symbol.set(fpSize+1, i, f.At(l-i))
|
||||
}
|
||||
|
||||
// Bits 6-8 on the corner of the top left finder pattern.
|
||||
m.symbol.set(fpSize+1, fpSize, f.At(l-6))
|
||||
m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
|
||||
m.symbol.set(fpSize, fpSize+1, f.At(l-8))
|
||||
|
||||
// Bits 9-14 on the underside of the top left finder pattern.
|
||||
for i := 9; i <= 14; i++ {
|
||||
m.symbol.set(14-i, fpSize+1, f.At(l-i))
|
||||
}
|
||||
|
||||
// Bits 8-14 on the right side of the bottom left finder pattern.
|
||||
for i := 8; i <= 14; i++ {
|
||||
m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
|
||||
}
|
||||
|
||||
// Always dark symbol.
|
||||
m.symbol.set(fpSize+1, m.size-fpSize-1, true)
|
||||
}
|
||||
|
||||
func (m *regularSymbol) addVersionInfo() {
|
||||
fpSize := finderPatternSize
|
||||
|
||||
v := m.version.versionInfo()
|
||||
l := versionInfoLengthBits - 1
|
||||
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
// Above the bottom left finder pattern.
|
||||
m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
|
||||
|
||||
// Left of the top right finder pattern.
|
||||
m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
|
||||
}
|
||||
}
|
||||
|
||||
type direction uint8
|
||||
|
||||
const (
|
||||
up direction = iota
|
||||
down
|
||||
)
|
||||
|
||||
func (m *regularSymbol) addData() (bool, error) {
|
||||
xOffset := 1
|
||||
dir := up
|
||||
|
||||
x := m.size - 2
|
||||
y := m.size - 1
|
||||
|
||||
for i := 0; i < m.data.Len(); i++ {
|
||||
var mask bool
|
||||
switch m.mask {
|
||||
case 0:
|
||||
mask = (y+x+xOffset)%2 == 0
|
||||
case 1:
|
||||
mask = y%2 == 0
|
||||
case 2:
|
||||
mask = (x+xOffset)%3 == 0
|
||||
case 3:
|
||||
mask = (y+x+xOffset)%3 == 0
|
||||
case 4:
|
||||
mask = (y/2+(x+xOffset)/3)%2 == 0
|
||||
case 5:
|
||||
mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
|
||||
case 6:
|
||||
mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
|
||||
case 7:
|
||||
mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
|
||||
}
|
||||
|
||||
// != is equivalent to XOR.
|
||||
m.symbol.set(x+xOffset, y, mask != m.data.At(i))
|
||||
|
||||
if i == m.data.Len()-1 {
|
||||
break
|
||||
}
|
||||
|
||||
// Find next free bit in the symbol.
|
||||
for {
|
||||
if xOffset == 1 {
|
||||
xOffset = 0
|
||||
} else {
|
||||
xOffset = 1
|
||||
|
||||
if dir == up {
|
||||
if y > 0 {
|
||||
y--
|
||||
} else {
|
||||
dir = down
|
||||
x -= 2
|
||||
}
|
||||
} else {
|
||||
if y < m.size-1 {
|
||||
y++
|
||||
} else {
|
||||
dir = up
|
||||
x -= 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip over the vertical timing pattern entirely.
|
||||
if x == 5 {
|
||||
x--
|
||||
}
|
||||
|
||||
if m.symbol.empty(x+xOffset, y) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
@@ -1,309 +0,0 @@
|
||||
// go-qrcode
|
||||
// Copyright 2014 Tom Harwood
|
||||
|
||||
package qrcode
|
||||
|
||||
// symbol is a 2D array of bits representing a QR Code symbol.
|
||||
//
|
||||
// A symbol consists of size*size modules, with each module normally drawn as a
|
||||
// black or white square. The symbol also has a border of quietZoneSize modules.
|
||||
//
|
||||
// A (fictional) size=2, quietZoneSize=1 QR Code looks like:
|
||||
//
|
||||
// +----+
|
||||
// | |
|
||||
// | ab |
|
||||
// | cd |
|
||||
// | |
|
||||
// +----+
|
||||
//
|
||||
// For ease of implementation, the functions to set/get bits ignore the border,
|
||||
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
|
||||
// border) is returned by bitmap().
|
||||
//
|
||||
type symbol struct {
|
||||
// Value of module at [y][x]. True is set.
|
||||
module [][]bool
|
||||
|
||||
// True if the module at [y][x] is used (to either true or false).
|
||||
// Used to identify unused modules.
|
||||
isUsed [][]bool
|
||||
|
||||
// Combined width/height of the symbol and quiet zones.
|
||||
//
|
||||
// size = symbolSize + 2*quietZoneSize.
|
||||
size int
|
||||
|
||||
// Width/height of the symbol only.
|
||||
symbolSize int
|
||||
|
||||
// Width/height of a single quiet zone.
|
||||
quietZoneSize int
|
||||
}
|
||||
|
||||
// newSymbol constructs a symbol of size size*size, with a border of
|
||||
// quietZoneSize.
|
||||
func newSymbol(size int, quietZoneSize int) *symbol {
|
||||
var m symbol
|
||||
|
||||
m.module = make([][]bool, size+2*quietZoneSize)
|
||||
m.isUsed = make([][]bool, size+2*quietZoneSize)
|
||||
|
||||
for i := range m.module {
|
||||
m.module[i] = make([]bool, size+2*quietZoneSize)
|
||||
m.isUsed[i] = make([]bool, size+2*quietZoneSize)
|
||||
}
|
||||
|
||||
m.size = size + 2*quietZoneSize
|
||||
m.symbolSize = size
|
||||
m.quietZoneSize = quietZoneSize
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
// get returns the module value at (x, y).
|
||||
func (m *symbol) get(x int, y int) (v bool) {
|
||||
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
|
||||
return
|
||||
}
|
||||
|
||||
// empty returns true if the module at (x, y) has not been set (to either true
|
||||
// or false).
|
||||
func (m *symbol) empty(x int, y int) bool {
|
||||
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
|
||||
}
|
||||
|
||||
// numEmptyModules returns the number of empty modules.
|
||||
//
|
||||
// Initially numEmptyModules is symbolSize * symbolSize. After every module has
|
||||
// been set (to either true or false), the number of empty modules is zero.
|
||||
func (m *symbol) numEmptyModules() int {
|
||||
var count int
|
||||
for y := 0; y < m.symbolSize; y++ {
|
||||
for x := 0; x < m.symbolSize; x++ {
|
||||
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// set sets the module at (x, y) to v.
|
||||
func (m *symbol) set(x int, y int, v bool) {
|
||||
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
|
||||
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
|
||||
}
|
||||
|
||||
// set2dPattern sets a 2D array of modules, starting at (x, y).
|
||||
func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
|
||||
for j, row := range v {
|
||||
for i, value := range row {
|
||||
m.set(x+i, y+j, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bitmap returns the entire symbol, including the quiet zone.
|
||||
func (m *symbol) bitmap() [][]bool {
|
||||
module := make([][]bool, len(m.module))
|
||||
|
||||
for i := range m.module {
|
||||
module[i] = m.module[i][:]
|
||||
}
|
||||
|
||||
return module
|
||||
}
|
||||
|
||||
// string returns a pictorial representation of the symbol, suitable for
|
||||
// printing in a TTY.
|
||||
func (m *symbol) string() string {
|
||||
var result string
|
||||
|
||||
for _, row := range m.module {
|
||||
for _, value := range row {
|
||||
switch value {
|
||||
case true:
|
||||
result += " "
|
||||
case false:
|
||||
// Unicode 'FULL BLOCK' (U+2588).
|
||||
result += "██"
|
||||
}
|
||||
}
|
||||
result += "\n"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Constants used to weight penalty calculations. Specified by ISO/IEC
|
||||
// 18004:2006.
|
||||
const (
|
||||
penaltyWeight1 = 3
|
||||
penaltyWeight2 = 3
|
||||
penaltyWeight3 = 40
|
||||
penaltyWeight4 = 10
|
||||
)
|
||||
|
||||
// penaltyScore returns the penalty score of the symbol. The penalty score
|
||||
// consists of the sum of the four individual penalty types.
|
||||
func (m *symbol) penaltyScore() int {
|
||||
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
|
||||
}
|
||||
|
||||
// penalty1 returns the penalty score for "adjacent modules in row/column with
|
||||
// same colour".
|
||||
//
|
||||
// The numbers of adjacent matching modules and scores are:
|
||||
// 0-5: score = 0
|
||||
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
|
||||
func (m *symbol) penalty1() int {
|
||||
penalty := 0
|
||||
|
||||
for x := 0; x < m.symbolSize; x++ {
|
||||
lastValue := m.get(x, 0)
|
||||
count := 1
|
||||
|
||||
for y := 1; y < m.symbolSize; y++ {
|
||||
v := m.get(x, y)
|
||||
|
||||
if v != lastValue {
|
||||
count = 1
|
||||
lastValue = v
|
||||
} else {
|
||||
count++
|
||||
if count == 6 {
|
||||
penalty += penaltyWeight1 + 1
|
||||
} else if count > 6 {
|
||||
penalty++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for y := 0; y < m.symbolSize; y++ {
|
||||
lastValue := m.get(0, y)
|
||||
count := 1
|
||||
|
||||
for x := 1; x < m.symbolSize; x++ {
|
||||
v := m.get(x, y)
|
||||
|
||||
if v != lastValue {
|
||||
count = 1
|
||||
lastValue = v
|
||||
} else {
|
||||
count++
|
||||
if count == 6 {
|
||||
penalty += penaltyWeight1 + 1
|
||||
} else if count > 6 {
|
||||
penalty++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return penalty
|
||||
}
|
||||
|
||||
// penalty2 returns the penalty score for "block of modules in the same colour".
|
||||
//
|
||||
// m*n: score = penaltyWeight2 * (m-1) * (n-1).
|
||||
func (m *symbol) penalty2() int {
|
||||
penalty := 0
|
||||
|
||||
for y := 1; y < m.symbolSize; y++ {
|
||||
for x := 1; x < m.symbolSize; x++ {
|
||||
topLeft := m.get(x-1, y-1)
|
||||
above := m.get(x, y-1)
|
||||
left := m.get(x-1, y)
|
||||
current := m.get(x, y)
|
||||
|
||||
if current == left && current == above && current == topLeft {
|
||||
penalty++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return penalty * penaltyWeight2
|
||||
}
|
||||
|
||||
// penalty3 returns the penalty score for "1:1:3:1:1 ratio
|
||||
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
|
||||
// light area 4 modules wide".
|
||||
//
|
||||
// Existence of the pattern scores penaltyWeight3.
|
||||
func (m *symbol) penalty3() int {
|
||||
penalty := 0
|
||||
|
||||
for y := 0; y < m.symbolSize; y++ {
|
||||
var bitBuffer int16 = 0x00
|
||||
|
||||
for x := 0; x < m.symbolSize; x++ {
|
||||
bitBuffer <<= 1
|
||||
if v := m.get(x, y); v {
|
||||
bitBuffer |= 1
|
||||
}
|
||||
|
||||
switch bitBuffer & 0x7ff {
|
||||
// 0b000 0101 1101 or 0b10111010000
|
||||
// 0x05d or 0x5d0
|
||||
case 0x05d, 0x5d0:
|
||||
penalty += penaltyWeight3
|
||||
bitBuffer = 0xFF
|
||||
default:
|
||||
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
||||
penalty += penaltyWeight3
|
||||
bitBuffer = 0xFF
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for x := 0; x < m.symbolSize; x++ {
|
||||
var bitBuffer int16 = 0x00
|
||||
|
||||
for y := 0; y < m.symbolSize; y++ {
|
||||
bitBuffer <<= 1
|
||||
if v := m.get(x, y); v {
|
||||
bitBuffer |= 1
|
||||
}
|
||||
|
||||
switch bitBuffer & 0x7ff {
|
||||
// 0b000 0101 1101 or 0b10111010000
|
||||
// 0x05d or 0x5d0
|
||||
case 0x05d, 0x5d0:
|
||||
penalty += penaltyWeight3
|
||||
bitBuffer = 0xFF
|
||||
default:
|
||||
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
||||
penalty += penaltyWeight3
|
||||
bitBuffer = 0xFF
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return penalty
|
||||
}
|
||||
|
||||
// penalty4 returns the penalty score...
|
||||
func (m *symbol) penalty4() int {
|
||||
numModules := m.symbolSize * m.symbolSize
|
||||
numDarkModules := 0
|
||||
|
||||
for x := 0; x < m.symbolSize; x++ {
|
||||
for y := 0; y < m.symbolSize; y++ {
|
||||
if v := m.get(x, y); v {
|
||||
numDarkModules++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
numDarkModuleDeviation := numModules/2 - numDarkModules
|
||||
if numDarkModuleDeviation < 0 {
|
||||
numDarkModuleDeviation *= -1
|
||||
}
|
||||
|
||||
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
|
||||
}
|
||||
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user