mirror of
https://github.com/42wim/matterbridge.git
synced 2024-12-18 09:02:39 +01:00
Add initial WhatsApp support (#711)
This commit is contained in:
parent
46f4bbb3b5
commit
55e79063d6
@ -18,6 +18,7 @@
|
|||||||
[Rocket.Chat][mb-rocketchat] |
|
[Rocket.Chat][mb-rocketchat] |
|
||||||
[XMPP][mb-xmpp] |
|
[XMPP][mb-xmpp] |
|
||||||
[Twitch][mb-twitch] |
|
[Twitch][mb-twitch] |
|
||||||
|
[WhatsApp][mb-whatsapp] |
|
||||||
[Zulip][mb-zulip] |
|
[Zulip][mb-zulip] |
|
||||||
And more...
|
And more...
|
||||||
</sup>
|
</sup>
|
||||||
@ -79,6 +80,7 @@
|
|||||||
* [Steam](https://store.steampowered.com/)
|
* [Steam](https://store.steampowered.com/)
|
||||||
* [Twitch](https://twitch.tv)
|
* [Twitch](https://twitch.tv)
|
||||||
* [Ssh-chat](https://github.com/shazow/ssh-chat)
|
* [Ssh-chat](https://github.com/shazow/ssh-chat)
|
||||||
|
* [WhatsApp](https://www.whatsapp.com/)
|
||||||
* [Zulip](https://zulipchat.com)
|
* [Zulip](https://zulipchat.com)
|
||||||
|
|
||||||
### 3rd party via matterbridge api
|
### 3rd party via matterbridge api
|
||||||
@ -276,6 +278,7 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
* steam - https://github.com/Philipp15b/go-steam
|
* steam - https://github.com/Philipp15b/go-steam
|
||||||
* telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
|
* telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
|
||||||
* xmpp - https://github.com/mattn/go-xmpp
|
* xmpp - https://github.com/mattn/go-xmpp
|
||||||
|
* whatsapp - https://github.com/Rhymen/go-whatsapp/
|
||||||
* zulip - https://github.com/ifo/gozulipbot
|
* zulip - https://github.com/ifo/gozulipbot
|
||||||
|
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
@ -289,4 +292,5 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
|
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
|
||||||
[mb-xmpp]: https://inverse.chat/
|
[mb-xmpp]: https://inverse.chat/
|
||||||
[mb-twitch]: https://www.twitch.tv/matterbridge
|
[mb-twitch]: https://www.twitch.tv/matterbridge
|
||||||
|
[mb-whatsapp]: https://www.whatsapp.com/
|
||||||
[mb-zulip]: https://matterbridge.zulipchat.com/register/
|
[mb-zulip]: https://matterbridge.zulipchat.com/register/
|
||||||
|
@ -185,6 +185,7 @@ type BridgeValues struct {
|
|||||||
Telegram map[string]Protocol
|
Telegram map[string]Protocol
|
||||||
Rocketchat map[string]Protocol
|
Rocketchat map[string]Protocol
|
||||||
SSHChat map[string]Protocol
|
SSHChat map[string]Protocol
|
||||||
|
WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
|
||||||
Zulip map[string]Protocol
|
Zulip map[string]Protocol
|
||||||
General Protocol
|
General Protocol
|
||||||
Gateway []Gateway
|
Gateway []Gateway
|
||||||
|
104
bridge/whatsapp/handlers.go
Normal file
104
bridge/whatsapp/handlers.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package bwhatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
|
whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implement handling messages coming from WhatsApp
|
||||||
|
Check:
|
||||||
|
- https://github.com/Rhymen/go-whatsapp#add-message-handlers
|
||||||
|
- https://github.com/Rhymen/go-whatsapp/blob/master/handler.go
|
||||||
|
- https://github.com/tulir/mautrix-whatsapp/tree/master/whatsapp-ext for more advanced command handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HandleError received from WhatsApp
|
||||||
|
func (b *Bwhatsapp) HandleError(err error) {
|
||||||
|
b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleTextMessage sent from WhatsApp, relay it to the brige
|
||||||
|
func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
|
||||||
|
if message.Info.FromMe { // || !strings.Contains(strings.ToLower(message.Text), "@echo") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// whatsapp sends last messages to show context , cut them
|
||||||
|
if message.Info.Timestamp < b.startedAt {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones
|
||||||
|
groupJid := message.Info.RemoteJid
|
||||||
|
|
||||||
|
senderJid := message.Info.SenderJid
|
||||||
|
if len(senderJid) == 0 {
|
||||||
|
// TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved
|
||||||
|
senderJid = *message.Info.Source.Participant
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate sender's Jid to the nicest username we can get
|
||||||
|
senderName := b.getSenderName(senderJid)
|
||||||
|
if senderName == "" {
|
||||||
|
senderName = "Someone" // don't expose telephone number
|
||||||
|
}
|
||||||
|
|
||||||
|
extText := message.Info.Source.Message.ExtendedTextMessage
|
||||||
|
if extText != nil && extText.ContextInfo != nil && extText.ContextInfo.MentionedJid != nil {
|
||||||
|
// handle user mentions
|
||||||
|
for _, mentionedJid := range extText.ContextInfo.MentionedJid {
|
||||||
|
numberAndSuffix := strings.SplitN(mentionedJid, "@", 2)
|
||||||
|
|
||||||
|
// mentions comes as telephone numbers and we don't want to expose it to other bridges
|
||||||
|
// replace it with something more meaninful to others
|
||||||
|
mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix)
|
||||||
|
if mention == "" {
|
||||||
|
mention = "someone"
|
||||||
|
}
|
||||||
|
message.Text = strings.Replace(message.Text, "@"+numberAndSuffix[0], "@"+mention, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJid, b.Account)
|
||||||
|
rmsg := config.Message{
|
||||||
|
UserID: senderJid,
|
||||||
|
Username: senderName,
|
||||||
|
Text: message.Text,
|
||||||
|
Timestamp: messageTime,
|
||||||
|
Channel: groupJid,
|
||||||
|
Account: b.Account,
|
||||||
|
Protocol: b.Protocol,
|
||||||
|
Extra: make(map[string][]interface{}),
|
||||||
|
// ParentID: TODO, // TODO handle thread replies // map from Info.QuotedMessageID string
|
||||||
|
// Event string `json:"event"`
|
||||||
|
// Gateway string // will be added during message processing
|
||||||
|
ID: message.Info.Id}
|
||||||
|
|
||||||
|
if avatarURL, exists := b.userAvatars[senderJid]; exists {
|
||||||
|
rmsg.Avatar = avatarURL
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log.Debugf("<= Message is %#v", rmsg)
|
||||||
|
b.Remote <- rmsg
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||||
|
// fmt.Println(message) // TODO implement
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||||
|
// fmt.Println(message) // TODO implement
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (b *Bwhatsapp) HandleJsonMessage(message string) {
|
||||||
|
// fmt.Println(message) // TODO implement
|
||||||
|
//}
|
||||||
|
// TODO HandleRawMessage
|
||||||
|
// TODO HandleAudioMessage
|
84
bridge/whatsapp/helpers.go
Normal file
84
bridge/whatsapp/helpers.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package bwhatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func qrFromTerminal(invert bool) chan string {
|
||||||
|
qr := make(chan string)
|
||||||
|
go func() {
|
||||||
|
terminal := qrcodeTerminal.New()
|
||||||
|
if invert {
|
||||||
|
terminal = qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightWhite, qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.QRCodeRecoveryLevels.Medium)
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.Get(<-qr).Print()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return qr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bwhatsapp) readSession() (whatsapp.Session, error) {
|
||||||
|
session := whatsapp.Session{}
|
||||||
|
sessionFile := b.Config.GetString(sessionFile)
|
||||||
|
|
||||||
|
if sessionFile == "" {
|
||||||
|
return session, errors.New("if you won't set SessionFile then you will need to scan QR code on every restart")
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(sessionFile)
|
||||||
|
if err != nil {
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
decoder := gob.NewDecoder(file)
|
||||||
|
err = decoder.Decode(&session)
|
||||||
|
if err != nil {
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bwhatsapp) writeSession(session whatsapp.Session) error {
|
||||||
|
sessionFile := b.Config.GetString(sessionFile)
|
||||||
|
|
||||||
|
if sessionFile == "" {
|
||||||
|
// we already sent a warning while starting the bridge, so let's be quiet here
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(sessionFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
encoder := gob.NewEncoder(file)
|
||||||
|
err = encoder.Encode(session)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bwhatsapp) getSenderName(senderJid string) string {
|
||||||
|
if sender, exists := b.users[senderJid]; exists {
|
||||||
|
if sender.Name != "" {
|
||||||
|
return sender.Name
|
||||||
|
}
|
||||||
|
// if user is not in phone contacts
|
||||||
|
// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
|
||||||
|
// users can change it in their WhatsApp settings -> profile -> click on Avatar
|
||||||
|
return sender.Notify
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
|
||||||
|
if sender, exists := b.users[senderJid]; exists {
|
||||||
|
return sender.Notify
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
305
bridge/whatsapp/whatsapp.go
Normal file
305
bridge/whatsapp/whatsapp.go
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
package bwhatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/42wim/matterbridge/bridge"
|
||||||
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
|
||||||
|
whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Account config parameters
|
||||||
|
cfgNumber = "Number"
|
||||||
|
qrOnWhiteTerminal = "QrOnWhiteTerminal"
|
||||||
|
sessionFile = "SessionFile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bwhatsapp Bridge structure keeping all the information needed for relying
|
||||||
|
type Bwhatsapp struct {
|
||||||
|
*bridge.Config
|
||||||
|
|
||||||
|
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
|
||||||
|
session *whatsapp.Session
|
||||||
|
conn *whatsapp.Conn
|
||||||
|
// https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go
|
||||||
|
connExt *whatsappExt.ExtendedConn
|
||||||
|
startedAt uint64
|
||||||
|
|
||||||
|
users map[string]whatsapp.Contact
|
||||||
|
userAvatars map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New Create a new WhatsApp bridge. This will be called for each [whatsapp.<server>] entry you have in the config file
|
||||||
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
|
number := cfg.GetString(cfgNumber)
|
||||||
|
if number == "" {
|
||||||
|
cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number")
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &Bwhatsapp{
|
||||||
|
Config: cfg,
|
||||||
|
|
||||||
|
users: make(map[string]whatsapp.Contact),
|
||||||
|
userAvatars: make(map[string]string),
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to WhatsApp. Required implementation of the Bridger interface
|
||||||
|
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
|
||||||
|
func (b *Bwhatsapp) Connect() error {
|
||||||
|
b.RLock() // TODO do we need locking for Whatsapp?
|
||||||
|
defer b.RUnlock()
|
||||||
|
|
||||||
|
number := b.GetString(cfgNumber)
|
||||||
|
if number == "" {
|
||||||
|
return errors.New("WhatsApp's telephone Number need to be configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/Rhymen/go-whatsapp#creating-a-connection
|
||||||
|
b.Log.Debugln("Connecting to WhatsApp..")
|
||||||
|
conn, err := whatsapp.NewConn(20 * time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to connect to WhatsApp: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
b.conn = conn
|
||||||
|
b.connExt = whatsappExt.ExtendConn(b.conn)
|
||||||
|
// TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa")
|
||||||
|
|
||||||
|
b.conn.AddHandler(b)
|
||||||
|
b.Log.Debugln("WhatsApp connection successful")
|
||||||
|
|
||||||
|
// load existing session in order to keep it between restarts
|
||||||
|
if b.session == nil {
|
||||||
|
var session whatsapp.Session
|
||||||
|
session, err = b.readSession()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
b.Log.Debugln("Restoring WhatsApp session..")
|
||||||
|
|
||||||
|
// https://github.com/Rhymen/go-whatsapp#restore
|
||||||
|
session, err = b.conn.RestoreSession(session)
|
||||||
|
if err != nil {
|
||||||
|
// TODO return or continue to normal login?
|
||||||
|
// restore session connection timed out (I couldn't get over it without logging in again)
|
||||||
|
return errors.New("failed to restore session: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
b.session = &session
|
||||||
|
b.Log.Debugln("Session restored successfully!")
|
||||||
|
} else {
|
||||||
|
b.Log.Warn(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// login to a new session
|
||||||
|
if b.session == nil {
|
||||||
|
err = b.Login()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.startedAt = uint64(time.Now().Unix())
|
||||||
|
|
||||||
|
_, err = b.conn.Contacts()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error on update of contacts: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// map all the users
|
||||||
|
for id, contact := range b.conn.Store.Contacts {
|
||||||
|
if !isGroupJid(id) && id != "status@broadcast" {
|
||||||
|
// it is user
|
||||||
|
b.users[id] = contact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get user avatar asynchronously
|
||||||
|
go func() {
|
||||||
|
b.Log.Debug("Getting user avatars..")
|
||||||
|
|
||||||
|
for jid := range b.users {
|
||||||
|
info, err := b.connExt.GetProfilePicThumb(jid)
|
||||||
|
if err != nil {
|
||||||
|
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO any race conditions here?
|
||||||
|
b.userAvatars[jid] = info.URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.Log.Debug("Finished getting avatars..")
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login to WhatsApp creating a new session. This will require to scan a QR code on your mobile device
|
||||||
|
func (b *Bwhatsapp) Login() error {
|
||||||
|
b.Log.Debugln("Logging in..")
|
||||||
|
|
||||||
|
invert := b.GetBool(qrOnWhiteTerminal) // false is the default
|
||||||
|
qrChan := qrFromTerminal(invert)
|
||||||
|
|
||||||
|
session, err := b.conn.Login(qrChan)
|
||||||
|
if err != nil {
|
||||||
|
b.Log.Warnln("Failed to log in:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.session = &session
|
||||||
|
|
||||||
|
b.Log.Infof("Logged into session: %#v", session)
|
||||||
|
b.Log.Infof("Connection: %#v", b.conn)
|
||||||
|
|
||||||
|
err = b.writeSession(session)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error saving session: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO change connection strings to configured ones longClientName:"github.com/rhymen/go-whatsapp", shortClientName:"go-whatsapp"}" prefix=whatsapp
|
||||||
|
// TODO get also a nice logo
|
||||||
|
|
||||||
|
// TODO notification about unplugged and dead battery
|
||||||
|
// conn.Info: Wid, Pushname, Connected, Battery, Plugged
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect is called while reconnecting to the bridge
|
||||||
|
// TODO 42wim Documentation would be helpful on when reconnects happen and what should be done in this function
|
||||||
|
// Required implementation of the Bridger interface
|
||||||
|
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
|
||||||
|
func (b *Bwhatsapp) Disconnect() error {
|
||||||
|
// We could Logout, but that would close the session completely and would require a new QR code scan
|
||||||
|
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L377-L381
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isGroupJid(identifier string) bool {
|
||||||
|
return strings.HasSuffix(identifier, "@g.us") || strings.HasSuffix(identifier, "@temp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinChannel Join a WhatsApp group specified in gateway config as channel='number-id@g.us' or channel='Channel name'
|
||||||
|
// Required implementation of the Bridger interface
|
||||||
|
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
|
||||||
|
func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
|
||||||
|
byJid := isGroupJid(channel.Name)
|
||||||
|
|
||||||
|
// verify if we are member of the given group
|
||||||
|
if byJid {
|
||||||
|
// channel.Name specifies static group jID, not the name
|
||||||
|
if _, exists := b.conn.Store.Contacts[channel.Name]; !exists {
|
||||||
|
return fmt.Errorf("account doesn't belong to group with jid %s", channel.Name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// channel.Name specifies group name that might change, warn about it
|
||||||
|
var jids []string
|
||||||
|
for id, contact := range b.conn.Store.Contacts {
|
||||||
|
if isGroupJid(id) && contact.Name == channel.Name {
|
||||||
|
jids = append(jids, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(jids) {
|
||||||
|
case 0:
|
||||||
|
// didn't match any group - print out possibilites
|
||||||
|
// TODO sort
|
||||||
|
// copy b;
|
||||||
|
//sort.Slice(people, func(i, j int) bool {
|
||||||
|
// return people[i].Age > people[j].Age
|
||||||
|
//})
|
||||||
|
for id, contact := range b.conn.Store.Contacts {
|
||||||
|
if isGroupJid(id) {
|
||||||
|
b.Log.Infof("%s %s", contact.Jid, contact.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name)
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return fmt.Errorf("group name might change. Please configure gateway with channel=\"%v\" instead of channel=\"%v\"", jids[0], channel.Name)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("there is more than one group with name '%s'. Please specify one of JIDs as channel name: %v", channel.Name, jids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a message from the bridge to WhatsApp
|
||||||
|
// Required implementation of the Bridger interface
|
||||||
|
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
|
||||||
|
func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
|
||||||
|
b.Log.Debugf("=> Receiving %#v", msg)
|
||||||
|
|
||||||
|
// Delete message
|
||||||
|
if msg.Event == config.EventMsgDelete {
|
||||||
|
if msg.ID == "" {
|
||||||
|
// No message ID in case action is executed on a message sent before the bridge was started
|
||||||
|
// and then the bridge cache doesn't have this message ID mapped
|
||||||
|
|
||||||
|
// TODO 42wim Doesn't the app get clogged with a ton of IDs after some time of running?
|
||||||
|
// WhatsApp allows to set any ID so in that case we could use external IDs and don't do mapping
|
||||||
|
// but external IDs are not set
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
// TODO delete message on WhatsApp https://github.com/Rhymen/go-whatsapp/issues/100
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit message
|
||||||
|
if msg.ID != "" {
|
||||||
|
b.Log.Debugf("updating message with id %s", msg.ID)
|
||||||
|
|
||||||
|
msg.Text += " (edited)"
|
||||||
|
// TODO handle edit as a message reply with updated text
|
||||||
|
}
|
||||||
|
|
||||||
|
//// TODO Handle Upload a file
|
||||||
|
//if msg.Extra != nil {
|
||||||
|
// for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||||
|
// b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
|
||||||
|
// }
|
||||||
|
// if len(msg.Extra["file"]) > 0 {
|
||||||
|
// return b.handleUploadFile(&msg, roomID)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Post text message
|
||||||
|
text := whatsapp.TextMessage{
|
||||||
|
Info: whatsapp.MessageInfo{
|
||||||
|
RemoteJid: msg.Channel, // which equals to group id
|
||||||
|
},
|
||||||
|
Text: msg.Username + msg.Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Log.Debugf("=> Sending %#v", msg)
|
||||||
|
|
||||||
|
// create message ID
|
||||||
|
// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented
|
||||||
|
bytes := make([]byte, 10)
|
||||||
|
if _, err := rand.Read(bytes); err != nil {
|
||||||
|
b.Log.Warn(err.Error())
|
||||||
|
}
|
||||||
|
text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
|
||||||
|
|
||||||
|
err := b.conn.Send(text)
|
||||||
|
|
||||||
|
return text.Info.Id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO do we want that? to allow login with QR code from a bridged channel? https://github.com/tulir/mautrix-whatsapp/blob/513eb18e2d59bada0dd515ee1abaaf38a3bfe3d5/commands.go#L76
|
||||||
|
//func (b *Bwhatsapp) Command(cmd string) string {
|
||||||
|
// return ""
|
||||||
|
//}
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/sshchat"
|
"github.com/42wim/matterbridge/bridge/sshchat"
|
||||||
"github.com/42wim/matterbridge/bridge/steam"
|
"github.com/42wim/matterbridge/bridge/steam"
|
||||||
"github.com/42wim/matterbridge/bridge/telegram"
|
"github.com/42wim/matterbridge/bridge/telegram"
|
||||||
|
"github.com/42wim/matterbridge/bridge/whatsapp"
|
||||||
"github.com/42wim/matterbridge/bridge/xmpp"
|
"github.com/42wim/matterbridge/bridge/xmpp"
|
||||||
"github.com/42wim/matterbridge/bridge/zulip"
|
"github.com/42wim/matterbridge/bridge/zulip"
|
||||||
)
|
)
|
||||||
@ -30,6 +31,7 @@ var FullMap = map[string]bridge.Factory{
|
|||||||
"sshchat": bsshchat.New,
|
"sshchat": bsshchat.New,
|
||||||
"steam": bsteam.New,
|
"steam": bsteam.New,
|
||||||
"telegram": btelegram.New,
|
"telegram": btelegram.New,
|
||||||
|
"whatsapp": bwhatsapp.New,
|
||||||
"xmpp": bxmpp.New,
|
"xmpp": bxmpp.New,
|
||||||
"zulip": bzulip.New,
|
"zulip": bzulip.New,
|
||||||
}
|
}
|
||||||
|
8
go.mod
8
go.mod
@ -2,14 +2,15 @@ module github.com/42wim/matterbridge
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||||
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
|
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
|
||||||
github.com/Jeffail/gabs v1.1.1 // indirect
|
github.com/Jeffail/gabs v1.1.1 // indirect
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884
|
||||||
github.com/bwmarrin/discordgo v0.19.0
|
github.com/bwmarrin/discordgo v0.19.0
|
||||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
||||||
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc // indirect
|
|
||||||
github.com/google/gops v0.3.5
|
github.com/google/gops v0.3.5
|
||||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
|
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
|
||||||
@ -47,7 +48,6 @@ require (
|
|||||||
github.com/russross/blackfriday v2.0.0+incompatible
|
github.com/russross/blackfriday v2.0.0+incompatible
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
|
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
|
||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.3.0
|
github.com/sirupsen/logrus v1.3.0
|
||||||
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
|
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
|
||||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||||
@ -65,10 +65,8 @@ require (
|
|||||||
go.uber.org/atomic v1.3.2 // indirect
|
go.uber.org/atomic v1.3.2 // indirect
|
||||||
go.uber.org/multierr v1.1.0 // indirect
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
go.uber.org/zap v1.9.1 // indirect
|
go.uber.org/zap v1.9.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 // indirect
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3
|
||||||
)
|
)
|
||||||
|
32
go.sum
32
go.sum
@ -1,11 +1,17 @@
|
|||||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
|
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
|
||||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
|
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
||||||
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 h1:v/zr4ns/4sSahF9KBm4Uc933bLsEEv7LuT63CJ019yo=
|
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 h1:v/zr4ns/4sSahF9KBm4Uc933bLsEEv7LuT63CJ019yo=
|
||||||
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
|
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
|
||||||
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c h1:ldRXgMEfKmzBomrZusl3edG9AGEeztA7jovLEQy62us=
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c/go.mod h1:MSDmePOOkbFFbVW2WRRppBcbA+aabwpXRgyIIG7jDFQ=
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884 h1:2AxfzkQi2L4QGBvUCZoWD6hQuUJa5MG54wiYyNqJlf4=
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
|
||||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
|
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
|
||||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
|
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
@ -21,18 +27,22 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX
|
|||||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
|
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||||
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc h1:wdhDSKrkYy24mcfzuA3oYm58h0QkyXjwERCkzJDP5kA=
|
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc h1:wdhDSKrkYy24mcfzuA3oYm58h0QkyXjwERCkzJDP5kA=
|
||||||
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
|
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
|
||||||
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
|
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
|
||||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
|
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
|
||||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
|
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
|
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
|
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
|
||||||
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
@ -86,6 +96,7 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
|
|||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
@ -120,6 +131,7 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
|||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
||||||
@ -128,8 +140,13 @@ github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhO
|
|||||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
|
||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20171229120447-cf5f9fa2f0d8/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg=
|
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
||||||
@ -185,14 +202,23 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
|
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
|
||||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho=
|
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho=
|
||||||
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
|
||||||
|
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181212120007-b05ddf57801d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
@ -208,3 +234,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
|
maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI=
|
||||||
|
maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
|
||||||
|
maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM=
|
||||||
|
maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3 h1:A18t5Lp7I3aK0V7B7zdpb0hb/PBlu0X/Ai2AyU/XEk4=
|
||||||
|
maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3/go.mod h1:r5E3J4urDEsjfui9OYZYMLBfCliaAqcCwM2xeczta6k=
|
||||||
|
@ -1172,10 +1172,45 @@ StripNick=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowTopicChange=false
|
ShowTopicChange=false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#zulip section
|
#
|
||||||
|
# WhatsApp
|
||||||
|
#
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
||||||
|
[whatsapp.bridge]
|
||||||
|
|
||||||
|
# Number you will use as a relay bot. Tip: Get some disposable sim card, don't rely on your own number.
|
||||||
|
Number="+48111222333"
|
||||||
|
|
||||||
|
# First time that you login you will need to scan QR code, then credentials willl be saved in a session file
|
||||||
|
# If you won't set SessionFile then you will need to scan QR code on every restart
|
||||||
|
# optional (by default the session is stored only in memory, till restarting matterbridge)
|
||||||
|
SessionFile="session-48111222333.gob"
|
||||||
|
|
||||||
|
# If your terminal is white we need to invert QR code in order for it to be scanned properly
|
||||||
|
# optional (default false)
|
||||||
|
QrOnWhiteTerminal=true
|
||||||
|
|
||||||
|
# Messages will be seen by other WhatsApp contacts as coming from the bridge. Original nick will be part of the message.
|
||||||
|
RemoteNickFormat="@{NICK}: "
|
||||||
|
|
||||||
|
# extra label that can be used in the RemoteNickFormat
|
||||||
|
# optional (default empty)
|
||||||
|
Label="Organization"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
#
|
||||||
|
# zulip
|
||||||
|
#
|
||||||
|
###################################################################
|
||||||
|
|
||||||
[zulip]
|
[zulip]
|
||||||
|
|
||||||
#You can configure multiple servers "[zulip.name]" or "[zulip.name2]"
|
#You can configure multiple servers "[zulip.name]" or "[zulip.name2]"
|
||||||
#In this example we use [zulip.streamchat]
|
#In this example we use [zulip.streamchat]
|
||||||
#REQUIRED
|
#REQUIRED
|
||||||
@ -1380,6 +1415,7 @@ enable=true
|
|||||||
# account specified above
|
# account specified above
|
||||||
# REQUIRED
|
# REQUIRED
|
||||||
account="irc.freenode"
|
account="irc.freenode"
|
||||||
|
|
||||||
# channel to connect on that account
|
# channel to connect on that account
|
||||||
# How to specify them for the different bridges:
|
# How to specify them for the different bridges:
|
||||||
#
|
#
|
||||||
@ -1400,6 +1436,10 @@ enable=true
|
|||||||
# - encrypted rooms are not supported in matrix
|
# - encrypted rooms are not supported in matrix
|
||||||
# steam - chatid (a large number).
|
# steam - chatid (a large number).
|
||||||
# The number in the URL when you click "enter chat room" in the browser
|
# The number in the URL when you click "enter chat room" in the browser
|
||||||
|
# whatsapp - 48111222333-123455678999@g.us A unique group JID;
|
||||||
|
# if you specify an empty string bridge will list all the possibilities
|
||||||
|
# - "Group Name" if you specify a group name the bridge will hint its JID to specify
|
||||||
|
# as group names might change in time and contain weird emoticons
|
||||||
# zulip - stream (without the #)
|
# zulip - stream (without the #)
|
||||||
#
|
#
|
||||||
# REQUIRED
|
# REQUIRED
|
||||||
|
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
Normal file
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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
Normal file
29
vendor/github.com/Baozisoftware/qrcode-terminal-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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
Normal file
39
vendor/github.com/Baozisoftware/qrcode-terminal-go/README.md
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
# 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
|
||||||
|
![winxp](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/winxp.png)
|
||||||
|
### Windows 7
|
||||||
|
![win7](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/win7.png)
|
||||||
|
### Windows 10
|
||||||
|
![win10](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/win10.png)
|
||||||
|
### Ubuntu
|
||||||
|
![ubuntu](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/ubuntu.png)
|
||||||
|
### macOS
|
||||||
|
![macos](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/macos.png)
|
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
Normal file
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
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()
|
2
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
Normal file
2
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea/
|
||||||
|
docs/
|
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
Normal file
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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.
|
104
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
Normal file
104
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# 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.RestoreSession(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) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||||
|
fmt.Println(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (myHandler) HandleJsonMessage(message string) {
|
||||||
|
fmt.Println(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 and VideoMessage 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)
|
||||||
|
```
|
||||||
|
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
Normal file
388
vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go
generated
vendored
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
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
Normal file
351
vendor/github.com/Rhymen/go-whatsapp/binary/encoder.go
generated
vendored
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
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)
|
||||||
|
}
|
103
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
Normal file
103
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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 {
|
||||||
|
n.Content, err = unmarshalMessageArray(n.Content.([]Node))
|
||||||
|
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
|
||||||
|
}
|
3800
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
Normal file
3800
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
417
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
generated
vendored
Normal file
417
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
generated
vendored
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
package proto;
|
||||||
|
|
||||||
|
message FingerprintData {
|
||||||
|
optional string publicKey = 1;
|
||||||
|
optional string identifier = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CombinedFingerprint {
|
||||||
|
optional uint32 version = 1;
|
||||||
|
optional FingerprintData localFingerprint = 2;
|
||||||
|
optional FingerprintData remoteFingerprint = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageKey {
|
||||||
|
optional string remoteJid = 1;
|
||||||
|
optional bool fromMe = 2;
|
||||||
|
optional string id = 3;
|
||||||
|
optional string participant = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SenderKeyDistributionMessage {
|
||||||
|
optional string groupId = 1;
|
||||||
|
optional bytes axolotlSenderKeyDistributionMessage = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImageMessage {
|
||||||
|
optional string url = 1;
|
||||||
|
optional string mimetype = 2;
|
||||||
|
optional string caption = 3;
|
||||||
|
optional bytes fileSha256 = 4;
|
||||||
|
optional uint64 fileLength = 5;
|
||||||
|
optional uint32 height = 6;
|
||||||
|
optional uint32 width = 7;
|
||||||
|
optional bytes mediaKey = 8;
|
||||||
|
optional bytes fileEncSha256 = 9;
|
||||||
|
repeated InteractiveAnnotation interactiveAnnotations = 10;
|
||||||
|
optional string directPath = 11;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
optional bytes firstScanSidecar = 18;
|
||||||
|
optional uint32 firstScanLength = 19;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContactMessage {
|
||||||
|
optional string displayName = 1;
|
||||||
|
optional string vcard = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LocationMessage {
|
||||||
|
optional double degreesLatitude = 1;
|
||||||
|
optional double degreesLongitude = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
optional string address = 4;
|
||||||
|
optional string url = 5;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtendedTextMessage {
|
||||||
|
optional string text = 1;
|
||||||
|
optional string matchedText = 2;
|
||||||
|
optional string canonicalUrl = 4;
|
||||||
|
optional string description = 5;
|
||||||
|
optional string title = 6;
|
||||||
|
optional fixed32 textArgb = 7;
|
||||||
|
optional fixed32 backgroundArgb = 8;
|
||||||
|
enum FONTTYPE {
|
||||||
|
SANS_SERIF = 0;
|
||||||
|
SERIF = 1;
|
||||||
|
NORICAN_REGULAR = 2;
|
||||||
|
BRYNDAN_WRITE = 3;
|
||||||
|
BEBASNEUE_REGULAR = 4;
|
||||||
|
OSWALD_HEAVY = 5;
|
||||||
|
}
|
||||||
|
optional FONTTYPE font = 9;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DocumentMessage {
|
||||||
|
optional string url = 1;
|
||||||
|
optional string mimetype = 2;
|
||||||
|
optional string title = 3;
|
||||||
|
optional bytes fileSha256 = 4;
|
||||||
|
optional uint64 fileLength = 5;
|
||||||
|
optional uint32 pageCount = 6;
|
||||||
|
optional bytes mediaKey = 7;
|
||||||
|
optional string fileName = 8;
|
||||||
|
optional bytes fileEncSha256 = 9;
|
||||||
|
optional string directPath = 10;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AudioMessage {
|
||||||
|
optional string url = 1;
|
||||||
|
optional string mimetype = 2;
|
||||||
|
optional bytes fileSha256 = 3;
|
||||||
|
optional uint64 fileLength = 4;
|
||||||
|
optional uint32 seconds = 5;
|
||||||
|
optional bool ptt = 6;
|
||||||
|
optional bytes mediaKey = 7;
|
||||||
|
optional bytes fileEncSha256 = 8;
|
||||||
|
optional string directPath = 9;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
optional bytes streamingSidecar = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VideoMessage {
|
||||||
|
optional string url = 1;
|
||||||
|
optional string mimetype = 2;
|
||||||
|
optional bytes fileSha256 = 3;
|
||||||
|
optional uint64 fileLength = 4;
|
||||||
|
optional uint32 seconds = 5;
|
||||||
|
optional bytes mediaKey = 6;
|
||||||
|
optional string caption = 7;
|
||||||
|
optional bool gifPlayback = 8;
|
||||||
|
optional uint32 height = 9;
|
||||||
|
optional uint32 width = 10;
|
||||||
|
optional bytes fileEncSha256 = 11;
|
||||||
|
repeated InteractiveAnnotation interactiveAnnotations = 12;
|
||||||
|
optional string directPath = 13;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
optional bytes streamingSidecar = 18;
|
||||||
|
enum ATTRIBUTION {
|
||||||
|
NONE = 0;
|
||||||
|
GIPHY = 1;
|
||||||
|
TENOR = 2;
|
||||||
|
}
|
||||||
|
optional ATTRIBUTION gifAttribution = 19;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Call {
|
||||||
|
optional bytes callKey = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Chat {
|
||||||
|
optional string displayName = 1;
|
||||||
|
optional string id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ProtocolMessage {
|
||||||
|
optional MessageKey key = 1;
|
||||||
|
enum TYPE {
|
||||||
|
REVOKE = 0;
|
||||||
|
}
|
||||||
|
optional TYPE type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContactsArrayMessage {
|
||||||
|
optional string displayName = 1;
|
||||||
|
repeated ContactMessage contacts = 2;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HSMCurrency {
|
||||||
|
optional string currencyCode = 1;
|
||||||
|
optional int64 amount1000 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HSMDateTimeComponent {
|
||||||
|
enum DAYOFWEEKTYPE {
|
||||||
|
MONDAY = 1;
|
||||||
|
TUESDAY = 2;
|
||||||
|
WEDNESDAY = 3;
|
||||||
|
THURSDAY = 4;
|
||||||
|
FRIDAY = 5;
|
||||||
|
SATURDAY = 6;
|
||||||
|
SUNDAY = 7;
|
||||||
|
}
|
||||||
|
optional DAYOFWEEKTYPE dayOfWeek = 1;
|
||||||
|
optional uint32 year = 2;
|
||||||
|
optional uint32 month = 3;
|
||||||
|
optional uint32 dayOfMonth = 4;
|
||||||
|
optional uint32 hour = 5;
|
||||||
|
optional uint32 minute = 6;
|
||||||
|
enum CALENDARTYPE {
|
||||||
|
GREGORIAN = 1;
|
||||||
|
SOLAR_HIJRI = 2;
|
||||||
|
}
|
||||||
|
optional CALENDARTYPE calendar = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HSMDateTimeUnixEpoch {
|
||||||
|
optional int64 timestamp = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HSMDateTime {
|
||||||
|
oneof datetimeOneof {
|
||||||
|
HSMDateTimeComponent component = 1;
|
||||||
|
HSMDateTimeUnixEpoch unixEpoch = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message HSMLocalizableParameter {
|
||||||
|
optional string default = 1;
|
||||||
|
oneof paramOneof {
|
||||||
|
HSMCurrency currency = 2;
|
||||||
|
HSMDateTime dateTime = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message HighlyStructuredMessage {
|
||||||
|
optional string namespace = 1;
|
||||||
|
optional string elementName = 2;
|
||||||
|
repeated string params = 3;
|
||||||
|
optional string fallbackLg = 4;
|
||||||
|
optional string fallbackLc = 5;
|
||||||
|
repeated HSMLocalizableParameter localizableParams = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendPaymentMessage {
|
||||||
|
optional Message noteMessage = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestPaymentMessage {
|
||||||
|
optional string currencyCodeIso4217 = 1;
|
||||||
|
optional uint64 amount1000 = 2;
|
||||||
|
optional string requestFrom = 3;
|
||||||
|
optional Message noteMessage = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LiveLocationMessage {
|
||||||
|
optional double degreesLatitude = 1;
|
||||||
|
optional double degreesLongitude = 2;
|
||||||
|
optional uint32 accuracyInMeters = 3;
|
||||||
|
optional float speedInMps = 4;
|
||||||
|
optional uint32 degreesClockwiseFromMagneticNorth = 5;
|
||||||
|
optional string caption = 6;
|
||||||
|
optional int64 sequenceNumber = 7;
|
||||||
|
optional bytes jpegThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StickerMessage {
|
||||||
|
optional string url = 1;
|
||||||
|
optional bytes fileSha256 = 2;
|
||||||
|
optional bytes fileEncSha256 = 3;
|
||||||
|
optional bytes mediaKey = 4;
|
||||||
|
optional string mimetype = 5;
|
||||||
|
optional uint32 height = 6;
|
||||||
|
optional uint32 width = 7;
|
||||||
|
optional string directPath = 8;
|
||||||
|
optional uint64 fileLength = 9;
|
||||||
|
optional bytes pngThumbnail = 16;
|
||||||
|
optional ContextInfo contextInfo = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
optional string conversation = 1;
|
||||||
|
optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2;
|
||||||
|
optional ImageMessage imageMessage = 3;
|
||||||
|
optional ContactMessage contactMessage = 4;
|
||||||
|
optional LocationMessage locationMessage = 5;
|
||||||
|
optional ExtendedTextMessage extendedTextMessage = 6;
|
||||||
|
optional DocumentMessage documentMessage = 7;
|
||||||
|
optional AudioMessage audioMessage = 8;
|
||||||
|
optional VideoMessage videoMessage = 9;
|
||||||
|
optional Call call = 10;
|
||||||
|
optional Chat chat = 11;
|
||||||
|
optional ProtocolMessage protocolMessage = 12;
|
||||||
|
optional ContactsArrayMessage contactsArrayMessage = 13;
|
||||||
|
optional HighlyStructuredMessage highlyStructuredMessage = 14;
|
||||||
|
optional SenderKeyDistributionMessage fastRatchetKeySenderKeyDistributionMessage = 15;
|
||||||
|
optional SendPaymentMessage sendPaymentMessage = 16;
|
||||||
|
optional RequestPaymentMessage requestPaymentMessage = 17;
|
||||||
|
optional LiveLocationMessage liveLocationMessage = 18;
|
||||||
|
optional StickerMessage stickerMessage = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContextInfo {
|
||||||
|
optional string stanzaId = 1;
|
||||||
|
optional string participant = 2;
|
||||||
|
repeated Message quotedMessage = 3;
|
||||||
|
optional string remoteJid = 4;
|
||||||
|
repeated string mentionedJid = 15;
|
||||||
|
optional string conversionSource = 18;
|
||||||
|
optional bytes conversionData = 19;
|
||||||
|
optional uint32 conversionDelaySeconds = 20;
|
||||||
|
optional bool isForwarded = 22;
|
||||||
|
reserved 16, 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InteractiveAnnotation {
|
||||||
|
repeated Point polygonVertices = 1;
|
||||||
|
oneof action {
|
||||||
|
Location location = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Point {
|
||||||
|
optional double x = 3;
|
||||||
|
optional double y = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Location {
|
||||||
|
optional double degreesLatitude = 1;
|
||||||
|
optional double degreesLongitude = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WebMessageInfo {
|
||||||
|
required MessageKey key = 1;
|
||||||
|
optional Message message = 2;
|
||||||
|
optional uint64 messageTimestamp = 3;
|
||||||
|
enum STATUS {
|
||||||
|
ERROR = 0;
|
||||||
|
PENDING = 1;
|
||||||
|
SERVER_ACK = 2;
|
||||||
|
DELIVERY_ACK = 3;
|
||||||
|
READ = 4;
|
||||||
|
PLAYED = 5;
|
||||||
|
}
|
||||||
|
optional STATUS status = 4 [default=PENDING];
|
||||||
|
optional string participant = 5;
|
||||||
|
optional bool ignore = 16;
|
||||||
|
optional bool starred = 17;
|
||||||
|
optional bool broadcast = 18;
|
||||||
|
optional string pushName = 19;
|
||||||
|
optional bytes mediaCiphertextSha256 = 20;
|
||||||
|
optional bool multicast = 21;
|
||||||
|
optional bool urlText = 22;
|
||||||
|
optional bool urlNumber = 23;
|
||||||
|
enum STUBTYPE {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
REVOKE = 1;
|
||||||
|
CIPHERTEXT = 2;
|
||||||
|
FUTUREPROOF = 3;
|
||||||
|
NON_VERIFIED_TRANSITION = 4;
|
||||||
|
UNVERIFIED_TRANSITION = 5;
|
||||||
|
VERIFIED_TRANSITION = 6;
|
||||||
|
VERIFIED_LOW_UNKNOWN = 7;
|
||||||
|
VERIFIED_HIGH = 8;
|
||||||
|
VERIFIED_INITIAL_UNKNOWN = 9;
|
||||||
|
VERIFIED_INITIAL_LOW = 10;
|
||||||
|
VERIFIED_INITIAL_HIGH = 11;
|
||||||
|
VERIFIED_TRANSITION_ANY_TO_NONE = 12;
|
||||||
|
VERIFIED_TRANSITION_ANY_TO_HIGH = 13;
|
||||||
|
VERIFIED_TRANSITION_HIGH_TO_LOW = 14;
|
||||||
|
VERIFIED_TRANSITION_HIGH_TO_UNKNOWN = 15;
|
||||||
|
VERIFIED_TRANSITION_UNKNOWN_TO_LOW = 16;
|
||||||
|
VERIFIED_TRANSITION_LOW_TO_UNKNOWN = 17;
|
||||||
|
VERIFIED_TRANSITION_NONE_TO_LOW = 18;
|
||||||
|
VERIFIED_TRANSITION_NONE_TO_UNKNOWN = 19;
|
||||||
|
GROUP_CREATE = 20;
|
||||||
|
GROUP_CHANGE_SUBJECT = 21;
|
||||||
|
GROUP_CHANGE_ICON = 22;
|
||||||
|
GROUP_CHANGE_INVITE_LINK = 23;
|
||||||
|
GROUP_CHANGE_DESCRIPTION = 24;
|
||||||
|
GROUP_CHANGE_RESTRICT = 25;
|
||||||
|
GROUP_CHANGE_ANNOUNCE = 26;
|
||||||
|
GROUP_PARTICIPANT_ADD = 27;
|
||||||
|
GROUP_PARTICIPANT_REMOVE = 28;
|
||||||
|
GROUP_PARTICIPANT_PROMOTE = 29;
|
||||||
|
GROUP_PARTICIPANT_DEMOTE = 30;
|
||||||
|
GROUP_PARTICIPANT_INVITE = 31;
|
||||||
|
GROUP_PARTICIPANT_LEAVE = 32;
|
||||||
|
GROUP_PARTICIPANT_CHANGE_NUMBER = 33;
|
||||||
|
BROADCAST_CREATE = 34;
|
||||||
|
BROADCAST_ADD = 35;
|
||||||
|
BROADCAST_REMOVE = 36;
|
||||||
|
GENERIC_NOTIFICATION = 37;
|
||||||
|
E2E_IDENTITY_CHANGED = 38;
|
||||||
|
E2E_ENCRYPTED = 39;
|
||||||
|
CALL_MISSED_VOICE = 40;
|
||||||
|
CALL_MISSED_VIDEO = 41;
|
||||||
|
INDIVIDUAL_CHANGE_NUMBER = 42;
|
||||||
|
GROUP_DELETE = 43;
|
||||||
|
}
|
||||||
|
optional STUBTYPE messageStubType = 24;
|
||||||
|
optional bool clearMedia = 25;
|
||||||
|
repeated string messageStubParameters = 26;
|
||||||
|
optional uint32 duration = 27;
|
||||||
|
repeated string labels = 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WebNotificationsInfo {
|
||||||
|
optional uint64 timestamp = 2;
|
||||||
|
optional uint32 unreadChats = 3;
|
||||||
|
optional uint32 notifyMessageCount = 4;
|
||||||
|
repeated Message notifyMessages = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotificationMessageInfo {
|
||||||
|
optional MessageKey key = 1;
|
||||||
|
optional Message message = 2;
|
||||||
|
optional uint64 messageTimestamp = 3;
|
||||||
|
optional string participant = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TabletNotificationsInfo {
|
||||||
|
optional uint64 timestamp = 2;
|
||||||
|
optional uint32 unreadChats = 3;
|
||||||
|
optional uint32 notifyMessageCount = 4;
|
||||||
|
repeated Message notifyMessage = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WebFeatures {
|
||||||
|
enum FLAG {
|
||||||
|
NOT_IMPLEMENTED = 0;
|
||||||
|
IMPLEMENTED = 1;
|
||||||
|
OPTIONAL = 2;
|
||||||
|
}
|
||||||
|
optional FLAG labelsDisplay = 1;
|
||||||
|
optional FLAG voipIndividualOutgoing = 2;
|
||||||
|
optional FLAG groupsV3 = 3;
|
||||||
|
optional FLAG groupsV3Create = 4;
|
||||||
|
optional FLAG changeNumberV2 = 5;
|
||||||
|
optional FLAG queryStatusV3Thumbnail = 6;
|
||||||
|
optional FLAG liveLocations = 7;
|
||||||
|
optional FLAG queryVname = 8;
|
||||||
|
optional FLAG voipIndividualIncoming = 9;
|
||||||
|
optional FLAG quickRepliesQuery = 10;
|
||||||
|
}
|
78
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
Normal file
78
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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"}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
389
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
389
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
wsConn *websocket.Conn
|
||||||
|
wsConnOK bool
|
||||||
|
wsConnMutex sync.RWMutex
|
||||||
|
session *Session
|
||||||
|
listener map[string]chan string
|
||||||
|
listenerMutex sync.RWMutex
|
||||||
|
writeChan chan wsMsg
|
||||||
|
handler []Handler
|
||||||
|
msgCount int
|
||||||
|
msgTimeout time.Duration
|
||||||
|
Info *Info
|
||||||
|
Store *Store
|
||||||
|
ServerLastSeen time.Time
|
||||||
|
|
||||||
|
longClientName string
|
||||||
|
shortClientName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type wsMsg struct {
|
||||||
|
messageType int
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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) {
|
||||||
|
wac := &Conn{
|
||||||
|
wsConn: nil, // will be set in connect()
|
||||||
|
wsConnMutex: sync.RWMutex{},
|
||||||
|
listener: make(map[string]chan string),
|
||||||
|
listenerMutex: sync.RWMutex{},
|
||||||
|
writeChan: make(chan wsMsg),
|
||||||
|
handler: make([]Handler, 0),
|
||||||
|
msgCount: 0,
|
||||||
|
msgTimeout: timeout,
|
||||||
|
Store: newStore(),
|
||||||
|
|
||||||
|
longClientName: "github.com/rhymen/go-whatsapp",
|
||||||
|
shortClientName: "go-whatsapp",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wac.connect(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go wac.readPump()
|
||||||
|
go wac.writePump()
|
||||||
|
go wac.keepAlive(20000, 90000)
|
||||||
|
|
||||||
|
return wac, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) isConnected() bool {
|
||||||
|
wac.wsConnMutex.RLock()
|
||||||
|
defer wac.wsConnMutex.RUnlock()
|
||||||
|
if wac.wsConn == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if wac.wsConnOK {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// just send a keepalive to test the connection
|
||||||
|
wac.sendKeepAlive()
|
||||||
|
|
||||||
|
// this method is expected to be called by loops. So we can just return false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect should be guarded with wsConnMutex
|
||||||
|
func (wac *Conn) connect() error {
|
||||||
|
dialer := &websocket.Dialer{
|
||||||
|
ReadBufferSize: 25 * 1024 * 1024,
|
||||||
|
WriteBufferSize: 10 * 1024 * 1024,
|
||||||
|
HandshakeTimeout: wac.msgTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
||||||
|
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't dial whatsapp web websocket: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||||
|
fmt.Fprintf(os.Stderr, "websocket connection closed(%d, %s)\n", code, text)
|
||||||
|
|
||||||
|
// from default CloseHandler
|
||||||
|
message := websocket.FormatCloseMessage(code, "")
|
||||||
|
wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
||||||
|
|
||||||
|
// our close handling
|
||||||
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
|
||||||
|
fmt.Println("Trigger reconnect")
|
||||||
|
go wac.reconnect()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
wac.wsConn = wsConn
|
||||||
|
wac.wsConnOK = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconnect should be run as go routine
|
||||||
|
func (wac *Conn) reconnect() {
|
||||||
|
wac.wsConnMutex.Lock()
|
||||||
|
wac.wsConn.Close()
|
||||||
|
wac.wsConn = nil
|
||||||
|
wac.wsConnOK = false
|
||||||
|
wac.wsConnMutex.Unlock()
|
||||||
|
|
||||||
|
// wait up to 60 seconds and then reconnect. As writePump should send immediately, it might
|
||||||
|
// reconnect as well. So we check its existance before reconnecting
|
||||||
|
for !wac.isConnected() {
|
||||||
|
time.Sleep(time.Duration(rand.Intn(60)) * time.Second)
|
||||||
|
|
||||||
|
wac.wsConnMutex.Lock()
|
||||||
|
if wac.wsConn == nil {
|
||||||
|
if err := wac.connect(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "could not reconnect to websocket: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wac.wsConnMutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) write(data []interface{}) (<-chan string, error) {
|
||||||
|
d, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := time.Now().Unix()
|
||||||
|
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||||
|
msg := fmt.Sprintf("%s,%s", messageTag, d)
|
||||||
|
|
||||||
|
ch := make(chan string, 1)
|
||||||
|
|
||||||
|
wac.listenerMutex.Lock()
|
||||||
|
wac.listener[messageTag] = ch
|
||||||
|
wac.listenerMutex.Unlock()
|
||||||
|
|
||||||
|
wac.writeChan <- wsMsg{websocket.TextMessage, []byte(msg)}
|
||||||
|
|
||||||
|
wac.msgCount++
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, tag string) (<-chan string, error) {
|
||||||
|
if len(tag) < 2 {
|
||||||
|
return nil, fmt.Errorf("no tag specified or to short")
|
||||||
|
}
|
||||||
|
b, err := binary.Marshal(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, wac.session.MacKey)
|
||||||
|
h.Write(cipher)
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
|
||||||
|
data := []byte(tag + ",")
|
||||||
|
data = append(data, byte(metric), byte(flag))
|
||||||
|
data = append(data, hash[:32]...)
|
||||||
|
data = append(data, cipher...)
|
||||||
|
|
||||||
|
ch := make(chan string, 1)
|
||||||
|
|
||||||
|
wac.listenerMutex.Lock()
|
||||||
|
wac.listener[tag] = ch
|
||||||
|
wac.listenerMutex.Unlock()
|
||||||
|
|
||||||
|
msg := wsMsg{websocket.BinaryMessage, data}
|
||||||
|
wac.writeChan <- msg
|
||||||
|
|
||||||
|
wac.msgCount++
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) readPump() {
|
||||||
|
defer wac.wsConn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
msgType, msg, err := wac.wsConn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
wac.wsConnOK = false
|
||||||
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||||
|
wac.handle(fmt.Errorf("unexpected websocket close: %v", err))
|
||||||
|
}
|
||||||
|
// sleep for a second and retry reading the next message
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wac.wsConnOK = true
|
||||||
|
|
||||||
|
data := strings.SplitN(string(msg), ",", 2)
|
||||||
|
|
||||||
|
//Kepp-Alive Timestmap
|
||||||
|
if data[0][0] == '!' {
|
||||||
|
msecs, err := strconv.ParseInt(data[0][1:], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error converting time string to uint: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.listenerMutex.RLock()
|
||||||
|
listener, hasListener := wac.listener[data[0]]
|
||||||
|
wac.listenerMutex.RUnlock()
|
||||||
|
|
||||||
|
if len(data[1]) == 0 {
|
||||||
|
continue
|
||||||
|
} else if hasListener {
|
||||||
|
listener <- data[1]
|
||||||
|
|
||||||
|
wac.listenerMutex.Lock()
|
||||||
|
delete(wac.listener, data[0])
|
||||||
|
wac.listenerMutex.Unlock()
|
||||||
|
} else if msgType == 2 && wac.session != nil && wac.session.EncKey != nil {
|
||||||
|
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
||||||
|
if err != nil {
|
||||||
|
wac.handle(fmt.Errorf("error decoding binary: %v", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.dispatch(message)
|
||||||
|
} else {
|
||||||
|
wac.handle(string(data[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) writePump() {
|
||||||
|
for msg := range wac.writeChan {
|
||||||
|
for !wac.isConnected() {
|
||||||
|
// reconnect to send the message ASAP
|
||||||
|
wac.wsConnMutex.Lock()
|
||||||
|
if wac.wsConn == nil {
|
||||||
|
if err := wac.connect(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "could not reconnect to websocket: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wac.wsConnMutex.Unlock()
|
||||||
|
if !wac.isConnected() {
|
||||||
|
// reconnecting failed. Sleep for a while and try again afterwards
|
||||||
|
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := wac.wsConn.WriteMessage(msg.messageType, msg.data); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error writing to socket: %v\n", err)
|
||||||
|
wac.wsConnOK = false
|
||||||
|
// add message to channel again to no loose it
|
||||||
|
go func() {
|
||||||
|
wac.writeChan <- msg
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) sendKeepAlive() {
|
||||||
|
// whatever issues might be there allow sending this message
|
||||||
|
wac.wsConnOK = true
|
||||||
|
wac.writeChan <- wsMsg{
|
||||||
|
messageType: websocket.TextMessage,
|
||||||
|
data: []byte("?,,"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
||||||
|
for {
|
||||||
|
wac.sendKeepAlive()
|
||||||
|
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
||||||
|
<-time.After(time.Duration(interval) * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
||||||
|
//message validation
|
||||||
|
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||||
|
h2.Write([]byte(msg[32:]))
|
||||||
|
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
||||||
|
return nil, fmt.Errorf("message received with invalid hmac")
|
||||||
|
}
|
||||||
|
|
||||||
|
// message decrypt
|
||||||
|
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decrypting message with AES: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// message unmarshal
|
||||||
|
message, err := binary.Unmarshal(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decoding binary: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return message, nil
|
||||||
|
}
|
267
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
Normal file
267
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Presence string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PresenceAvailable = "available"
|
||||||
|
PresenceUnavailable = "unavailable"
|
||||||
|
PresenceComposing = "composing"
|
||||||
|
PresenceRecording = "recording"
|
||||||
|
PresencePaused = "paused"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
||||||
|
//TODO: functions probably shouldn't return a string, maybe build a struct / return json
|
||||||
|
//TODO: check for further queries
|
||||||
|
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
||||||
|
data := []interface{}{"query", "ProfilePicThumb", jid}
|
||||||
|
return wac.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
||||||
|
data := []interface{}{"query", "Status", jid}
|
||||||
|
return wac.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
||||||
|
data := []interface{}{"query", "GroupMetadata", jid}
|
||||||
|
return wac.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
||||||
|
data := []interface{}{"action", "presence", "subscribe", jid}
|
||||||
|
return wac.write(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) 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) 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.write(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, err := wac.writeBinary(n, group, 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
|
||||||
|
}
|
101
vendor/github.com/Rhymen/go-whatsapp/crypto/cbc/cbc.go
generated
vendored
Normal file
101
vendor/github.com/Rhymen/go-whatsapp/crypto/cbc/cbc.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
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
Normal file
44
vendor/github.com/Rhymen/go-whatsapp/crypto/curve25519/curve.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
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[:]
|
||||||
|
}
|
52
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
Normal file
52
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
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/hmac"
|
||||||
|
"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) {
|
||||||
|
if info == "" {
|
||||||
|
keyBlock := hmac.New(sha256.New, key)
|
||||||
|
var out, last []byte
|
||||||
|
|
||||||
|
var blockIndex byte = 1
|
||||||
|
for i := 0; len(out) < length; i++ {
|
||||||
|
keyBlock.Reset()
|
||||||
|
//keyBlock.Write(append(append(last, []byte(info)...), blockIndex))
|
||||||
|
keyBlock.Write(last)
|
||||||
|
keyBlock.Write([]byte(info))
|
||||||
|
keyBlock.Write([]byte{blockIndex})
|
||||||
|
last = keyBlock.Sum(nil)
|
||||||
|
blockIndex += 1
|
||||||
|
out = append(out, last...)
|
||||||
|
}
|
||||||
|
return out[:length], nil
|
||||||
|
} else {
|
||||||
|
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[:length], nil
|
||||||
|
}
|
||||||
|
}
|
8
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
8
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/Rhymen/go-whatsapp
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.2.0
|
||||||
|
github.com/gorilla/websocket v1.4.0
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||||
|
)
|
4
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
4
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
169
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
Normal file
169
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) handle(message interface{}) {
|
||||||
|
switch m := message.(type) {
|
||||||
|
case error:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
go h.HandleError(m)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(JsonMessageHandler); ok {
|
||||||
|
go x.HandleJsonMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case TextMessage:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(TextMessageHandler); ok {
|
||||||
|
go x.HandleTextMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ImageMessage:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(ImageMessageHandler); ok {
|
||||||
|
go x.HandleImageMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case VideoMessage:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(VideoMessageHandler); ok {
|
||||||
|
go x.HandleVideoMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case AudioMessage:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(AudioMessageHandler); ok {
|
||||||
|
go x.HandleAudioMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case DocumentMessage:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(DocumentMessageHandler); ok {
|
||||||
|
go x.HandleDocumentMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *proto.WebMessageInfo:
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(RawMessageHandler); ok {
|
||||||
|
go x.HandleRawMessage(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
||||||
|
wac.updateContacts(message.Content)
|
||||||
|
}
|
||||||
|
case error:
|
||||||
|
wac.handle(message)
|
||||||
|
case string:
|
||||||
|
wac.handle(message)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "unknown type in dipatcher chan: %T", msg)
|
||||||
|
}
|
||||||
|
}
|
199
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
Normal file
199
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return nil, nil, fmt.Errorf("download failed")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.ContentLength <= 10 {
|
||||||
|
return nil, nil, fmt.Errorf("file to short")
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
n := len(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return data[:n-10], data[n-10 : n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url 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)
|
||||||
|
|
||||||
|
var filetype string
|
||||||
|
switch appInfo {
|
||||||
|
case MediaImage:
|
||||||
|
filetype = "image"
|
||||||
|
case MediaAudio:
|
||||||
|
filetype = "audio"
|
||||||
|
case MediaDocument:
|
||||||
|
filetype = "document"
|
||||||
|
case MediaVideo:
|
||||||
|
filetype = "video"
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
|
||||||
|
ch, err := wac.write(uploadReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp map[string]interface{}
|
||||||
|
select {
|
||||||
|
case r := <-ch:
|
||||||
|
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||||
|
return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v\n", err)
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(resp["status"].(float64)) != 200 {
|
||||||
|
return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := multipart.NewWriter(&b)
|
||||||
|
hashWriter, err := w.CreateFormField("hash")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
|
||||||
|
|
||||||
|
fileWriter, err := w.CreateFormFile("file", "blob")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
|
||||||
|
err = w.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", resp["url"].(string), &b)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, nil, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", w.FormDataContentType())
|
||||||
|
req.Header.Set("Origin", "https://web.whatsapp.com")
|
||||||
|
req.Header.Set("Referer", "https://web.whatsapp.com/")
|
||||||
|
|
||||||
|
req.URL.Query().Set("f", "j")
|
||||||
|
|
||||||
|
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
|
||||||
|
json.NewDecoder(res.Body).Decode(&jsonRes)
|
||||||
|
|
||||||
|
return jsonRes["url"], mediaKey, fileEncSha256, fileSha256, fileLength, nil
|
||||||
|
}
|
455
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
Normal file
455
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MediaImage MediaType = "WhatsApp Image Keys"
|
||||||
|
MediaVideo MediaType = "WhatsApp Video Keys"
|
||||||
|
MediaAudio MediaType = "WhatsApp Audio Keys"
|
||||||
|
MediaDocument MediaType = "WhatsApp Document Keys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (wac *Conn) Send(msg interface{}) error {
|
||||||
|
var err error
|
||||||
|
var ch <-chan string
|
||||||
|
|
||||||
|
switch m := msg.(type) {
|
||||||
|
case *proto.WebMessageInfo:
|
||||||
|
ch, err = wac.sendProto(m)
|
||||||
|
case TextMessage:
|
||||||
|
ch, err = wac.sendProto(getTextProto(m))
|
||||||
|
case ImageMessage:
|
||||||
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("image upload failed: %v", err)
|
||||||
|
}
|
||||||
|
ch, err = wac.sendProto(getImageProto(m))
|
||||||
|
case VideoMessage:
|
||||||
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("video upload failed: %v", err)
|
||||||
|
}
|
||||||
|
ch, err = wac.sendProto(getVideoProto(m))
|
||||||
|
case DocumentMessage:
|
||||||
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("document upload failed: %v", err)
|
||||||
|
}
|
||||||
|
ch, err = wac.sendProto(getDocumentProto(m))
|
||||||
|
case AudioMessage:
|
||||||
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("audio upload failed: %v", err)
|
||||||
|
}
|
||||||
|
ch, err = wac.sendProto(getAudioProto(m))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot match type %T, use message types declared in the package", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not send proto: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case response := <-ch:
|
||||||
|
var resp map[string]interface{}
|
||||||
|
if err = json.Unmarshal([]byte(response), &resp); err != nil {
|
||||||
|
return fmt.Errorf("error decoding sending response: %v\n", err)
|
||||||
|
}
|
||||||
|
if int(resp["status"].(float64)) != 200 {
|
||||||
|
return fmt.Errorf("message sending responded with %d", resp["status"])
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
return fmt.Errorf("sending message timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) {
|
||||||
|
n := binary.Node{
|
||||||
|
Description: "action",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"type": "relay",
|
||||||
|
"epoch": strconv.Itoa(wac.msgCount),
|
||||||
|
},
|
||||||
|
Content: []interface{}{p},
|
||||||
|
}
|
||||||
|
return wac.writeBinary(n, message, ignore, p.Key.GetId())
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MessageInfo contains general message information. It is part of every of every message type.
|
||||||
|
*/
|
||||||
|
type MessageInfo struct {
|
||||||
|
Id string
|
||||||
|
RemoteJid string
|
||||||
|
SenderJid string
|
||||||
|
FromMe bool
|
||||||
|
Timestamp uint64
|
||||||
|
PushName string
|
||||||
|
Status MessageStatus
|
||||||
|
QuotedMessageID string
|
||||||
|
|
||||||
|
Source *proto.WebMessageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Error MessageStatus = 0
|
||||||
|
Pending = 1
|
||||||
|
ServerAck = 2
|
||||||
|
DeliveryAck = 3
|
||||||
|
Read = 4
|
||||||
|
Played = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func getMessageInfo(msg *proto.WebMessageInfo) MessageInfo {
|
||||||
|
return MessageInfo{
|
||||||
|
Id: msg.GetKey().GetId(),
|
||||||
|
RemoteJid: msg.GetKey().GetRemoteJid(),
|
||||||
|
SenderJid: msg.GetKey().GetParticipant(),
|
||||||
|
FromMe: msg.GetKey().GetFromMe(),
|
||||||
|
Timestamp: msg.GetMessageTimestamp(),
|
||||||
|
Status: MessageStatus(msg.GetStatus()),
|
||||||
|
PushName: msg.GetPushName(),
|
||||||
|
Source: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInfoProto(info *MessageInfo) *proto.WebMessageInfo {
|
||||||
|
if info.Id == "" || len(info.Id) < 2 {
|
||||||
|
b := make([]byte, 10)
|
||||||
|
rand.Read(b)
|
||||||
|
info.Id = strings.ToUpper(hex.EncodeToString(b))
|
||||||
|
}
|
||||||
|
if info.Timestamp == 0 {
|
||||||
|
info.Timestamp = uint64(time.Now().Unix())
|
||||||
|
}
|
||||||
|
info.FromMe = true
|
||||||
|
|
||||||
|
status := proto.WebMessageInfo_STATUS(info.Status)
|
||||||
|
|
||||||
|
return &proto.WebMessageInfo{
|
||||||
|
Key: &proto.MessageKey{
|
||||||
|
FromMe: &info.FromMe,
|
||||||
|
RemoteJid: &info.RemoteJid,
|
||||||
|
Id: &info.Id,
|
||||||
|
},
|
||||||
|
MessageTimestamp: &info.Timestamp,
|
||||||
|
Status: &status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TextMessage represents a text message.
|
||||||
|
*/
|
||||||
|
type TextMessage struct {
|
||||||
|
Info MessageInfo
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTextMessage(msg *proto.WebMessageInfo) TextMessage {
|
||||||
|
text := TextMessage{Info: getMessageInfo(msg)}
|
||||||
|
if m := msg.GetMessage().GetExtendedTextMessage(); m != nil {
|
||||||
|
text.Text = m.GetText()
|
||||||
|
text.Info.QuotedMessageID = m.GetContextInfo().GetStanzaId()
|
||||||
|
} else {
|
||||||
|
text.Text = msg.GetMessage().GetConversation()
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTextProto(msg TextMessage) *proto.WebMessageInfo {
|
||||||
|
p := getInfoProto(&msg.Info)
|
||||||
|
p.Message = &proto.Message{
|
||||||
|
Conversation: &msg.Text,
|
||||||
|
}
|
||||||
|
if msg.Info.QuotedMessageID != "" {
|
||||||
|
previousText := "previous"
|
||||||
|
p.Message.ExtendedTextMessage = &proto.ExtendedTextMessage{
|
||||||
|
ContextInfo: &proto.ContextInfo{
|
||||||
|
StanzaId: &msg.Info.QuotedMessageID,
|
||||||
|
QuotedMessage: []*proto.Message{
|
||||||
|
&proto.Message{
|
||||||
|
Conversation: &previousText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ImageMessage represents a image message. Unexported fields are needed for media up/downloading and media validation.
|
||||||
|
Provide a io.Reader as Content for message sending.
|
||||||
|
*/
|
||||||
|
type ImageMessage struct {
|
||||||
|
Info MessageInfo
|
||||||
|
Caption string
|
||||||
|
Thumbnail []byte
|
||||||
|
Type string
|
||||||
|
Content io.Reader
|
||||||
|
url string
|
||||||
|
mediaKey []byte
|
||||||
|
fileEncSha256 []byte
|
||||||
|
fileSha256 []byte
|
||||||
|
fileLength uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImageMessage(msg *proto.WebMessageInfo) ImageMessage {
|
||||||
|
image := msg.GetMessage().GetImageMessage()
|
||||||
|
return ImageMessage{
|
||||||
|
Info: getMessageInfo(msg),
|
||||||
|
Caption: image.GetCaption(),
|
||||||
|
Thumbnail: image.GetJpegThumbnail(),
|
||||||
|
url: image.GetUrl(),
|
||||||
|
mediaKey: image.GetMediaKey(),
|
||||||
|
Type: image.GetMimetype(),
|
||||||
|
fileEncSha256: image.GetFileEncSha256(),
|
||||||
|
fileSha256: image.GetFileSha256(),
|
||||||
|
fileLength: image.GetFileLength(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImageProto(msg ImageMessage) *proto.WebMessageInfo {
|
||||||
|
p := getInfoProto(&msg.Info)
|
||||||
|
p.Message = &proto.Message{
|
||||||
|
ImageMessage: &proto.ImageMessage{
|
||||||
|
Caption: &msg.Caption,
|
||||||
|
JpegThumbnail: msg.Thumbnail,
|
||||||
|
Url: &msg.url,
|
||||||
|
MediaKey: msg.mediaKey,
|
||||||
|
Mimetype: &msg.Type,
|
||||||
|
FileEncSha256: msg.fileEncSha256,
|
||||||
|
FileSha256: msg.fileSha256,
|
||||||
|
FileLength: &msg.fileLength,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Download is the function to retrieve media data. The media gets downloaded, validated and returned.
|
||||||
|
*/
|
||||||
|
func (m *ImageMessage) Download() ([]byte, error) {
|
||||||
|
return Download(m.url, m.mediaKey, MediaImage, int(m.fileLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
VideoMessage represents a video message. Unexported fields are needed for media up/downloading and media validation.
|
||||||
|
Provide a io.Reader as Content for message sending.
|
||||||
|
*/
|
||||||
|
type VideoMessage struct {
|
||||||
|
Info MessageInfo
|
||||||
|
Caption string
|
||||||
|
Thumbnail []byte
|
||||||
|
Length uint32
|
||||||
|
Type string
|
||||||
|
Content io.Reader
|
||||||
|
url string
|
||||||
|
mediaKey []byte
|
||||||
|
fileEncSha256 []byte
|
||||||
|
fileSha256 []byte
|
||||||
|
fileLength uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage {
|
||||||
|
vid := msg.GetMessage().GetVideoMessage()
|
||||||
|
return VideoMessage{
|
||||||
|
Info: getMessageInfo(msg),
|
||||||
|
Caption: vid.GetCaption(),
|
||||||
|
Thumbnail: vid.GetJpegThumbnail(),
|
||||||
|
url: vid.GetUrl(),
|
||||||
|
mediaKey: vid.GetMediaKey(),
|
||||||
|
Length: vid.GetSeconds(),
|
||||||
|
Type: vid.GetMimetype(),
|
||||||
|
fileEncSha256: vid.GetFileEncSha256(),
|
||||||
|
fileSha256: vid.GetFileSha256(),
|
||||||
|
fileLength: vid.GetFileLength(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVideoProto(msg VideoMessage) *proto.WebMessageInfo {
|
||||||
|
p := getInfoProto(&msg.Info)
|
||||||
|
p.Message = &proto.Message{
|
||||||
|
VideoMessage: &proto.VideoMessage{
|
||||||
|
Caption: &msg.Caption,
|
||||||
|
JpegThumbnail: msg.Thumbnail,
|
||||||
|
Url: &msg.url,
|
||||||
|
MediaKey: msg.mediaKey,
|
||||||
|
Seconds: &msg.Length,
|
||||||
|
FileEncSha256: msg.fileEncSha256,
|
||||||
|
FileSha256: msg.fileSha256,
|
||||||
|
FileLength: &msg.fileLength,
|
||||||
|
Mimetype: &msg.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Download is the function to retrieve media data. The media gets downloaded, validated and returned.
|
||||||
|
*/
|
||||||
|
func (m *VideoMessage) Download() ([]byte, error) {
|
||||||
|
return Download(m.url, m.mediaKey, MediaVideo, int(m.fileLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AudioMessage represents a audio message. Unexported fields are needed for media up/downloading and media validation.
|
||||||
|
Provide a io.Reader as Content for message sending.
|
||||||
|
*/
|
||||||
|
type AudioMessage struct {
|
||||||
|
Info MessageInfo
|
||||||
|
Length uint32
|
||||||
|
Type string
|
||||||
|
Content io.Reader
|
||||||
|
url string
|
||||||
|
mediaKey []byte
|
||||||
|
fileEncSha256 []byte
|
||||||
|
fileSha256 []byte
|
||||||
|
fileLength uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage {
|
||||||
|
aud := msg.GetMessage().GetAudioMessage()
|
||||||
|
return AudioMessage{
|
||||||
|
Info: getMessageInfo(msg),
|
||||||
|
url: aud.GetUrl(),
|
||||||
|
mediaKey: aud.GetMediaKey(),
|
||||||
|
Length: aud.GetSeconds(),
|
||||||
|
Type: aud.GetMimetype(),
|
||||||
|
fileEncSha256: aud.GetFileEncSha256(),
|
||||||
|
fileSha256: aud.GetFileSha256(),
|
||||||
|
fileLength: aud.GetFileLength(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAudioProto(msg AudioMessage) *proto.WebMessageInfo {
|
||||||
|
p := getInfoProto(&msg.Info)
|
||||||
|
p.Message = &proto.Message{
|
||||||
|
AudioMessage: &proto.AudioMessage{
|
||||||
|
Url: &msg.url,
|
||||||
|
MediaKey: msg.mediaKey,
|
||||||
|
Seconds: &msg.Length,
|
||||||
|
FileEncSha256: msg.fileEncSha256,
|
||||||
|
FileSha256: msg.fileSha256,
|
||||||
|
FileLength: &msg.fileLength,
|
||||||
|
Mimetype: &msg.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Download is the function to retrieve media data. The media gets downloaded, validated and returned.
|
||||||
|
*/
|
||||||
|
func (m *AudioMessage) Download() ([]byte, error) {
|
||||||
|
return Download(m.url, m.mediaKey, MediaAudio, int(m.fileLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DocumentMessage represents a document message. Unexported fields are needed for media up/downloading and media
|
||||||
|
validation. Provide a io.Reader as Content for message sending.
|
||||||
|
*/
|
||||||
|
type DocumentMessage struct {
|
||||||
|
Info MessageInfo
|
||||||
|
Title string
|
||||||
|
PageCount uint32
|
||||||
|
Type string
|
||||||
|
Thumbnail []byte
|
||||||
|
Content io.Reader
|
||||||
|
url string
|
||||||
|
mediaKey []byte
|
||||||
|
fileEncSha256 []byte
|
||||||
|
fileSha256 []byte
|
||||||
|
fileLength uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage {
|
||||||
|
doc := msg.GetMessage().GetDocumentMessage()
|
||||||
|
return DocumentMessage{
|
||||||
|
Info: getMessageInfo(msg),
|
||||||
|
Thumbnail: doc.GetJpegThumbnail(),
|
||||||
|
url: doc.GetUrl(),
|
||||||
|
mediaKey: doc.GetMediaKey(),
|
||||||
|
fileEncSha256: doc.GetFileEncSha256(),
|
||||||
|
fileSha256: doc.GetFileSha256(),
|
||||||
|
fileLength: doc.GetFileLength(),
|
||||||
|
PageCount: doc.GetPageCount(),
|
||||||
|
Title: doc.GetTitle(),
|
||||||
|
Type: doc.GetMimetype(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo {
|
||||||
|
p := getInfoProto(&msg.Info)
|
||||||
|
p.Message = &proto.Message{
|
||||||
|
DocumentMessage: &proto.DocumentMessage{
|
||||||
|
JpegThumbnail: msg.Thumbnail,
|
||||||
|
Url: &msg.url,
|
||||||
|
MediaKey: msg.mediaKey,
|
||||||
|
FileEncSha256: msg.fileEncSha256,
|
||||||
|
FileSha256: msg.fileSha256,
|
||||||
|
FileLength: &msg.fileLength,
|
||||||
|
PageCount: &msg.PageCount,
|
||||||
|
Title: &msg.Title,
|
||||||
|
Mimetype: &msg.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Download is the function to retrieve media data. The media gets downloaded, validated and returned.
|
||||||
|
*/
|
||||||
|
func (m *DocumentMessage) Download() ([]byte, error) {
|
||||||
|
return Download(m.url, m.mediaKey, MediaDocument, int(m.fileLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProtoMessage(msg *proto.WebMessageInfo) interface{} {
|
||||||
|
switch {
|
||||||
|
|
||||||
|
case msg.GetMessage().GetAudioMessage() != nil:
|
||||||
|
return getAudioMessage(msg)
|
||||||
|
|
||||||
|
case msg.GetMessage().GetImageMessage() != nil:
|
||||||
|
return getImageMessage(msg)
|
||||||
|
|
||||||
|
case msg.GetMessage().GetVideoMessage() != nil:
|
||||||
|
return getVideoMessage(msg)
|
||||||
|
|
||||||
|
case msg.GetMessage().GetDocumentMessage() != nil:
|
||||||
|
return getDocumentMessage(msg)
|
||||||
|
|
||||||
|
case msg.GetMessage().GetConversation() != "":
|
||||||
|
return getTextMessage(msg)
|
||||||
|
|
||||||
|
case msg.GetMessage().GetExtendedTextMessage() != nil:
|
||||||
|
return getTextMessage(msg)
|
||||||
|
|
||||||
|
default:
|
||||||
|
//cannot match message
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
389
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
Normal file
389
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/curve25519"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 RestoreSession 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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) 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 = long, short
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 RestoreSession 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{}
|
||||||
|
|
||||||
|
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)
|
||||||
|
//oldVersion=8691
|
||||||
|
login := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||||
|
loginChan, err := wac.write(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := resp["ref"].(string)
|
||||||
|
|
||||||
|
priv, pub, err := curve25519.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
return session, fmt.Errorf("error generating keys: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//listener for Login response
|
||||||
|
messageTag := "s1"
|
||||||
|
wac.listener[messageTag] = make(chan string, 1)
|
||||||
|
|
||||||
|
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
||||||
|
|
||||||
|
var resp2 []interface{}
|
||||||
|
select {
|
||||||
|
case r1 := <-wac.listener[messageTag]:
|
||||||
|
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
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RestoreSession 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) RestoreSession(session Session) (Session, error) {
|
||||||
|
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||||
|
return Session{}, fmt.Errorf("already logged in")
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.session = &session
|
||||||
|
|
||||||
|
//listener for Conn or challenge; s1 is not allowed to drop
|
||||||
|
wac.listener["s1"] = make(chan string, 1)
|
||||||
|
|
||||||
|
//admin init
|
||||||
|
init := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||||
|
initChan, err := wac.write(init)
|
||||||
|
if err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error writing admin init: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//admin login with takeover
|
||||||
|
login := []interface{}{"admin", "login", session.ClientToken, session.ServerToken, session.ClientId, "takeover"}
|
||||||
|
loginChan, err := wac.write(login)
|
||||||
|
if err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, 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 {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(resp["status"].(float64)) != 200 {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("init responded with %d", resp["status"])
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("restore session init timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
//wait for s1
|
||||||
|
var connResp []interface{}
|
||||||
|
select {
|
||||||
|
case r1 := <-wac.listener["s1"]:
|
||||||
|
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error decoding s1 message: %v\n", err)
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, 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" {
|
||||||
|
wac.listener["s2"] = make(chan string, 1)
|
||||||
|
|
||||||
|
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error resolving challenge: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case r := <-wac.listener["s2"]:
|
||||||
|
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error decoding s2 message: %v\n", err)
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, 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.session = nil
|
||||||
|
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(resp["status"].(float64)) != 200 {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("admin login responded with %d", resp["status"])
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, fmt.Errorf("restore session login timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
info := connResp[1].(map[string]interface{})
|
||||||
|
|
||||||
|
wac.Info = newInfoFromReq(info)
|
||||||
|
|
||||||
|
//set new tokens
|
||||||
|
session.ClientToken = info["clientToken"].(string)
|
||||||
|
session.ServerToken = info["serverToken"].(string)
|
||||||
|
session.Wid = info["wid"].(string)
|
||||||
|
|
||||||
|
return *wac.session, 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.write(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.write(login)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error writing logout: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
45
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
45
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
Contacts map[string]Contact
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
Jid string
|
||||||
|
Notify string
|
||||||
|
Name string
|
||||||
|
Short string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStore() *Store {
|
||||||
|
return &Store{
|
||||||
|
make(map[string]Contact),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
3
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
@ -1,7 +1,4 @@
|
|||||||
Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
|
|
||||||
Copyright 2010 The Go Authors. All rights reserved.
|
Copyright 2010 The Go Authors. All rights reserved.
|
||||||
https://github.com/golang/protobuf
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
43
vendor/github.com/golang/protobuf/proto/Makefile
generated
vendored
43
vendor/github.com/golang/protobuf/proto/Makefile
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
# Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
#
|
|
||||||
# Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
# https://github.com/golang/protobuf
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
install:
|
|
||||||
go install
|
|
||||||
|
|
||||||
test: install generate-test-pbs
|
|
||||||
go test
|
|
||||||
|
|
||||||
|
|
||||||
generate-test-pbs:
|
|
||||||
make install
|
|
||||||
make -C testdata
|
|
||||||
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
|
|
||||||
make
|
|
46
vendor/github.com/golang/protobuf/proto/clone.go
generated
vendored
46
vendor/github.com/golang/protobuf/proto/clone.go
generated
vendored
@ -35,22 +35,39 @@
|
|||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Clone returns a deep copy of a protocol buffer.
|
// Clone returns a deep copy of a protocol buffer.
|
||||||
func Clone(pb Message) Message {
|
func Clone(src Message) Message {
|
||||||
in := reflect.ValueOf(pb)
|
in := reflect.ValueOf(src)
|
||||||
if in.IsNil() {
|
if in.IsNil() {
|
||||||
return pb
|
return src
|
||||||
|
}
|
||||||
|
out := reflect.New(in.Type().Elem())
|
||||||
|
dst := out.Interface().(Message)
|
||||||
|
Merge(dst, src)
|
||||||
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
out := reflect.New(in.Type().Elem())
|
// Merger is the interface representing objects that can merge messages of the same type.
|
||||||
// out is empty so a merge is a deep copy.
|
type Merger interface {
|
||||||
mergeStruct(out.Elem(), in.Elem())
|
// Merge merges src into this message.
|
||||||
return out.Interface().(Message)
|
// Required and optional fields that are set in src will be set to that value in dst.
|
||||||
|
// Elements of repeated fields will be appended.
|
||||||
|
//
|
||||||
|
// Merge may panic if called with a different argument type than the receiver.
|
||||||
|
Merge(src Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatedMerger is the custom merge method that generated protos will have.
|
||||||
|
// We must add this method since a generate Merge method will conflict with
|
||||||
|
// many existing protos that have a Merge data field already defined.
|
||||||
|
type generatedMerger interface {
|
||||||
|
XXX_Merge(src Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge merges src into dst.
|
// Merge merges src into dst.
|
||||||
@ -58,17 +75,24 @@ func Clone(pb Message) Message {
|
|||||||
// Elements of repeated fields will be appended.
|
// Elements of repeated fields will be appended.
|
||||||
// Merge panics if src and dst are not the same type, or if dst is nil.
|
// Merge panics if src and dst are not the same type, or if dst is nil.
|
||||||
func Merge(dst, src Message) {
|
func Merge(dst, src Message) {
|
||||||
|
if m, ok := dst.(Merger); ok {
|
||||||
|
m.Merge(src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
in := reflect.ValueOf(src)
|
in := reflect.ValueOf(src)
|
||||||
out := reflect.ValueOf(dst)
|
out := reflect.ValueOf(dst)
|
||||||
if out.IsNil() {
|
if out.IsNil() {
|
||||||
panic("proto: nil destination")
|
panic("proto: nil destination")
|
||||||
}
|
}
|
||||||
if in.Type() != out.Type() {
|
if in.Type() != out.Type() {
|
||||||
// Explicit test prior to mergeStruct so that mistyped nils will fail
|
panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src))
|
||||||
panic("proto: type mismatch")
|
|
||||||
}
|
}
|
||||||
if in.IsNil() {
|
if in.IsNil() {
|
||||||
// Merging nil into non-nil is a quiet no-op
|
return // Merge from nil src is a noop
|
||||||
|
}
|
||||||
|
if m, ok := dst.(generatedMerger); ok {
|
||||||
|
m.XXX_Merge(src)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mergeStruct(out.Elem(), in.Elem())
|
mergeStruct(out.Elem(), in.Elem())
|
||||||
@ -84,7 +108,7 @@ func mergeStruct(out, in reflect.Value) {
|
|||||||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
if emIn, ok := extendable(in.Addr().Interface()); ok {
|
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
||||||
emOut, _ := extendable(out.Addr().Interface())
|
emOut, _ := extendable(out.Addr().Interface())
|
||||||
mIn, muIn := emIn.extensionsRead()
|
mIn, muIn := emIn.extensionsRead()
|
||||||
if mIn != nil {
|
if mIn != nil {
|
||||||
|
668
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
668
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
@ -39,8 +39,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// errOverflow is returned when an integer is too large to be represented.
|
// errOverflow is returned when an integer is too large to be represented.
|
||||||
@ -50,10 +48,6 @@ var errOverflow = errors.New("proto: integer overflow")
|
|||||||
// wire type is encountered. It does not get returned to user code.
|
// wire type is encountered. It does not get returned to user code.
|
||||||
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
||||||
|
|
||||||
// The fundamental decoders that interpret bytes on the wire.
|
|
||||||
// Those that take integer types all return uint64 and are
|
|
||||||
// therefore of type valueDecoder.
|
|
||||||
|
|
||||||
// DecodeVarint reads a varint-encoded integer from the slice.
|
// DecodeVarint reads a varint-encoded integer from the slice.
|
||||||
// It returns the integer and the number of bytes consumed, or
|
// It returns the integer and the number of bytes consumed, or
|
||||||
// zero if there is not enough.
|
// zero if there is not enough.
|
||||||
@ -267,9 +261,6 @@ func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are not ValueDecoders: they produce an array of bytes or a string.
|
|
||||||
// bytes, embedded messages
|
|
||||||
|
|
||||||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
||||||
// This is the format used for the bytes protocol buffer
|
// This is the format used for the bytes protocol buffer
|
||||||
// type and for embedded messages.
|
// type and for embedded messages.
|
||||||
@ -311,81 +302,29 @@ func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
|||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
|
||||||
// If the protocol buffer has extensions, and the field matches, add it as an extension.
|
|
||||||
// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
|
|
||||||
func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
|
|
||||||
oi := o.index
|
|
||||||
|
|
||||||
err := o.skip(t, tag, wire)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unrecField.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr := structPointer_Bytes(base, unrecField)
|
|
||||||
|
|
||||||
// Add the skipped field to struct field
|
|
||||||
obuf := o.buf
|
|
||||||
|
|
||||||
o.buf = *ptr
|
|
||||||
o.EncodeVarint(uint64(tag<<3 | wire))
|
|
||||||
*ptr = append(o.buf, obuf[oi:o.index]...)
|
|
||||||
|
|
||||||
o.buf = obuf
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
|
||||||
func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
|
|
||||||
|
|
||||||
var u uint64
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch wire {
|
|
||||||
case WireVarint:
|
|
||||||
_, err = o.DecodeVarint()
|
|
||||||
case WireFixed64:
|
|
||||||
_, err = o.DecodeFixed64()
|
|
||||||
case WireBytes:
|
|
||||||
_, err = o.DecodeRawBytes(false)
|
|
||||||
case WireFixed32:
|
|
||||||
_, err = o.DecodeFixed32()
|
|
||||||
case WireStartGroup:
|
|
||||||
for {
|
|
||||||
u, err = o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fwire := int(u & 0x7)
|
|
||||||
if fwire == WireEndGroup {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ftag := int(u >> 3)
|
|
||||||
err = o.skip(t, ftag, fwire)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface representing objects that can
|
// Unmarshaler is the interface representing objects that can
|
||||||
// unmarshal themselves. The method should reset the receiver before
|
// unmarshal themselves. The argument points to data that may be
|
||||||
// decoding starts. The argument points to data that may be
|
|
||||||
// overwritten, so implementations should not keep references to the
|
// overwritten, so implementations should not keep references to the
|
||||||
// buffer.
|
// buffer.
|
||||||
|
// Unmarshal implementations should not clear the receiver.
|
||||||
|
// Any unmarshaled data should be merged into the receiver.
|
||||||
|
// Callers of Unmarshal that do not want to retain existing data
|
||||||
|
// should Reset the receiver before calling Unmarshal.
|
||||||
type Unmarshaler interface {
|
type Unmarshaler interface {
|
||||||
Unmarshal([]byte) error
|
Unmarshal([]byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newUnmarshaler is the interface representing objects that can
|
||||||
|
// unmarshal themselves. The semantics are identical to Unmarshaler.
|
||||||
|
//
|
||||||
|
// This exists to support protoc-gen-go generated messages.
|
||||||
|
// The proto package will stop type-asserting to this interface in the future.
|
||||||
|
//
|
||||||
|
// DO NOT DEPEND ON THIS.
|
||||||
|
type newUnmarshaler interface {
|
||||||
|
XXX_Unmarshal([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in buf and places the
|
// Unmarshal parses the protocol buffer representation in buf and places the
|
||||||
// decoded result in pb. If the struct underlying pb does not match
|
// decoded result in pb. If the struct underlying pb does not match
|
||||||
// the data in buf, the results can be unpredictable.
|
// the data in buf, the results can be unpredictable.
|
||||||
@ -395,7 +334,13 @@ type Unmarshaler interface {
|
|||||||
// to preserve and append to existing data.
|
// to preserve and append to existing data.
|
||||||
func Unmarshal(buf []byte, pb Message) error {
|
func Unmarshal(buf []byte, pb Message) error {
|
||||||
pb.Reset()
|
pb.Reset()
|
||||||
return UnmarshalMerge(buf, pb)
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
return u.XXX_Unmarshal(buf)
|
||||||
|
}
|
||||||
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
return u.Unmarshal(buf)
|
||||||
|
}
|
||||||
|
return NewBuffer(buf).Unmarshal(pb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalMerge parses the protocol buffer representation in buf and
|
// UnmarshalMerge parses the protocol buffer representation in buf and
|
||||||
@ -405,8 +350,16 @@ func Unmarshal(buf []byte, pb Message) error {
|
|||||||
// UnmarshalMerge merges into existing data in pb.
|
// UnmarshalMerge merges into existing data in pb.
|
||||||
// Most code should use Unmarshal instead.
|
// Most code should use Unmarshal instead.
|
||||||
func UnmarshalMerge(buf []byte, pb Message) error {
|
func UnmarshalMerge(buf []byte, pb Message) error {
|
||||||
// If the object can unmarshal itself, let it.
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
return u.XXX_Unmarshal(buf)
|
||||||
|
}
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
// NOTE: The history of proto have unfortunately been inconsistent
|
||||||
|
// whether Unmarshaler should or should not implicitly clear itself.
|
||||||
|
// Some implementations do, most do not.
|
||||||
|
// Thus, calling this here may or may not do what people want.
|
||||||
|
//
|
||||||
|
// See https://github.com/golang/protobuf/issues/424
|
||||||
return u.Unmarshal(buf)
|
return u.Unmarshal(buf)
|
||||||
}
|
}
|
||||||
return NewBuffer(buf).Unmarshal(pb)
|
return NewBuffer(buf).Unmarshal(pb)
|
||||||
@ -422,12 +375,17 @@ func (p *Buffer) DecodeMessage(pb Message) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DecodeGroup reads a tag-delimited group from the Buffer.
|
// DecodeGroup reads a tag-delimited group from the Buffer.
|
||||||
|
// StartGroup tag is already consumed. This function consumes
|
||||||
|
// EndGroup tag.
|
||||||
func (p *Buffer) DecodeGroup(pb Message) error {
|
func (p *Buffer) DecodeGroup(pb Message) error {
|
||||||
typ, base, err := getbase(pb)
|
b := p.buf[p.index:]
|
||||||
if err != nil {
|
x, y := findEndGroup(b)
|
||||||
return err
|
if x < 0 {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base)
|
err := Unmarshal(b[:x], pb)
|
||||||
|
p.index += y
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in the
|
// Unmarshal parses the protocol buffer representation in the
|
||||||
@ -438,533 +396,33 @@ func (p *Buffer) DecodeGroup(pb Message) error {
|
|||||||
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
|
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
|
||||||
func (p *Buffer) Unmarshal(pb Message) error {
|
func (p *Buffer) Unmarshal(pb Message) error {
|
||||||
// If the object can unmarshal itself, let it.
|
// If the object can unmarshal itself, let it.
|
||||||
|
if u, ok := pb.(newUnmarshaler); ok {
|
||||||
|
err := u.XXX_Unmarshal(p.buf[p.index:])
|
||||||
|
p.index = len(p.buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
if u, ok := pb.(Unmarshaler); ok {
|
||||||
|
// NOTE: The history of proto have unfortunately been inconsistent
|
||||||
|
// whether Unmarshaler should or should not implicitly clear itself.
|
||||||
|
// Some implementations do, most do not.
|
||||||
|
// Thus, calling this here may or may not do what people want.
|
||||||
|
//
|
||||||
|
// See https://github.com/golang/protobuf/issues/424
|
||||||
err := u.Unmarshal(p.buf[p.index:])
|
err := u.Unmarshal(p.buf[p.index:])
|
||||||
p.index = len(p.buf)
|
p.index = len(p.buf)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, base, err := getbase(pb)
|
// Slow workaround for messages that aren't Unmarshalers.
|
||||||
if err != nil {
|
// This includes some hand-coded .pb.go files and
|
||||||
return err
|
// bootstrap protos.
|
||||||
}
|
// TODO: fix all of those and then add Unmarshal to
|
||||||
|
// the Message interface. Then:
|
||||||
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
|
// The cast above and code below can be deleted.
|
||||||
|
// The old unmarshaler can be deleted.
|
||||||
if collectStats {
|
// Clients can call Unmarshal directly (can already do that, actually).
|
||||||
stats.Decode++
|
var info InternalMessageInfo
|
||||||
}
|
err := info.Unmarshal(pb, p.buf[p.index:])
|
||||||
|
p.index = len(p.buf)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmarshalType does the work of unmarshaling a structure.
|
|
||||||
func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
|
|
||||||
var state errorState
|
|
||||||
required, reqFields := prop.reqCount, uint64(0)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
for err == nil && o.index < len(o.buf) {
|
|
||||||
oi := o.index
|
|
||||||
var u uint64
|
|
||||||
u, err = o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
wire := int(u & 0x7)
|
|
||||||
if wire == WireEndGroup {
|
|
||||||
if is_group {
|
|
||||||
if required > 0 {
|
|
||||||
// Not enough information to determine the exact field.
|
|
||||||
// (See below.)
|
|
||||||
return &RequiredNotSetError{"{Unknown}"}
|
|
||||||
}
|
|
||||||
return nil // input is satisfied
|
|
||||||
}
|
|
||||||
return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
|
|
||||||
}
|
|
||||||
tag := int(u >> 3)
|
|
||||||
if tag <= 0 {
|
|
||||||
return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
|
|
||||||
}
|
|
||||||
fieldnum, ok := prop.decoderTags.get(tag)
|
|
||||||
if !ok {
|
|
||||||
// Maybe it's an extension?
|
|
||||||
if prop.extendable {
|
|
||||||
if e, _ := extendable(structPointer_Interface(base, st)); isExtensionField(e, int32(tag)) {
|
|
||||||
if err = o.skip(st, tag, wire); err == nil {
|
|
||||||
extmap := e.extensionsWrite()
|
|
||||||
ext := extmap[int32(tag)] // may be missing
|
|
||||||
ext.enc = append(ext.enc, o.buf[oi:o.index]...)
|
|
||||||
extmap[int32(tag)] = ext
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Maybe it's a oneof?
|
|
||||||
if prop.oneofUnmarshaler != nil {
|
|
||||||
m := structPointer_Interface(base, st).(Message)
|
|
||||||
// First return value indicates whether tag is a oneof field.
|
|
||||||
ok, err = prop.oneofUnmarshaler(m, tag, wire, o)
|
|
||||||
if err == ErrInternalBadWireType {
|
|
||||||
// Map the error to something more descriptive.
|
|
||||||
// Do the formatting here to save generated code space.
|
|
||||||
err = fmt.Errorf("bad wiretype for oneof field in %T", m)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p := prop.Prop[fieldnum]
|
|
||||||
|
|
||||||
if p.dec == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dec := p.dec
|
|
||||||
if wire != WireStartGroup && wire != p.WireType {
|
|
||||||
if wire == WireBytes && p.packedDec != nil {
|
|
||||||
// a packable field
|
|
||||||
dec = p.packedDec
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decErr := dec(o, p, base)
|
|
||||||
if decErr != nil && !state.shouldContinue(decErr, p) {
|
|
||||||
err = decErr
|
|
||||||
}
|
|
||||||
if err == nil && p.Required {
|
|
||||||
// Successfully decoded a required field.
|
|
||||||
if tag <= 64 {
|
|
||||||
// use bitmap for fields 1-64 to catch field reuse.
|
|
||||||
var mask uint64 = 1 << uint64(tag-1)
|
|
||||||
if reqFields&mask == 0 {
|
|
||||||
// new required field
|
|
||||||
reqFields |= mask
|
|
||||||
required--
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is imprecise. It can be fooled by a required field
|
|
||||||
// with a tag > 64 that is encoded twice; that's very rare.
|
|
||||||
// A fully correct implementation would require allocating
|
|
||||||
// a data structure, which we would like to avoid.
|
|
||||||
required--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if is_group {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if state.err != nil {
|
|
||||||
return state.err
|
|
||||||
}
|
|
||||||
if required > 0 {
|
|
||||||
// Not enough information to determine the exact field. If we use extra
|
|
||||||
// CPU, we could determine the field only if the missing required field
|
|
||||||
// has a tag <= 64 and we check reqFields.
|
|
||||||
return &RequiredNotSetError{"{Unknown}"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Individual type decoders
|
|
||||||
// For each,
|
|
||||||
// u is the decoded value,
|
|
||||||
// v is a pointer to the field (pointer) in the struct
|
|
||||||
|
|
||||||
// Sizes of the pools to allocate inside the Buffer.
|
|
||||||
// The goal is modest amortization and allocation
|
|
||||||
// on at least 16-byte boundaries.
|
|
||||||
const (
|
|
||||||
boolPoolSize = 16
|
|
||||||
uint32PoolSize = 8
|
|
||||||
uint64PoolSize = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Decode a bool.
|
|
||||||
func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(o.bools) == 0 {
|
|
||||||
o.bools = make([]bool, boolPoolSize)
|
|
||||||
}
|
|
||||||
o.bools[0] = u != 0
|
|
||||||
*structPointer_Bool(base, p.field) = &o.bools[0]
|
|
||||||
o.bools = o.bools[1:]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_BoolVal(base, p.field) = u != 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an int32.
|
|
||||||
func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an int64.
|
|
||||||
func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word64_Set(structPointer_Word64(base, p.field), o, u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a string.
|
|
||||||
func (o *Buffer) dec_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_String(base, p.field) = &s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_StringVal(base, p.field) = s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bytes ([]byte).
|
|
||||||
func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
|
|
||||||
b, err := o.DecodeRawBytes(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_Bytes(base, p.field) = b
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bools ([]bool).
|
|
||||||
func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_BoolSlice(base, p.field)
|
|
||||||
*v = append(*v, u != 0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bools ([]bool) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_BoolSlice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded bools
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
y := *v
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
y = append(y, u != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
*v = y
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int32s ([]int32).
|
|
||||||
func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
structPointer_Word32Slice(base, p.field).Append(uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int32s ([]int32) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_Word32Slice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded int32s
|
|
||||||
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Append(uint32(u))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int64s ([]int64).
|
|
||||||
func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
structPointer_Word64Slice(base, p.field).Append(u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int64s ([]int64) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_Word64Slice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded int64s
|
|
||||||
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Append(u)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of strings ([]string).
|
|
||||||
func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_StringSlice(base, p.field)
|
|
||||||
*v = append(*v, s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of slice of bytes ([][]byte).
|
|
||||||
func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
|
|
||||||
b, err := o.DecodeRawBytes(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_BytesSlice(base, p.field)
|
|
||||||
*v = append(*v, b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a map field.
|
|
||||||
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
|
||||||
raw, err := o.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
oi := o.index // index at the end of this map entry
|
|
||||||
o.index -= len(raw) // move buffer back to start of map entry
|
|
||||||
|
|
||||||
mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
|
|
||||||
if mptr.Elem().IsNil() {
|
|
||||||
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
|
|
||||||
}
|
|
||||||
v := mptr.Elem() // map[K]V
|
|
||||||
|
|
||||||
// Prepare addressable doubly-indirect placeholders for the key and value types.
|
|
||||||
// See enc_new_map for why.
|
|
||||||
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
|
|
||||||
keybase := toStructPointer(keyptr.Addr()) // **K
|
|
||||||
|
|
||||||
var valbase structPointer
|
|
||||||
var valptr reflect.Value
|
|
||||||
switch p.mtype.Elem().Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
// []byte
|
|
||||||
var dummy []byte
|
|
||||||
valptr = reflect.ValueOf(&dummy) // *[]byte
|
|
||||||
valbase = toStructPointer(valptr) // *[]byte
|
|
||||||
case reflect.Ptr:
|
|
||||||
// message; valptr is **Msg; need to allocate the intermediate pointer
|
|
||||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
|
||||||
valptr.Set(reflect.New(valptr.Type().Elem()))
|
|
||||||
valbase = toStructPointer(valptr)
|
|
||||||
default:
|
|
||||||
// everything else
|
|
||||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
|
||||||
valbase = toStructPointer(valptr.Addr()) // **V
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode.
|
|
||||||
// This parses a restricted wire format, namely the encoding of a message
|
|
||||||
// with two fields. See enc_new_map for the format.
|
|
||||||
for o.index < oi {
|
|
||||||
// tagcode for key and value properties are always a single byte
|
|
||||||
// because they have tags 1 and 2.
|
|
||||||
tagcode := o.buf[o.index]
|
|
||||||
o.index++
|
|
||||||
switch tagcode {
|
|
||||||
case p.mkeyprop.tagcode[0]:
|
|
||||||
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case p.mvalprop.tagcode[0]:
|
|
||||||
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO: Should we silently skip this instead?
|
|
||||||
return fmt.Errorf("proto: bad map data tag %d", raw[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyelem, valelem := keyptr.Elem(), valptr.Elem()
|
|
||||||
if !keyelem.IsValid() {
|
|
||||||
keyelem = reflect.Zero(p.mtype.Key())
|
|
||||||
}
|
|
||||||
if !valelem.IsValid() {
|
|
||||||
valelem = reflect.Zero(p.mtype.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
v.SetMapIndex(keyelem, valelem)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a group.
|
|
||||||
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
|
|
||||||
bas := structPointer_GetStructPointer(base, p.field)
|
|
||||||
if structPointer_IsNil(bas) {
|
|
||||||
// allocate new nested message
|
|
||||||
bas = toStructPointer(reflect.New(p.stype))
|
|
||||||
structPointer_SetStructPointer(base, p.field, bas)
|
|
||||||
}
|
|
||||||
return o.unmarshalType(p.stype, p.sprop, true, bas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an embedded message.
|
|
||||||
func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
|
|
||||||
raw, e := o.DecodeRawBytes(false)
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
bas := structPointer_GetStructPointer(base, p.field)
|
|
||||||
if structPointer_IsNil(bas) {
|
|
||||||
// allocate new nested message
|
|
||||||
bas = toStructPointer(reflect.New(p.stype))
|
|
||||||
structPointer_SetStructPointer(base, p.field, bas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if p.isUnmarshaler {
|
|
||||||
iv := structPointer_Interface(bas, p.stype)
|
|
||||||
return iv.(Unmarshaler).Unmarshal(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
obuf := o.buf
|
|
||||||
oi := o.index
|
|
||||||
o.buf = raw
|
|
||||||
o.index = 0
|
|
||||||
|
|
||||||
err = o.unmarshalType(p.stype, p.sprop, false, bas)
|
|
||||||
o.buf = obuf
|
|
||||||
o.index = oi
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of embedded messages.
|
|
||||||
func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
|
|
||||||
return o.dec_slice_struct(p, false, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of embedded groups.
|
|
||||||
func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
|
|
||||||
return o.dec_slice_struct(p, true, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of structs ([]*struct).
|
|
||||||
func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
|
|
||||||
v := reflect.New(p.stype)
|
|
||||||
bas := toStructPointer(v)
|
|
||||||
structPointer_StructPointerSlice(base, p.field).Append(bas)
|
|
||||||
|
|
||||||
if is_group {
|
|
||||||
err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := o.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if p.isUnmarshaler {
|
|
||||||
iv := v.Interface()
|
|
||||||
return iv.(Unmarshaler).Unmarshal(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
obuf := o.buf
|
|
||||||
oi := o.index
|
|
||||||
o.buf = raw
|
|
||||||
o.index = 0
|
|
||||||
|
|
||||||
err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
|
||||||
|
|
||||||
o.buf = obuf
|
|
||||||
o.index = oi
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
350
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
Normal file
350
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type generatedDiscarder interface {
|
||||||
|
XXX_DiscardUnknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardUnknown recursively discards all unknown fields from this message
|
||||||
|
// and all embedded messages.
|
||||||
|
//
|
||||||
|
// When unmarshaling a message with unrecognized fields, the tags and values
|
||||||
|
// of such fields are preserved in the Message. This allows a later call to
|
||||||
|
// marshal to be able to produce a message that continues to have those
|
||||||
|
// unrecognized fields. To avoid this, DiscardUnknown is used to
|
||||||
|
// explicitly clear the unknown fields after unmarshaling.
|
||||||
|
//
|
||||||
|
// For proto2 messages, the unknown fields of message extensions are only
|
||||||
|
// discarded from messages that have been accessed via GetExtension.
|
||||||
|
func DiscardUnknown(m Message) {
|
||||||
|
if m, ok := m.(generatedDiscarder); ok {
|
||||||
|
m.XXX_DiscardUnknown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: Dynamically populate a InternalMessageInfo for legacy messages,
|
||||||
|
// but the master branch has no implementation for InternalMessageInfo,
|
||||||
|
// so it would be more work to replicate that approach.
|
||||||
|
discardLegacy(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardUnknown recursively discards all unknown fields.
|
||||||
|
func (a *InternalMessageInfo) DiscardUnknown(m Message) {
|
||||||
|
di := atomicLoadDiscardInfo(&a.discard)
|
||||||
|
if di == nil {
|
||||||
|
di = getDiscardInfo(reflect.TypeOf(m).Elem())
|
||||||
|
atomicStoreDiscardInfo(&a.discard, di)
|
||||||
|
}
|
||||||
|
di.discard(toPointer(&m))
|
||||||
|
}
|
||||||
|
|
||||||
|
type discardInfo struct {
|
||||||
|
typ reflect.Type
|
||||||
|
|
||||||
|
initialized int32 // 0: only typ is valid, 1: everything is valid
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
fields []discardFieldInfo
|
||||||
|
unrecognized field
|
||||||
|
}
|
||||||
|
|
||||||
|
type discardFieldInfo struct {
|
||||||
|
field field // Offset of field, guaranteed to be valid
|
||||||
|
discard func(src pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
discardInfoMap = map[reflect.Type]*discardInfo{}
|
||||||
|
discardInfoLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDiscardInfo(t reflect.Type) *discardInfo {
|
||||||
|
discardInfoLock.Lock()
|
||||||
|
defer discardInfoLock.Unlock()
|
||||||
|
di := discardInfoMap[t]
|
||||||
|
if di == nil {
|
||||||
|
di = &discardInfo{typ: t}
|
||||||
|
discardInfoMap[t] = di
|
||||||
|
}
|
||||||
|
return di
|
||||||
|
}
|
||||||
|
|
||||||
|
func (di *discardInfo) discard(src pointer) {
|
||||||
|
if src.isNil() {
|
||||||
|
return // Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.LoadInt32(&di.initialized) == 0 {
|
||||||
|
di.computeDiscardInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range di.fields {
|
||||||
|
sfp := src.offset(fi.field)
|
||||||
|
fi.discard(sfp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For proto2 messages, only discard unknown fields in message extensions
|
||||||
|
// that have been accessed via GetExtension.
|
||||||
|
if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil {
|
||||||
|
// Ignore lock since DiscardUnknown is not concurrency safe.
|
||||||
|
emm, _ := em.extensionsRead()
|
||||||
|
for _, mx := range emm {
|
||||||
|
if m, ok := mx.value.(Message); ok {
|
||||||
|
DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if di.unrecognized.IsValid() {
|
||||||
|
*src.offset(di.unrecognized).toBytes() = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (di *discardInfo) computeDiscardInfo() {
|
||||||
|
di.lock.Lock()
|
||||||
|
defer di.lock.Unlock()
|
||||||
|
if di.initialized != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := di.typ
|
||||||
|
n := t.NumField()
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dfi := discardFieldInfo{field: toField(&f)}
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// Unwrap tf to get its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
di := getDiscardInfo(tf)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sps := src.getPointerSlice()
|
||||||
|
for _, sp := range sps {
|
||||||
|
if !sp.isNil() {
|
||||||
|
di.discard(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
di := getDiscardInfo(tf)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sp := src.getPointer()
|
||||||
|
if !sp.isNil() {
|
||||||
|
di.discard(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name))
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T)
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
sm := src.asPointerTo(tf).Elem()
|
||||||
|
if sm.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
DiscardUnknown(val.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dfi.discard = func(pointer) {} // Noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name))
|
||||||
|
default: // E.g., interface{}
|
||||||
|
// TODO: Make this faster?
|
||||||
|
dfi.discard = func(src pointer) {
|
||||||
|
su := src.asPointerTo(tf).Elem()
|
||||||
|
if !su.IsNil() {
|
||||||
|
sv := su.Elem().Elem().Field(0)
|
||||||
|
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch sv.Type().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
DiscardUnknown(sv.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
di.fields = append(di.fields, dfi)
|
||||||
|
}
|
||||||
|
|
||||||
|
di.unrecognized = invalidField
|
||||||
|
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
||||||
|
if f.Type != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
di.unrecognized = toField(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreInt32(&di.initialized, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func discardLegacy(m Message) {
|
||||||
|
v := reflect.ValueOf(m)
|
||||||
|
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vf := v.Field(i)
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// Unwrap tf to get its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
for j := 0; j < vf.Len(); j++ {
|
||||||
|
discardLegacy(vf.Index(j).Interface().(Message))
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
discardLegacy(vf.Interface().(Message))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name))
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
tv := vf.Type().Elem()
|
||||||
|
if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T)
|
||||||
|
for _, key := range vf.MapKeys() {
|
||||||
|
val := vf.MapIndex(key)
|
||||||
|
discardLegacy(val.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name))
|
||||||
|
default: // E.g., test_proto.isCommunique_Union interface
|
||||||
|
if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" {
|
||||||
|
vf = vf.Elem() // E.g., *test_proto.Communique_Msg
|
||||||
|
if !vf.IsNil() {
|
||||||
|
vf = vf.Elem() // E.g., test_proto.Communique_Msg
|
||||||
|
vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value
|
||||||
|
if vf.Kind() == reflect.Ptr {
|
||||||
|
discardLegacy(vf.Interface().(Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() {
|
||||||
|
if vf.Type() != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
vf.Set(reflect.ValueOf([]byte(nil)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// For proto2 messages, only discard unknown fields in message extensions
|
||||||
|
// that have been accessed via GetExtension.
|
||||||
|
if em, err := extendable(m); err == nil {
|
||||||
|
// Ignore lock since discardLegacy is not concurrency safe.
|
||||||
|
emm, _ := em.extensionsRead()
|
||||||
|
for _, mx := range emm {
|
||||||
|
if m, ok := mx.value.(Message); ok {
|
||||||
|
discardLegacy(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1205
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
1205
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
File diff suppressed because it is too large
Load Diff
30
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
30
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
@ -109,15 +109,6 @@ func equalStruct(v1, v2 reflect.Value) bool {
|
|||||||
// set/unset mismatch
|
// set/unset mismatch
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
b1, ok := f1.Interface().(raw)
|
|
||||||
if ok {
|
|
||||||
b2 := f2.Interface().(raw)
|
|
||||||
// RawMessage
|
|
||||||
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f1, f2 = f1.Elem(), f2.Elem()
|
f1, f2 = f1.Elem(), f2.Elem()
|
||||||
}
|
}
|
||||||
if !equalAny(f1, f2, sprop.Prop[i]) {
|
if !equalAny(f1, f2, sprop.Prop[i]) {
|
||||||
@ -146,11 +137,7 @@ func equalStruct(v1, v2 reflect.Value) bool {
|
|||||||
|
|
||||||
u1 := uf.Bytes()
|
u1 := uf.Bytes()
|
||||||
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
||||||
if !bytes.Equal(u1, u2) {
|
return bytes.Equal(u1, u2)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// v1 and v2 are known to have the same type.
|
// v1 and v2 are known to have the same type.
|
||||||
@ -261,6 +248,15 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
|||||||
|
|
||||||
m1, m2 := e1.value, e2.value
|
m1, m2 := e1.value, e2.value
|
||||||
|
|
||||||
|
if m1 == nil && m2 == nil {
|
||||||
|
// Both have only encoded form.
|
||||||
|
if bytes.Equal(e1.enc, e2.enc) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The bytes are different, but the extensions might still be
|
||||||
|
// equal. We need to decode them to compare.
|
||||||
|
}
|
||||||
|
|
||||||
if m1 != nil && m2 != nil {
|
if m1 != nil && m2 != nil {
|
||||||
// Both are unencoded.
|
// Both are unencoded.
|
||||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
||||||
@ -276,8 +272,12 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
|||||||
desc = m[extNum]
|
desc = m[extNum]
|
||||||
}
|
}
|
||||||
if desc == nil {
|
if desc == nil {
|
||||||
|
// If both have only encoded form and the bytes are the same,
|
||||||
|
// it is handled above. We get here when the bytes are different.
|
||||||
|
// We don't know how to decode it, so just compare them as byte
|
||||||
|
// slices.
|
||||||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
||||||
continue
|
return false
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if m1 == nil {
|
if m1 == nil {
|
||||||
|
202
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
202
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
@ -38,6 +38,7 @@ package proto
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@ -91,14 +92,29 @@ func (n notLocker) Unlock() {}
|
|||||||
// extendable returns the extendableProto interface for the given generated proto message.
|
// extendable returns the extendableProto interface for the given generated proto message.
|
||||||
// If the proto message has the old extension format, it returns a wrapper that implements
|
// If the proto message has the old extension format, it returns a wrapper that implements
|
||||||
// the extendableProto interface.
|
// the extendableProto interface.
|
||||||
func extendable(p interface{}) (extendableProto, bool) {
|
func extendable(p interface{}) (extendableProto, error) {
|
||||||
if ep, ok := p.(extendableProto); ok {
|
switch p := p.(type) {
|
||||||
return ep, ok
|
case extendableProto:
|
||||||
|
if isNilPtr(p) {
|
||||||
|
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
||||||
}
|
}
|
||||||
if ep, ok := p.(extendableProtoV1); ok {
|
return p, nil
|
||||||
return extensionAdapter{ep}, ok
|
case extendableProtoV1:
|
||||||
|
if isNilPtr(p) {
|
||||||
|
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
|
||||||
}
|
}
|
||||||
return nil, false
|
return extensionAdapter{p}, nil
|
||||||
|
}
|
||||||
|
// Don't allocate a specific error containing %T:
|
||||||
|
// this is the hot path for Clone and MarshalText.
|
||||||
|
return nil, errNotExtendable
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNotExtendable = errors.New("proto: not an extendable proto.Message")
|
||||||
|
|
||||||
|
func isNilPtr(x interface{}) bool {
|
||||||
|
v := reflect.ValueOf(x)
|
||||||
|
return v.Kind() == reflect.Ptr && v.IsNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX_InternalExtensions is an internal representation of proto extensions.
|
// XXX_InternalExtensions is an internal representation of proto extensions.
|
||||||
@ -143,9 +159,6 @@ func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Loc
|
|||||||
return e.p.extensionMap, &e.p.mu
|
return e.p.extensionMap, &e.p.mu
|
||||||
}
|
}
|
||||||
|
|
||||||
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
|
|
||||||
var extendableProtoV1Type = reflect.TypeOf((*extendableProtoV1)(nil)).Elem()
|
|
||||||
|
|
||||||
// ExtensionDesc represents an extension specification.
|
// ExtensionDesc represents an extension specification.
|
||||||
// Used in generated code from the protocol compiler.
|
// Used in generated code from the protocol compiler.
|
||||||
type ExtensionDesc struct {
|
type ExtensionDesc struct {
|
||||||
@ -179,8 +192,8 @@ type Extension struct {
|
|||||||
|
|
||||||
// SetRawExtension is for testing only.
|
// SetRawExtension is for testing only.
|
||||||
func SetRawExtension(base Message, id int32, b []byte) {
|
func SetRawExtension(base Message, id int32, b []byte) {
|
||||||
epb, ok := extendable(base)
|
epb, err := extendable(base)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
extmap := epb.extensionsWrite()
|
extmap := epb.extensionsWrite()
|
||||||
@ -205,7 +218,7 @@ func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
|||||||
pbi = ea.extendableProtoV1
|
pbi = ea.extendableProtoV1
|
||||||
}
|
}
|
||||||
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
|
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
|
||||||
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
|
return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a)
|
||||||
}
|
}
|
||||||
// Check the range.
|
// Check the range.
|
||||||
if !isExtensionField(pb, extension.Field) {
|
if !isExtensionField(pb, extension.Field) {
|
||||||
@ -250,85 +263,11 @@ func extensionProperties(ed *ExtensionDesc) *Properties {
|
|||||||
return prop
|
return prop
|
||||||
}
|
}
|
||||||
|
|
||||||
// encode encodes any unmarshaled (unencoded) extensions in e.
|
|
||||||
func encodeExtensions(e *XXX_InternalExtensions) error {
|
|
||||||
m, mu := e.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return nil // fast path
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return encodeExtensionsMap(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode encodes any unmarshaled (unencoded) extensions in e.
|
|
||||||
func encodeExtensionsMap(m map[int32]Extension) error {
|
|
||||||
for k, e := range m {
|
|
||||||
if e.value == nil || e.desc == nil {
|
|
||||||
// Extension is only in its encoded form.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't skip extensions that have an encoded form set,
|
|
||||||
// because the extension value may have been mutated after
|
|
||||||
// the last time this function was called.
|
|
||||||
|
|
||||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
|
||||||
props := extensionProperties(e.desc)
|
|
||||||
|
|
||||||
p := NewBuffer(nil)
|
|
||||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
|
||||||
// Pass a *T with a zero field and hope it all works out.
|
|
||||||
x := reflect.New(et)
|
|
||||||
x.Elem().Set(reflect.ValueOf(e.value))
|
|
||||||
if err := props.enc(p, props, toStructPointer(x)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.enc = p.buf
|
|
||||||
m[k] = e
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionsSize(e *XXX_InternalExtensions) (n int) {
|
|
||||||
m, mu := e.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return extensionsMapSize(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionsMapSize(m map[int32]Extension) (n int) {
|
|
||||||
for _, e := range m {
|
|
||||||
if e.value == nil || e.desc == nil {
|
|
||||||
// Extension is only in its encoded form.
|
|
||||||
n += len(e.enc)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't skip extensions that have an encoded form set,
|
|
||||||
// because the extension value may have been mutated after
|
|
||||||
// the last time this function was called.
|
|
||||||
|
|
||||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
|
||||||
props := extensionProperties(e.desc)
|
|
||||||
|
|
||||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
|
||||||
// Pass a *T with a zero field and hope it all works out.
|
|
||||||
x := reflect.New(et)
|
|
||||||
x.Elem().Set(reflect.ValueOf(e.value))
|
|
||||||
n += props.size(props, toStructPointer(x))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasExtension returns whether the given extension is present in pb.
|
// HasExtension returns whether the given extension is present in pb.
|
||||||
func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
||||||
// TODO: Check types, field numbers, etc.?
|
// TODO: Check types, field numbers, etc.?
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
extmap, mu := epb.extensionsRead()
|
extmap, mu := epb.extensionsRead()
|
||||||
@ -336,15 +275,15 @@ func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
_, ok = extmap[extension.Field]
|
_, ok := extmap[extension.Field]
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearExtension removes the given extension from pb.
|
// ClearExtension removes the given extension from pb.
|
||||||
func ClearExtension(pb Message, extension *ExtensionDesc) {
|
func ClearExtension(pb Message, extension *ExtensionDesc) {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: Check types, field numbers, etc.?
|
// TODO: Check types, field numbers, etc.?
|
||||||
@ -352,17 +291,27 @@ func ClearExtension(pb Message, extension *ExtensionDesc) {
|
|||||||
delete(extmap, extension.Field)
|
delete(extmap, extension.Field)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExtension parses and returns the given extension of pb.
|
// GetExtension retrieves a proto2 extended field from pb.
|
||||||
// If the extension is not present and has no default value it returns ErrMissingExtension.
|
//
|
||||||
|
// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
|
||||||
|
// then GetExtension parses the encoded field and returns a Go value of the specified type.
|
||||||
|
// If the field is not present, then the default value is returned (if one is specified),
|
||||||
|
// otherwise ErrMissingExtension is reported.
|
||||||
|
//
|
||||||
|
// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil),
|
||||||
|
// then GetExtension returns the raw encoded bytes of the field extension.
|
||||||
func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, errors.New("proto: not an extendable proto")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if extension.ExtendedType != nil {
|
||||||
|
// can only check type if this is a complete descriptor
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
if err := checkExtensionTypes(epb, extension); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emap, mu := epb.extensionsRead()
|
emap, mu := epb.extensionsRead()
|
||||||
if emap == nil {
|
if emap == nil {
|
||||||
@ -388,6 +337,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
|||||||
return e.value, nil
|
return e.value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if extension.ExtensionType == nil {
|
||||||
|
// incomplete descriptor
|
||||||
|
return e.enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
v, err := decodeExtension(e.enc, extension)
|
v, err := decodeExtension(e.enc, extension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -405,6 +359,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
|||||||
// defaultExtensionValue returns the default value for extension.
|
// defaultExtensionValue returns the default value for extension.
|
||||||
// If no default for an extension is defined ErrMissingExtension is returned.
|
// If no default for an extension is defined ErrMissingExtension is returned.
|
||||||
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
||||||
|
if extension.ExtensionType == nil {
|
||||||
|
// incomplete descriptor, so no default
|
||||||
|
return nil, ErrMissingExtension
|
||||||
|
}
|
||||||
|
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
t := reflect.TypeOf(extension.ExtensionType)
|
||||||
props := extensionProperties(extension)
|
props := extensionProperties(extension)
|
||||||
|
|
||||||
@ -439,31 +398,28 @@ func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
|||||||
|
|
||||||
// decodeExtension decodes an extension encoded in b.
|
// decodeExtension decodes an extension encoded in b.
|
||||||
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
||||||
o := NewBuffer(b)
|
|
||||||
|
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
t := reflect.TypeOf(extension.ExtensionType)
|
||||||
|
unmarshal := typeUnmarshaler(t, extension.Tag)
|
||||||
props := extensionProperties(extension)
|
|
||||||
|
|
||||||
// t is a pointer to a struct, pointer to basic type or a slice.
|
// t is a pointer to a struct, pointer to basic type or a slice.
|
||||||
// Allocate a "field" to store the pointer/slice itself; the
|
// Allocate space to store the pointer/slice.
|
||||||
// pointer/slice will be stored here. We pass
|
|
||||||
// the address of this field to props.dec.
|
|
||||||
// This passes a zero field and a *t and lets props.dec
|
|
||||||
// interpret it as a *struct{ x t }.
|
|
||||||
value := reflect.New(t).Elem()
|
value := reflect.New(t).Elem()
|
||||||
|
|
||||||
|
var err error
|
||||||
for {
|
for {
|
||||||
// Discard wire type and field number varint. It isn't needed.
|
x, n := decodeVarint(b)
|
||||||
if _, err := o.DecodeVarint(); err != nil {
|
if n == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
wire := int(x) & 7
|
||||||
|
|
||||||
|
b, err = unmarshal(b, valToPointer(value.Addr()), wire)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
|
if len(b) == 0 {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.index >= len(o.buf) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,9 +429,9 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
|||||||
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
||||||
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
||||||
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, errors.New("proto: not an extendable proto")
|
return nil, err
|
||||||
}
|
}
|
||||||
extensions = make([]interface{}, len(es))
|
extensions = make([]interface{}, len(es))
|
||||||
for i, e := range es {
|
for i, e := range es {
|
||||||
@ -494,9 +450,9 @@ func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, e
|
|||||||
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
|
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
|
||||||
// just the Field field, which defines the extension's field number.
|
// just the Field field, which defines the extension's field number.
|
||||||
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
|
return nil, err
|
||||||
}
|
}
|
||||||
registeredExtensions := RegisteredExtensions(pb)
|
registeredExtensions := RegisteredExtensions(pb)
|
||||||
|
|
||||||
@ -523,9 +479,9 @@ func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
|||||||
|
|
||||||
// SetExtension sets the specified extension of pb to the specified value.
|
// SetExtension sets the specified extension of pb to the specified value.
|
||||||
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
|
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return errors.New("proto: not an extendable proto")
|
return err
|
||||||
}
|
}
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
if err := checkExtensionTypes(epb, extension); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -550,8 +506,8 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
|
|||||||
|
|
||||||
// ClearAllExtensions clears all extensions from pb.
|
// ClearAllExtensions clears all extensions from pb.
|
||||||
func ClearAllExtensions(pb Message) {
|
func ClearAllExtensions(pb Message) {
|
||||||
epb, ok := extendable(pb)
|
epb, err := extendable(pb)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m := epb.extensionsWrite()
|
m := epb.extensionsWrite()
|
||||||
|
128
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
128
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
@ -273,6 +273,67 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal.
|
||||||
|
// Marshal reports this when a required field is not initialized.
|
||||||
|
// Unmarshal reports this when a required field is missing from the wire data.
|
||||||
|
type RequiredNotSetError struct{ field string }
|
||||||
|
|
||||||
|
func (e *RequiredNotSetError) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return fmt.Sprintf("proto: required field not set")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: required field %q not set", e.field)
|
||||||
|
}
|
||||||
|
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidUTF8Error struct{ field string }
|
||||||
|
|
||||||
|
func (e *invalidUTF8Error) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return "proto: invalid UTF-8 detected"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field)
|
||||||
|
}
|
||||||
|
func (e *invalidUTF8Error) InvalidUTF8() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8.
|
||||||
|
// This error should not be exposed to the external API as such errors should
|
||||||
|
// be recreated with the field information.
|
||||||
|
var errInvalidUTF8 = &invalidUTF8Error{}
|
||||||
|
|
||||||
|
// isNonFatal reports whether the error is either a RequiredNotSet error
|
||||||
|
// or a InvalidUTF8 error.
|
||||||
|
func isNonFatal(err error) bool {
|
||||||
|
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type nonFatal struct{ E error }
|
||||||
|
|
||||||
|
// Merge merges err into nf and reports whether it was successful.
|
||||||
|
// Otherwise it returns false for any fatal non-nil errors.
|
||||||
|
func (nf *nonFatal) Merge(err error) (ok bool) {
|
||||||
|
if err == nil {
|
||||||
|
return true // not an error
|
||||||
|
}
|
||||||
|
if !isNonFatal(err) {
|
||||||
|
return false // fatal error
|
||||||
|
}
|
||||||
|
if nf.E == nil {
|
||||||
|
nf.E = err // store first instance of non-fatal error
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Message is implemented by generated protocol buffer messages.
|
// Message is implemented by generated protocol buffer messages.
|
||||||
type Message interface {
|
type Message interface {
|
||||||
Reset()
|
Reset()
|
||||||
@ -309,16 +370,7 @@ type Buffer struct {
|
|||||||
buf []byte // encode/decode byte stream
|
buf []byte // encode/decode byte stream
|
||||||
index int // read point
|
index int // read point
|
||||||
|
|
||||||
// pools of basic types to amortize allocation.
|
deterministic bool
|
||||||
bools []bool
|
|
||||||
uint32s []uint32
|
|
||||||
uint64s []uint64
|
|
||||||
|
|
||||||
// extra pools, only used with pointer_reflect.go
|
|
||||||
int32s []int32
|
|
||||||
int64s []int64
|
|
||||||
float32s []float32
|
|
||||||
float64s []float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuffer allocates a new Buffer and initializes its internal data to
|
// NewBuffer allocates a new Buffer and initializes its internal data to
|
||||||
@ -343,6 +395,30 @@ func (p *Buffer) SetBuf(s []byte) {
|
|||||||
// Bytes returns the contents of the Buffer.
|
// Bytes returns the contents of the Buffer.
|
||||||
func (p *Buffer) Bytes() []byte { return p.buf }
|
func (p *Buffer) Bytes() []byte { return p.buf }
|
||||||
|
|
||||||
|
// SetDeterministic sets whether to use deterministic serialization.
|
||||||
|
//
|
||||||
|
// Deterministic serialization guarantees that for a given binary, equal
|
||||||
|
// messages will always be serialized to the same bytes. This implies:
|
||||||
|
//
|
||||||
|
// - Repeated serialization of a message will return the same bytes.
|
||||||
|
// - Different processes of the same binary (which may be executing on
|
||||||
|
// different machines) will serialize equal messages to the same bytes.
|
||||||
|
//
|
||||||
|
// Note that the deterministic serialization is NOT canonical across
|
||||||
|
// languages. It is not guaranteed to remain stable over time. It is unstable
|
||||||
|
// across different builds with schema changes due to unknown fields.
|
||||||
|
// Users who need canonical serialization (e.g., persistent storage in a
|
||||||
|
// canonical form, fingerprinting, etc.) should define their own
|
||||||
|
// canonicalization specification and implement their own serializer rather
|
||||||
|
// than relying on this API.
|
||||||
|
//
|
||||||
|
// If deterministic serialization is requested, map entries will be sorted
|
||||||
|
// by keys in lexographical order. This is an implementation detail and
|
||||||
|
// subject to change.
|
||||||
|
func (p *Buffer) SetDeterministic(deterministic bool) {
|
||||||
|
p.deterministic = deterministic
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||||
*/
|
*/
|
||||||
@ -831,22 +907,12 @@ func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMes
|
|||||||
return sf, false, nil
|
return sf, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mapKeys returns a sort.Interface to be used for sorting the map keys.
|
||||||
// Map fields may have key types of non-float scalars, strings and enums.
|
// Map fields may have key types of non-float scalars, strings and enums.
|
||||||
// The easiest way to sort them in some deterministic order is to use fmt.
|
|
||||||
// If this turns out to be inefficient we can always consider other options,
|
|
||||||
// such as doing a Schwartzian transform.
|
|
||||||
|
|
||||||
func mapKeys(vs []reflect.Value) sort.Interface {
|
func mapKeys(vs []reflect.Value) sort.Interface {
|
||||||
s := mapKeySorter{
|
s := mapKeySorter{vs: vs}
|
||||||
vs: vs,
|
|
||||||
// default Less function: textual comparison
|
|
||||||
less: func(a, b reflect.Value) bool {
|
|
||||||
return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
|
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps.
|
||||||
// numeric keys are sorted numerically.
|
|
||||||
if len(vs) == 0 {
|
if len(vs) == 0 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -855,6 +921,12 @@ func mapKeys(vs []reflect.Value) sort.Interface {
|
|||||||
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||||
case reflect.Uint32, reflect.Uint64:
|
case reflect.Uint32, reflect.Uint64:
|
||||||
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||||
|
case reflect.Bool:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true
|
||||||
|
case reflect.String:
|
||||||
|
s.less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
@ -895,3 +967,13 @@ const ProtoPackageIsVersion2 = true
|
|||||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
const ProtoPackageIsVersion1 = true
|
const ProtoPackageIsVersion1 = true
|
||||||
|
|
||||||
|
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
||||||
|
// This type is not intended to be used by non-generated code.
|
||||||
|
// This type is not subject to any compatibility guarantee.
|
||||||
|
type InternalMessageInfo struct {
|
||||||
|
marshal *marshalInfo
|
||||||
|
unmarshal *unmarshalInfo
|
||||||
|
merge *mergeInfo
|
||||||
|
discard *discardInfo
|
||||||
|
}
|
||||||
|
81
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
81
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
@ -42,6 +42,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
||||||
@ -94,10 +95,7 @@ func (ms *messageSet) find(pb Message) *_MessageSet_Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ms *messageSet) Has(pb Message) bool {
|
func (ms *messageSet) Has(pb Message) bool {
|
||||||
if ms.find(pb) != nil {
|
return ms.find(pb) != nil
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *messageSet) Unmarshal(pb Message) error {
|
func (ms *messageSet) Unmarshal(pb Message) error {
|
||||||
@ -150,46 +148,42 @@ func skipVarint(buf []byte) []byte {
|
|||||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
||||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
||||||
var m map[int32]Extension
|
return marshalMessageSet(exts, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
|
||||||
|
func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
|
||||||
switch exts := exts.(type) {
|
switch exts := exts.(type) {
|
||||||
case *XXX_InternalExtensions:
|
case *XXX_InternalExtensions:
|
||||||
if err := encodeExtensions(exts); err != nil {
|
var u marshalInfo
|
||||||
return nil, err
|
siz := u.sizeMessageSet(exts)
|
||||||
}
|
b := make([]byte, 0, siz)
|
||||||
m, _ = exts.extensionsRead()
|
return u.appendMessageSet(b, exts, deterministic)
|
||||||
|
|
||||||
case map[int32]Extension:
|
case map[int32]Extension:
|
||||||
if err := encodeExtensionsMap(exts); err != nil {
|
// This is an old-style extension map.
|
||||||
return nil, err
|
// Wrap it in a new-style XXX_InternalExtensions.
|
||||||
|
ie := XXX_InternalExtensions{
|
||||||
|
p: &struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
extensionMap map[int32]Extension
|
||||||
|
}{
|
||||||
|
extensionMap: exts,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
m = exts
|
|
||||||
|
var u marshalInfo
|
||||||
|
siz := u.sizeMessageSet(&ie)
|
||||||
|
b := make([]byte, 0, siz)
|
||||||
|
return u.appendMessageSet(b, &ie, deterministic)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("proto: not an extension map")
|
return nil, errors.New("proto: not an extension map")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort extension IDs to provide a deterministic encoding.
|
|
||||||
// See also enc_map in encode.go.
|
|
||||||
ids := make([]int, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, int(id))
|
|
||||||
}
|
|
||||||
sort.Ints(ids)
|
|
||||||
|
|
||||||
ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
|
|
||||||
for _, id := range ids {
|
|
||||||
e := m[int32(id)]
|
|
||||||
// Remove the wire type and field number varint, as well as the length varint.
|
|
||||||
msg := skipVarint(skipVarint(e.enc))
|
|
||||||
|
|
||||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
|
||||||
TypeId: Int32(int32(id)),
|
|
||||||
Message: msg,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return Marshal(ms)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||||
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||||
var m map[int32]Extension
|
var m map[int32]Extension
|
||||||
switch exts := exts.(type) {
|
switch exts := exts.(type) {
|
||||||
@ -235,7 +229,15 @@ func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
|||||||
var m map[int32]Extension
|
var m map[int32]Extension
|
||||||
switch exts := exts.(type) {
|
switch exts := exts.(type) {
|
||||||
case *XXX_InternalExtensions:
|
case *XXX_InternalExtensions:
|
||||||
m, _ = exts.extensionsRead()
|
var mu sync.Locker
|
||||||
|
m, mu = exts.extensionsRead()
|
||||||
|
if m != nil {
|
||||||
|
// Keep the extensions map locked until we're done marshaling to prevent
|
||||||
|
// races between marshaling and unmarshaling the lazily-{en,de}coded
|
||||||
|
// values.
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
}
|
||||||
case map[int32]Extension:
|
case map[int32]Extension:
|
||||||
m = exts
|
m = exts
|
||||||
default:
|
default:
|
||||||
@ -253,15 +255,16 @@ func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
|||||||
|
|
||||||
for i, id := range ids {
|
for i, id := range ids {
|
||||||
ext := m[id]
|
ext := m[id]
|
||||||
if i > 0 {
|
|
||||||
b.WriteByte(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
msd, ok := messageSetMap[id]
|
msd, ok := messageSetMap[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
// Unknown type; we can't render it, so skip it.
|
// Unknown type; we can't render it, so skip it.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i > 0 && b.Len() > 1 {
|
||||||
|
b.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
||||||
|
|
||||||
x := ext.value
|
x := ext.value
|
||||||
|
639
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
639
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
@ -29,7 +29,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// +build appengine js
|
// +build purego appengine js
|
||||||
|
|
||||||
// This file contains an implementation of proto field accesses using package reflect.
|
// This file contains an implementation of proto field accesses using package reflect.
|
||||||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
||||||
@ -38,32 +38,13 @@
|
|||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A structPointer is a pointer to a struct.
|
const unsafeAllowed = false
|
||||||
type structPointer struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
// A field identifies a field in a struct, accessible from a pointer.
|
||||||
// The reflect value must itself be a pointer to a struct.
|
|
||||||
func toStructPointer(v reflect.Value) structPointer {
|
|
||||||
return structPointer{v}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
|
||||||
func structPointer_IsNil(p structPointer) bool {
|
|
||||||
return p.v.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface returns the struct pointer as an interface value.
|
|
||||||
func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
|
|
||||||
return p.v.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a structPointer.
|
|
||||||
// In this implementation, a field is identified by the sequence of field indices
|
// In this implementation, a field is identified by the sequence of field indices
|
||||||
// passed to reflect's FieldByIndex.
|
// passed to reflect's FieldByIndex.
|
||||||
type field []int
|
type field []int
|
||||||
@ -76,409 +57,301 @@ func toField(f *reflect.StructField) field {
|
|||||||
// invalidField is an invalid field identifier.
|
// invalidField is an invalid field identifier.
|
||||||
var invalidField = field(nil)
|
var invalidField = field(nil)
|
||||||
|
|
||||||
|
// zeroField is a noop when calling pointer.offset.
|
||||||
|
var zeroField = field([]int{})
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
// IsValid reports whether the field identifier is valid.
|
||||||
func (f field) IsValid() bool { return f != nil }
|
func (f field) IsValid() bool { return f != nil }
|
||||||
|
|
||||||
// field returns the given field in the struct as a reflect value.
|
// The pointer type is for the table-driven decoder.
|
||||||
func structPointer_field(p structPointer, f field) reflect.Value {
|
// The implementation here uses a reflect.Value of pointer type to
|
||||||
// Special case: an extension map entry with a value of type T
|
// create a generic pointer. In pointer_unsafe.go we use unsafe
|
||||||
// passes a *T to the struct-handling code with a zero field,
|
// instead of reflect to implement the same (but faster) interface.
|
||||||
// expecting that it will be treated as equivalent to *struct{ X T },
|
type pointer struct {
|
||||||
// which has the same memory layout. We have to handle that case
|
|
||||||
// specially, because reflect will panic if we call FieldByIndex on a
|
|
||||||
// non-struct.
|
|
||||||
if f == nil {
|
|
||||||
return p.v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.v.Elem().FieldByIndex(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ifield returns the given field in the struct as an interface value.
|
|
||||||
func structPointer_ifield(p structPointer, f field) interface{} {
|
|
||||||
return structPointer_field(p, f).Addr().Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the address of a []byte field in the struct.
|
|
||||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
|
||||||
return structPointer_ifield(p, f).(*[]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
|
||||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
|
||||||
return structPointer_ifield(p, f).(*[][]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns the address of a *bool field in the struct.
|
|
||||||
func structPointer_Bool(p structPointer, f field) **bool {
|
|
||||||
return structPointer_ifield(p, f).(**bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolVal returns the address of a bool field in the struct.
|
|
||||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
|
||||||
return structPointer_ifield(p, f).(*bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolSlice returns the address of a []bool field in the struct.
|
|
||||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
|
||||||
return structPointer_ifield(p, f).(*[]bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the address of a *string field in the struct.
|
|
||||||
func structPointer_String(p structPointer, f field) **string {
|
|
||||||
return structPointer_ifield(p, f).(**string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringVal returns the address of a string field in the struct.
|
|
||||||
func structPointer_StringVal(p structPointer, f field) *string {
|
|
||||||
return structPointer_ifield(p, f).(*string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice returns the address of a []string field in the struct.
|
|
||||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
|
||||||
return structPointer_ifield(p, f).(*[]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extensions returns the address of an extension map field in the struct.
|
|
||||||
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
|
|
||||||
return structPointer_ifield(p, f).(*XXX_InternalExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtMap returns the address of an extension map field in the struct.
|
|
||||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
|
||||||
return structPointer_ifield(p, f).(*map[int32]Extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
|
||||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
|
||||||
return structPointer_field(p, f).Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStructPointer writes a *struct field in the struct.
|
|
||||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
|
||||||
structPointer_field(p, f).Set(q.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStructPointer reads a *struct field in the struct.
|
|
||||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
|
||||||
return structPointer{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructPointerSlice the address of a []*struct field in the struct.
|
|
||||||
func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
|
|
||||||
return structPointerSlice{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A structPointerSlice represents the address of a slice of pointers to structs
|
|
||||||
// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
|
|
||||||
type structPointerSlice struct {
|
|
||||||
v reflect.Value
|
v reflect.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p structPointerSlice) Len() int { return p.v.Len() }
|
// toPointer converts an interface of pointer type to a pointer
|
||||||
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
|
// that points to the same target.
|
||||||
func (p structPointerSlice) Append(q structPointer) {
|
func toPointer(i *Message) pointer {
|
||||||
p.v.Set(reflect.Append(p.v, q.v))
|
return pointer{v: reflect.ValueOf(*i)}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
int32Type = reflect.TypeOf(int32(0))
|
// the interface data.
|
||||||
uint32Type = reflect.TypeOf(uint32(0))
|
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||||
float32Type = reflect.TypeOf(float32(0))
|
v := reflect.ValueOf(*i)
|
||||||
int64Type = reflect.TypeOf(int64(0))
|
u := reflect.New(v.Type())
|
||||||
uint64Type = reflect.TypeOf(uint64(0))
|
u.Elem().Set(v)
|
||||||
float64Type = reflect.TypeOf(float64(0))
|
return pointer{v: u}
|
||||||
)
|
|
||||||
|
|
||||||
// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
|
|
||||||
// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
|
|
||||||
type word32 struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||||
func word32_IsNil(p word32) bool {
|
func valToPointer(v reflect.Value) pointer {
|
||||||
|
return pointer{v: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset converts from a pointer to a structure to a pointer to
|
||||||
|
// one of its fields.
|
||||||
|
func (p pointer) offset(f field) pointer {
|
||||||
|
return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pointer) isNil() bool {
|
||||||
return p.v.IsNil()
|
return p.v.IsNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets p to point at a newly allocated word with bits set to x.
|
// grow updates the slice s in place to make it one element longer.
|
||||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
// s must be addressable.
|
||||||
t := p.v.Type().Elem()
|
// Returns the (addressable) new element.
|
||||||
switch t {
|
func grow(s reflect.Value) reflect.Value {
|
||||||
case int32Type:
|
n, m := s.Len(), s.Cap()
|
||||||
if len(o.int32s) == 0 {
|
|
||||||
o.int32s = make([]int32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.int32s[0] = int32(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.int32s[0]))
|
|
||||||
o.int32s = o.int32s[1:]
|
|
||||||
return
|
|
||||||
case uint32Type:
|
|
||||||
if len(o.uint32s) == 0 {
|
|
||||||
o.uint32s = make([]uint32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.uint32s[0] = x
|
|
||||||
p.v.Set(reflect.ValueOf(&o.uint32s[0]))
|
|
||||||
o.uint32s = o.uint32s[1:]
|
|
||||||
return
|
|
||||||
case float32Type:
|
|
||||||
if len(o.float32s) == 0 {
|
|
||||||
o.float32s = make([]float32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.float32s[0] = math.Float32frombits(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.float32s[0]))
|
|
||||||
o.float32s = o.float32s[1:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be enum
|
|
||||||
p.v.Set(reflect.New(t))
|
|
||||||
p.v.Elem().SetInt(int64(int32(x)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the bits pointed at by p, as a uint32.
|
|
||||||
func word32_Get(p word32) uint32 {
|
|
||||||
elem := p.v.Elem()
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
|
|
||||||
func structPointer_Word32(p structPointer, f field) word32 {
|
|
||||||
return word32{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Val represents a field of type int32, uint32, float32, or enum.
|
|
||||||
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
|
|
||||||
type word32Val struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets *p to x.
|
|
||||||
func word32Val_Set(p word32Val, x uint32) {
|
|
||||||
switch p.v.Type() {
|
|
||||||
case int32Type:
|
|
||||||
p.v.SetInt(int64(x))
|
|
||||||
return
|
|
||||||
case uint32Type:
|
|
||||||
p.v.SetUint(uint64(x))
|
|
||||||
return
|
|
||||||
case float32Type:
|
|
||||||
p.v.SetFloat(float64(math.Float32frombits(x)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be enum
|
|
||||||
p.v.SetInt(int64(int32(x)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the bits pointed at by p, as a uint32.
|
|
||||||
func word32Val_Get(p word32Val) uint32 {
|
|
||||||
elem := p.v
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
|
|
||||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
|
||||||
return word32Val{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Slice is a slice of 32-bit values.
|
|
||||||
// That is, v.Type() is []int32, []uint32, []float32, or []enum.
|
|
||||||
type word32Slice struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word32Slice) Append(x uint32) {
|
|
||||||
n, m := p.v.Len(), p.v.Cap()
|
|
||||||
if n < m {
|
if n < m {
|
||||||
p.v.SetLen(n + 1)
|
s.SetLen(n + 1)
|
||||||
} else {
|
} else {
|
||||||
t := p.v.Type().Elem()
|
s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem())))
|
||||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
|
||||||
}
|
|
||||||
elem := p.v.Index(n)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
elem.SetInt(int64(int32(x)))
|
|
||||||
case reflect.Uint32:
|
|
||||||
elem.SetUint(uint64(x))
|
|
||||||
case reflect.Float32:
|
|
||||||
elem.SetFloat(float64(math.Float32frombits(x)))
|
|
||||||
}
|
}
|
||||||
|
return s.Index(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p word32Slice) Len() int {
|
func (p pointer) toInt64() *int64 {
|
||||||
return p.v.Len()
|
return p.v.Interface().(*int64)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Ptr() **int64 {
|
||||||
|
return p.v.Interface().(**int64)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Slice() *[]int64 {
|
||||||
|
return p.v.Interface().(*[]int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p word32Slice) Index(i int) uint32 {
|
var int32ptr = reflect.TypeOf((*int32)(nil))
|
||||||
elem := p.v.Index(i)
|
|
||||||
switch elem.Kind() {
|
func (p pointer) toInt32() *int32 {
|
||||||
case reflect.Int32:
|
return p.v.Convert(int32ptr).Interface().(*int32)
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
|
// The toInt32Ptr/Slice methods don't work because of enums.
|
||||||
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
|
// Instead, we must use set/get methods for the int32ptr/slice case.
|
||||||
return word32Slice{structPointer_field(p, f)}
|
/*
|
||||||
|
func (p pointer) toInt32Ptr() **int32 {
|
||||||
|
return p.v.Interface().(**int32)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32Slice() *[]int32 {
|
||||||
|
return p.v.Interface().(*[]int32)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p pointer) getInt32Ptr() *int32 {
|
||||||
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
|
// raw int32 type
|
||||||
|
return p.v.Elem().Interface().(*int32)
|
||||||
|
}
|
||||||
|
// an enum
|
||||||
|
return p.v.Elem().Convert(int32PtrType).Interface().(*int32)
|
||||||
|
}
|
||||||
|
func (p pointer) setInt32Ptr(v int32) {
|
||||||
|
// Allocate value in a *int32. Possibly convert that to a *enum.
|
||||||
|
// Then assign it to a **int32 or **enum.
|
||||||
|
// Note: we can convert *int32 to *enum, but we can't convert
|
||||||
|
// **int32 to **enum!
|
||||||
|
p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// word64 is like word32 but for 64-bit values.
|
// getInt32Slice copies []int32 from p as a new slice.
|
||||||
type word64 struct {
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
v reflect.Value
|
func (p pointer) getInt32Slice() []int32 {
|
||||||
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
|
// raw int32 type
|
||||||
|
return p.v.Elem().Interface().([]int32)
|
||||||
|
}
|
||||||
|
// an enum
|
||||||
|
// Allocate a []int32, then assign []enum's values into it.
|
||||||
|
// Note: we can't convert []enum to []int32.
|
||||||
|
slice := p.v.Elem()
|
||||||
|
s := make([]int32, slice.Len())
|
||||||
|
for i := 0; i < slice.Len(); i++ {
|
||||||
|
s[i] = int32(slice.Index(i).Int())
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
// setInt32Slice copies []int32 into p as a new slice.
|
||||||
t := p.v.Type().Elem()
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
switch t {
|
func (p pointer) setInt32Slice(v []int32) {
|
||||||
case int64Type:
|
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
|
||||||
if len(o.int64s) == 0 {
|
// raw int32 type
|
||||||
o.int64s = make([]int64, uint64PoolSize)
|
p.v.Elem().Set(reflect.ValueOf(v))
|
||||||
}
|
|
||||||
o.int64s[0] = int64(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.int64s[0]))
|
|
||||||
o.int64s = o.int64s[1:]
|
|
||||||
return
|
|
||||||
case uint64Type:
|
|
||||||
if len(o.uint64s) == 0 {
|
|
||||||
o.uint64s = make([]uint64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.uint64s[0] = x
|
|
||||||
p.v.Set(reflect.ValueOf(&o.uint64s[0]))
|
|
||||||
o.uint64s = o.uint64s[1:]
|
|
||||||
return
|
|
||||||
case float64Type:
|
|
||||||
if len(o.float64s) == 0 {
|
|
||||||
o.float64s = make([]float64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.float64s[0] = math.Float64frombits(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.float64s[0]))
|
|
||||||
o.float64s = o.float64s[1:]
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
// an enum
|
||||||
|
// Allocate a []enum, then assign []int32's values into it.
|
||||||
|
// Note: we can't convert []enum to []int32.
|
||||||
|
slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v))
|
||||||
|
for i, x := range v {
|
||||||
|
slice.Index(i).SetInt(int64(x))
|
||||||
|
}
|
||||||
|
p.v.Elem().Set(slice)
|
||||||
|
}
|
||||||
|
func (p pointer) appendInt32Slice(v int32) {
|
||||||
|
grow(p.v.Elem()).SetInt(int64(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func word64_IsNil(p word64) bool {
|
func (p pointer) toUint64() *uint64 {
|
||||||
return p.v.IsNil()
|
return p.v.Interface().(*uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Ptr() **uint64 {
|
||||||
|
return p.v.Interface().(**uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Slice() *[]uint64 {
|
||||||
|
return p.v.Interface().(*[]uint64)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32() *uint32 {
|
||||||
|
return p.v.Interface().(*uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Ptr() **uint32 {
|
||||||
|
return p.v.Interface().(**uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Slice() *[]uint32 {
|
||||||
|
return p.v.Interface().(*[]uint32)
|
||||||
|
}
|
||||||
|
func (p pointer) toBool() *bool {
|
||||||
|
return p.v.Interface().(*bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolPtr() **bool {
|
||||||
|
return p.v.Interface().(**bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolSlice() *[]bool {
|
||||||
|
return p.v.Interface().(*[]bool)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64() *float64 {
|
||||||
|
return p.v.Interface().(*float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Ptr() **float64 {
|
||||||
|
return p.v.Interface().(**float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Slice() *[]float64 {
|
||||||
|
return p.v.Interface().(*[]float64)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32() *float32 {
|
||||||
|
return p.v.Interface().(*float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Ptr() **float32 {
|
||||||
|
return p.v.Interface().(**float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Slice() *[]float32 {
|
||||||
|
return p.v.Interface().(*[]float32)
|
||||||
|
}
|
||||||
|
func (p pointer) toString() *string {
|
||||||
|
return p.v.Interface().(*string)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringPtr() **string {
|
||||||
|
return p.v.Interface().(**string)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringSlice() *[]string {
|
||||||
|
return p.v.Interface().(*[]string)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytes() *[]byte {
|
||||||
|
return p.v.Interface().(*[]byte)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytesSlice() *[][]byte {
|
||||||
|
return p.v.Interface().(*[][]byte)
|
||||||
|
}
|
||||||
|
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
||||||
|
return p.v.Interface().(*XXX_InternalExtensions)
|
||||||
|
}
|
||||||
|
func (p pointer) toOldExtensions() *map[int32]Extension {
|
||||||
|
return p.v.Interface().(*map[int32]Extension)
|
||||||
|
}
|
||||||
|
func (p pointer) getPointer() pointer {
|
||||||
|
return pointer{v: p.v.Elem()}
|
||||||
|
}
|
||||||
|
func (p pointer) setPointer(q pointer) {
|
||||||
|
p.v.Elem().Set(q.v)
|
||||||
|
}
|
||||||
|
func (p pointer) appendPointer(q pointer) {
|
||||||
|
grow(p.v.Elem()).Set(q.v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func word64_Get(p word64) uint64 {
|
// getPointerSlice copies []*T from p as a new []pointer.
|
||||||
elem := p.v.Elem()
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
switch elem.Kind() {
|
func (p pointer) getPointerSlice() []pointer {
|
||||||
case reflect.Int64:
|
if p.v.IsNil() {
|
||||||
return uint64(elem.Int())
|
return nil
|
||||||
case reflect.Uint64:
|
|
||||||
return elem.Uint()
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(elem.Float())
|
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
n := p.v.Elem().Len()
|
||||||
|
s := make([]pointer, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
s[i] = pointer{v: p.v.Elem().Index(i)}
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func structPointer_Word64(p structPointer, f field) word64 {
|
// setPointerSlice copies []pointer into p as a new []*T.
|
||||||
return word64{structPointer_field(p, f)}
|
// This behavior differs from the implementation in pointer_unsafe.go.
|
||||||
}
|
func (p pointer) setPointerSlice(v []pointer) {
|
||||||
|
if v == nil {
|
||||||
// word64Val is like word32Val but for 64-bit values.
|
p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem())
|
||||||
type word64Val struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
|
||||||
switch p.v.Type() {
|
|
||||||
case int64Type:
|
|
||||||
p.v.SetInt(int64(x))
|
|
||||||
return
|
|
||||||
case uint64Type:
|
|
||||||
p.v.SetUint(x)
|
|
||||||
return
|
|
||||||
case float64Type:
|
|
||||||
p.v.SetFloat(math.Float64frombits(x))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v))
|
||||||
|
for _, p := range v {
|
||||||
|
s = reflect.Append(s, p.v)
|
||||||
|
}
|
||||||
|
p.v.Elem().Set(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func word64Val_Get(p word64Val) uint64 {
|
// getInterfacePointer returns a pointer that points to the
|
||||||
elem := p.v
|
// interface data of the interface pointed by p.
|
||||||
switch elem.Kind() {
|
func (p pointer) getInterfacePointer() pointer {
|
||||||
case reflect.Int64:
|
if p.v.Elem().IsNil() {
|
||||||
return uint64(elem.Int())
|
return pointer{v: p.v.Elem()}
|
||||||
case reflect.Uint64:
|
|
||||||
return elem.Uint()
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(elem.Float())
|
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct
|
||||||
}
|
}
|
||||||
|
|
||||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
||||||
return word64Val{structPointer_field(p, f)}
|
// TODO: check that p.v.Type().Elem() == t?
|
||||||
|
return p.v
|
||||||
}
|
}
|
||||||
|
|
||||||
type word64Slice struct {
|
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
||||||
v reflect.Value
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
|
}
|
||||||
|
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
||||||
|
atomicLock.Lock()
|
||||||
|
defer atomicLock.Unlock()
|
||||||
|
*p = v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p word64Slice) Append(x uint64) {
|
var atomicLock sync.Mutex
|
||||||
n, m := p.v.Len(), p.v.Cap()
|
|
||||||
if n < m {
|
|
||||||
p.v.SetLen(n + 1)
|
|
||||||
} else {
|
|
||||||
t := p.v.Type().Elem()
|
|
||||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
|
||||||
}
|
|
||||||
elem := p.v.Index(n)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
elem.SetInt(int64(int64(x)))
|
|
||||||
case reflect.Uint64:
|
|
||||||
elem.SetUint(uint64(x))
|
|
||||||
case reflect.Float64:
|
|
||||||
elem.SetFloat(float64(math.Float64frombits(x)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word64Slice) Len() int {
|
|
||||||
return p.v.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word64Slice) Index(i int) uint64 {
|
|
||||||
elem := p.v.Index(i)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
return uint64(elem.Int())
|
|
||||||
case reflect.Uint64:
|
|
||||||
return uint64(elem.Uint())
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(float64(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64Slice(p structPointer, f field) word64Slice {
|
|
||||||
return word64Slice{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
372
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
372
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
@ -29,7 +29,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// +build !appengine,!js
|
// +build !purego,!appengine,!js
|
||||||
|
|
||||||
// This file contains the implementation of the proto field accesses using package unsafe.
|
// This file contains the implementation of the proto field accesses using package unsafe.
|
||||||
|
|
||||||
@ -37,38 +37,13 @@ package proto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NOTE: These type_Foo functions would more idiomatically be methods,
|
const unsafeAllowed = true
|
||||||
// but Go does not allow methods on pointer types, and we must preserve
|
|
||||||
// some pointer type for the garbage collector. We use these
|
|
||||||
// funcs with clunky names as our poor approximation to methods.
|
|
||||||
//
|
|
||||||
// An alternative would be
|
|
||||||
// type structPointer struct { p unsafe.Pointer }
|
|
||||||
// but that does not registerize as well.
|
|
||||||
|
|
||||||
// A structPointer is a pointer to a struct.
|
// A field identifies a field in a struct, accessible from a pointer.
|
||||||
type structPointer unsafe.Pointer
|
|
||||||
|
|
||||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
|
||||||
func toStructPointer(v reflect.Value) structPointer {
|
|
||||||
return structPointer(unsafe.Pointer(v.Pointer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
|
||||||
func structPointer_IsNil(p structPointer) bool {
|
|
||||||
return p == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface returns the struct pointer, assumed to have element type t,
|
|
||||||
// as an interface value.
|
|
||||||
func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
|
|
||||||
return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a structPointer.
|
|
||||||
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
||||||
type field uintptr
|
type field uintptr
|
||||||
|
|
||||||
@ -80,191 +55,254 @@ func toField(f *reflect.StructField) field {
|
|||||||
// invalidField is an invalid field identifier.
|
// invalidField is an invalid field identifier.
|
||||||
const invalidField = ^field(0)
|
const invalidField = ^field(0)
|
||||||
|
|
||||||
|
// zeroField is a noop when calling pointer.offset.
|
||||||
|
const zeroField = field(0)
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
// IsValid reports whether the field identifier is valid.
|
||||||
func (f field) IsValid() bool {
|
func (f field) IsValid() bool {
|
||||||
return f != ^field(0)
|
return f != invalidField
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the address of a []byte field in the struct.
|
// The pointer type below is for the new table-driven encoder/decoder.
|
||||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
// The implementation here uses unsafe.Pointer to create a generic pointer.
|
||||||
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
// In pointer_reflect.go we use reflect instead of unsafe to implement
|
||||||
|
// the same (but slower) interface.
|
||||||
|
type pointer struct {
|
||||||
|
p unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
// size of pointer
|
||||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
var ptrSize = unsafe.Sizeof(uintptr(0))
|
||||||
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
|
// toPointer converts an interface of pointer type to a pointer
|
||||||
|
// that points to the same target.
|
||||||
|
func toPointer(i *Message) pointer {
|
||||||
|
// Super-tricky - read pointer out of data word of interface value.
|
||||||
|
// Saves ~25ns over the equivalent:
|
||||||
|
// return valToPointer(reflect.ValueOf(*i))
|
||||||
|
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool returns the address of a *bool field in the struct.
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
func structPointer_Bool(p structPointer, f field) **bool {
|
// the interface data.
|
||||||
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||||
|
// Super-tricky - read or get the address of data word of interface value.
|
||||||
|
if isptr {
|
||||||
|
// The interface is of pointer type, thus it is a direct interface.
|
||||||
|
// The data word is the pointer data itself. We take its address.
|
||||||
|
return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
||||||
|
}
|
||||||
|
// The interface is not of pointer type. The data word is the pointer
|
||||||
|
// to the data.
|
||||||
|
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolVal returns the address of a bool field in the struct.
|
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
func valToPointer(v reflect.Value) pointer {
|
||||||
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
return pointer{p: unsafe.Pointer(v.Pointer())}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolSlice returns the address of a []bool field in the struct.
|
// offset converts from a pointer to a structure to a pointer to
|
||||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
// one of its fields.
|
||||||
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
func (p pointer) offset(f field) pointer {
|
||||||
|
// For safety, we should panic if !f.IsValid, however calling panic causes
|
||||||
|
// this to no longer be inlineable, which is a serious performance cost.
|
||||||
|
/*
|
||||||
|
if !f.IsValid() {
|
||||||
|
panic("invalid field")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the address of a *string field in the struct.
|
func (p pointer) isNil() bool {
|
||||||
func structPointer_String(p structPointer, f field) **string {
|
return p.p == nil
|
||||||
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringVal returns the address of a string field in the struct.
|
func (p pointer) toInt64() *int64 {
|
||||||
func structPointer_StringVal(p structPointer, f field) *string {
|
return (*int64)(p.p)
|
||||||
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
}
|
||||||
|
func (p pointer) toInt64Ptr() **int64 {
|
||||||
|
return (**int64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt64Slice() *[]int64 {
|
||||||
|
return (*[]int64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32() *int32 {
|
||||||
|
return (*int32)(p.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringSlice returns the address of a []string field in the struct.
|
// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist.
|
||||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
/*
|
||||||
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
func (p pointer) toInt32Ptr() **int32 {
|
||||||
|
return (**int32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toInt32Slice() *[]int32 {
|
||||||
|
return (*[]int32)(p.p)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p pointer) getInt32Ptr() *int32 {
|
||||||
|
return *(**int32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) setInt32Ptr(v int32) {
|
||||||
|
*(**int32)(p.p) = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtMap returns the address of an extension map field in the struct.
|
// getInt32Slice loads a []int32 from p.
|
||||||
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
|
// The value returned is aliased with the original slice.
|
||||||
return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) getInt32Slice() []int32 {
|
||||||
|
return *(*[]int32)(p.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
// setInt32Slice stores a []int32 to p.
|
||||||
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
// The value set is aliased with the input slice.
|
||||||
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) setInt32Slice(v []int32) {
|
||||||
|
*(*[]int32)(p.p) = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead?
|
||||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
func (p pointer) appendInt32Slice(v int32) {
|
||||||
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
|
s := (*[]int32)(p.p)
|
||||||
|
*s = append(*s, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStructPointer writes a *struct field in the struct.
|
func (p pointer) toUint64() *uint64 {
|
||||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
return (*uint64)(p.p)
|
||||||
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
|
}
|
||||||
|
func (p pointer) toUint64Ptr() **uint64 {
|
||||||
|
return (**uint64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint64Slice() *[]uint64 {
|
||||||
|
return (*[]uint64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32() *uint32 {
|
||||||
|
return (*uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Ptr() **uint32 {
|
||||||
|
return (**uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toUint32Slice() *[]uint32 {
|
||||||
|
return (*[]uint32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBool() *bool {
|
||||||
|
return (*bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolPtr() **bool {
|
||||||
|
return (**bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBoolSlice() *[]bool {
|
||||||
|
return (*[]bool)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64() *float64 {
|
||||||
|
return (*float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Ptr() **float64 {
|
||||||
|
return (**float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat64Slice() *[]float64 {
|
||||||
|
return (*[]float64)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32() *float32 {
|
||||||
|
return (*float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Ptr() **float32 {
|
||||||
|
return (**float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toFloat32Slice() *[]float32 {
|
||||||
|
return (*[]float32)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toString() *string {
|
||||||
|
return (*string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringPtr() **string {
|
||||||
|
return (**string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toStringSlice() *[]string {
|
||||||
|
return (*[]string)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytes() *[]byte {
|
||||||
|
return (*[]byte)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toBytesSlice() *[][]byte {
|
||||||
|
return (*[][]byte)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toExtensions() *XXX_InternalExtensions {
|
||||||
|
return (*XXX_InternalExtensions)(p.p)
|
||||||
|
}
|
||||||
|
func (p pointer) toOldExtensions() *map[int32]Extension {
|
||||||
|
return (*map[int32]Extension)(p.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStructPointer reads a *struct field in the struct.
|
// getPointerSlice loads []*T from p as a []pointer.
|
||||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
// The value returned is aliased with the original slice.
|
||||||
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) getPointerSlice() []pointer {
|
||||||
|
// Super-tricky - p should point to a []*T where T is a
|
||||||
|
// message type. We load it as []pointer.
|
||||||
|
return *(*[]pointer)(p.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructPointerSlice the address of a []*struct field in the struct.
|
// setPointerSlice stores []pointer into p as a []*T.
|
||||||
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
|
// The value set is aliased with the input slice.
|
||||||
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
// This behavior differs from the implementation in pointer_reflect.go.
|
||||||
|
func (p pointer) setPointerSlice(v []pointer) {
|
||||||
|
// Super-tricky - p should point to a []*T where T is a
|
||||||
|
// message type. We store it as []pointer.
|
||||||
|
*(*[]pointer)(p.p) = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
|
// getPointer loads the pointer at p and returns it.
|
||||||
type structPointerSlice []structPointer
|
func (p pointer) getPointer() pointer {
|
||||||
|
return pointer{p: *(*unsafe.Pointer)(p.p)}
|
||||||
func (v *structPointerSlice) Len() int { return len(*v) }
|
|
||||||
func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
|
|
||||||
func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
|
|
||||||
|
|
||||||
// A word32 is the address of a "pointer to 32-bit value" field.
|
|
||||||
type word32 **uint32
|
|
||||||
|
|
||||||
// IsNil reports whether *v is nil.
|
|
||||||
func word32_IsNil(p word32) bool {
|
|
||||||
return *p == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets *v to point at a newly allocated word set to x.
|
// setPointer stores the pointer q at p.
|
||||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
func (p pointer) setPointer(q pointer) {
|
||||||
if len(o.uint32s) == 0 {
|
*(*unsafe.Pointer)(p.p) = q.p
|
||||||
o.uint32s = make([]uint32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.uint32s[0] = x
|
|
||||||
*p = &o.uint32s[0]
|
|
||||||
o.uint32s = o.uint32s[1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets the value pointed at by *v.
|
// append q to the slice pointed to by p.
|
||||||
func word32_Get(p word32) uint32 {
|
func (p pointer) appendPointer(q pointer) {
|
||||||
return **p
|
s := (*[]unsafe.Pointer)(p.p)
|
||||||
|
*s = append(*s, q.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
// getInterfacePointer returns a pointer that points to the
|
||||||
func structPointer_Word32(p structPointer, f field) word32 {
|
// interface data of the interface pointed by p.
|
||||||
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
func (p pointer) getInterfacePointer() pointer {
|
||||||
|
// Super-tricky - read pointer out of data word of interface value.
|
||||||
|
return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A word32Val is the address of a 32-bit value field.
|
// asPointerTo returns a reflect.Value that is a pointer to an
|
||||||
type word32Val *uint32
|
// object of type t stored at p.
|
||||||
|
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
|
||||||
// Set sets *p to x.
|
return reflect.NewAt(t, p.p)
|
||||||
func word32Val_Set(p word32Val, x uint32) {
|
|
||||||
*p = x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets the value pointed at by p.
|
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
|
||||||
func word32Val_Get(p word32Val) uint32 {
|
return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
return *p
|
|
||||||
}
|
}
|
||||||
|
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
|
||||||
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
|
||||||
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
}
|
||||||
|
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
|
||||||
// A word32Slice is a slice of 32-bit values.
|
return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
type word32Slice []uint32
|
|
||||||
|
|
||||||
func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
|
|
||||||
func (v *word32Slice) Len() int { return len(*v) }
|
|
||||||
func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
|
|
||||||
|
|
||||||
// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
|
|
||||||
func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
|
|
||||||
return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
}
|
||||||
|
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
|
||||||
// word64 is like word32 but for 64-bit values.
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
type word64 **uint64
|
|
||||||
|
|
||||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
|
||||||
if len(o.uint64s) == 0 {
|
|
||||||
o.uint64s = make([]uint64, uint64PoolSize)
|
|
||||||
}
|
}
|
||||||
o.uint64s[0] = x
|
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
|
||||||
*p = &o.uint64s[0]
|
return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
o.uint64s = o.uint64s[1:]
|
|
||||||
}
|
}
|
||||||
|
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
|
||||||
func word64_IsNil(p word64) bool {
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
return *p == nil
|
|
||||||
}
|
}
|
||||||
|
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
|
||||||
func word64_Get(p word64) uint64 {
|
return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||||
return **p
|
|
||||||
}
|
}
|
||||||
|
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
|
||||||
func structPointer_Word64(p structPointer, f field) word64 {
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
|
||||||
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64Val is like word32Val but for 64-bit values.
|
|
||||||
type word64Val *uint64
|
|
||||||
|
|
||||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
|
||||||
*p = x
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64Val_Get(p word64Val) uint64 {
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
|
||||||
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64Slice is like word32Slice but for 64-bit values.
|
|
||||||
type word64Slice []uint64
|
|
||||||
|
|
||||||
func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
|
|
||||||
func (v *word64Slice) Len() int { return len(*v) }
|
|
||||||
func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
|
|
||||||
|
|
||||||
func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
|
|
||||||
return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
}
|
||||||
|
426
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
426
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
@ -58,42 +58,6 @@ const (
|
|||||||
WireFixed32 = 5
|
WireFixed32 = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
const startSize = 10 // initial slice/string sizes
|
|
||||||
|
|
||||||
// Encoders are defined in encode.go
|
|
||||||
// An encoder outputs the full representation of a field, including its
|
|
||||||
// tag and encoder type.
|
|
||||||
type encoder func(p *Buffer, prop *Properties, base structPointer) error
|
|
||||||
|
|
||||||
// A valueEncoder encodes a single integer in a particular encoding.
|
|
||||||
type valueEncoder func(o *Buffer, x uint64) error
|
|
||||||
|
|
||||||
// Sizers are defined in encode.go
|
|
||||||
// A sizer returns the encoded size of a field, including its tag and encoder
|
|
||||||
// type.
|
|
||||||
type sizer func(prop *Properties, base structPointer) int
|
|
||||||
|
|
||||||
// A valueSizer returns the encoded size of a single integer in a particular
|
|
||||||
// encoding.
|
|
||||||
type valueSizer func(x uint64) int
|
|
||||||
|
|
||||||
// Decoders are defined in decode.go
|
|
||||||
// A decoder creates a value from its wire representation.
|
|
||||||
// Unrecognized subelements are saved in unrec.
|
|
||||||
type decoder func(p *Buffer, prop *Properties, base structPointer) error
|
|
||||||
|
|
||||||
// A valueDecoder decodes a single integer in a particular encoding.
|
|
||||||
type valueDecoder func(o *Buffer) (x uint64, err error)
|
|
||||||
|
|
||||||
// A oneofMarshaler does the marshaling for all oneof fields in a message.
|
|
||||||
type oneofMarshaler func(Message, *Buffer) error
|
|
||||||
|
|
||||||
// A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
|
|
||||||
type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error)
|
|
||||||
|
|
||||||
// A oneofSizer does the sizing for all oneof fields in a message.
|
|
||||||
type oneofSizer func(Message) int
|
|
||||||
|
|
||||||
// tagMap is an optimization over map[int]int for typical protocol buffer
|
// tagMap is an optimization over map[int]int for typical protocol buffer
|
||||||
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
||||||
// numbers.
|
// numbers.
|
||||||
@ -140,13 +104,6 @@ type StructProperties struct {
|
|||||||
decoderTags tagMap // map from proto tag to struct field number
|
decoderTags tagMap // map from proto tag to struct field number
|
||||||
decoderOrigNames map[string]int // map from original name to struct field number
|
decoderOrigNames map[string]int // map from original name to struct field number
|
||||||
order []int // list of struct field numbers in tag order
|
order []int // list of struct field numbers in tag order
|
||||||
unrecField field // field id of the XXX_unrecognized []byte field
|
|
||||||
extendable bool // is this an extendable proto
|
|
||||||
|
|
||||||
oneofMarshaler oneofMarshaler
|
|
||||||
oneofUnmarshaler oneofUnmarshaler
|
|
||||||
oneofSizer oneofSizer
|
|
||||||
stype reflect.Type
|
|
||||||
|
|
||||||
// OneofTypes contains information about the oneof fields in this message.
|
// OneofTypes contains information about the oneof fields in this message.
|
||||||
// It is keyed by the original name of a field.
|
// It is keyed by the original name of a field.
|
||||||
@ -182,41 +139,24 @@ type Properties struct {
|
|||||||
Repeated bool
|
Repeated bool
|
||||||
Packed bool // relevant for repeated primitives only
|
Packed bool // relevant for repeated primitives only
|
||||||
Enum string // set for enum types only
|
Enum string // set for enum types only
|
||||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
proto3 bool // whether this is known to be a proto3 field
|
||||||
oneof bool // whether this is a oneof field
|
oneof bool // whether this is a oneof field
|
||||||
|
|
||||||
Default string // default value
|
Default string // default value
|
||||||
HasDefault bool // whether an explicit default was provided
|
HasDefault bool // whether an explicit default was provided
|
||||||
def_uint64 uint64
|
|
||||||
|
|
||||||
enc encoder
|
|
||||||
valEnc valueEncoder // set for bool and numeric types only
|
|
||||||
field field
|
|
||||||
tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
|
|
||||||
tagbuf [8]byte
|
|
||||||
stype reflect.Type // set for struct types only
|
stype reflect.Type // set for struct types only
|
||||||
sprop *StructProperties // set for struct types only
|
sprop *StructProperties // set for struct types only
|
||||||
isMarshaler bool
|
|
||||||
isUnmarshaler bool
|
|
||||||
|
|
||||||
mtype reflect.Type // set for map types only
|
mtype reflect.Type // set for map types only
|
||||||
mkeyprop *Properties // set for map types only
|
MapKeyProp *Properties // set for map types only
|
||||||
mvalprop *Properties // set for map types only
|
MapValProp *Properties // set for map types only
|
||||||
|
|
||||||
size sizer
|
|
||||||
valSize valueSizer // set for bool and numeric types only
|
|
||||||
|
|
||||||
dec decoder
|
|
||||||
valDec valueDecoder // set for bool and numeric types only
|
|
||||||
|
|
||||||
// If this is a packable field, this will be the decoder for the packed version of the field.
|
|
||||||
packedDec decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String formats the properties in the protobuf struct field tag style.
|
// String formats the properties in the protobuf struct field tag style.
|
||||||
func (p *Properties) String() string {
|
func (p *Properties) String() string {
|
||||||
s := p.Wire
|
s := p.Wire
|
||||||
s = ","
|
s += ","
|
||||||
s += strconv.Itoa(p.Tag)
|
s += strconv.Itoa(p.Tag)
|
||||||
if p.Required {
|
if p.Required {
|
||||||
s += ",req"
|
s += ",req"
|
||||||
@ -262,29 +202,14 @@ func (p *Properties) Parse(s string) {
|
|||||||
switch p.Wire {
|
switch p.Wire {
|
||||||
case "varint":
|
case "varint":
|
||||||
p.WireType = WireVarint
|
p.WireType = WireVarint
|
||||||
p.valEnc = (*Buffer).EncodeVarint
|
|
||||||
p.valDec = (*Buffer).DecodeVarint
|
|
||||||
p.valSize = sizeVarint
|
|
||||||
case "fixed32":
|
case "fixed32":
|
||||||
p.WireType = WireFixed32
|
p.WireType = WireFixed32
|
||||||
p.valEnc = (*Buffer).EncodeFixed32
|
|
||||||
p.valDec = (*Buffer).DecodeFixed32
|
|
||||||
p.valSize = sizeFixed32
|
|
||||||
case "fixed64":
|
case "fixed64":
|
||||||
p.WireType = WireFixed64
|
p.WireType = WireFixed64
|
||||||
p.valEnc = (*Buffer).EncodeFixed64
|
|
||||||
p.valDec = (*Buffer).DecodeFixed64
|
|
||||||
p.valSize = sizeFixed64
|
|
||||||
case "zigzag32":
|
case "zigzag32":
|
||||||
p.WireType = WireVarint
|
p.WireType = WireVarint
|
||||||
p.valEnc = (*Buffer).EncodeZigzag32
|
|
||||||
p.valDec = (*Buffer).DecodeZigzag32
|
|
||||||
p.valSize = sizeZigzag32
|
|
||||||
case "zigzag64":
|
case "zigzag64":
|
||||||
p.WireType = WireVarint
|
p.WireType = WireVarint
|
||||||
p.valEnc = (*Buffer).EncodeZigzag64
|
|
||||||
p.valDec = (*Buffer).DecodeZigzag64
|
|
||||||
p.valSize = sizeZigzag64
|
|
||||||
case "bytes", "group":
|
case "bytes", "group":
|
||||||
p.WireType = WireBytes
|
p.WireType = WireBytes
|
||||||
// no numeric converter for non-numeric types
|
// no numeric converter for non-numeric types
|
||||||
@ -299,6 +224,7 @@ func (p *Properties) Parse(s string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outer:
|
||||||
for i := 2; i < len(fields); i++ {
|
for i := 2; i < len(fields); i++ {
|
||||||
f := fields[i]
|
f := fields[i]
|
||||||
switch {
|
switch {
|
||||||
@ -326,256 +252,41 @@ func (p *Properties) Parse(s string) {
|
|||||||
if i+1 < len(fields) {
|
if i+1 < len(fields) {
|
||||||
// Commas aren't escaped, and def is always last.
|
// Commas aren't escaped, and def is always last.
|
||||||
p.Default += "," + strings.Join(fields[i+1:], ",")
|
p.Default += "," + strings.Join(fields[i+1:], ",")
|
||||||
break
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func logNoSliceEnc(t1, t2 reflect.Type) {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
|
|
||||||
}
|
|
||||||
|
|
||||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
||||||
|
|
||||||
// Initialize the fields for encoding and decoding.
|
// setFieldProps initializes the field properties for submessages and maps.
|
||||||
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
||||||
p.enc = nil
|
|
||||||
p.dec = nil
|
|
||||||
p.size = nil
|
|
||||||
|
|
||||||
switch t1 := typ; t1.Kind() {
|
switch t1 := typ; t1.Kind() {
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
|
|
||||||
|
|
||||||
// proto3 scalar types
|
|
||||||
|
|
||||||
case reflect.Bool:
|
|
||||||
p.enc = (*Buffer).enc_proto3_bool
|
|
||||||
p.dec = (*Buffer).dec_proto3_bool
|
|
||||||
p.size = size_proto3_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int32
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32
|
|
||||||
p.size = size_proto3_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_uint32
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32 // can reuse
|
|
||||||
p.size = size_proto3_uint32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int64
|
|
||||||
p.dec = (*Buffer).dec_proto3_int64
|
|
||||||
p.size = size_proto3_int64
|
|
||||||
case reflect.Float32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32
|
|
||||||
p.size = size_proto3_uint32
|
|
||||||
case reflect.Float64:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_proto3_int64
|
|
||||||
p.size = size_proto3_int64
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_proto3_string
|
|
||||||
p.dec = (*Buffer).dec_proto3_string
|
|
||||||
p.size = size_proto3_string
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
switch t2 := t1.Elem(); t2.Kind() {
|
if t1.Elem().Kind() == reflect.Struct {
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
|
|
||||||
break
|
|
||||||
case reflect.Bool:
|
|
||||||
p.enc = (*Buffer).enc_bool
|
|
||||||
p.dec = (*Buffer).dec_bool
|
|
||||||
p.size = size_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
p.enc = (*Buffer).enc_int32
|
|
||||||
p.dec = (*Buffer).dec_int32
|
|
||||||
p.size = size_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
p.enc = (*Buffer).enc_uint32
|
|
||||||
p.dec = (*Buffer).dec_int32 // can reuse
|
|
||||||
p.size = size_uint32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
p.enc = (*Buffer).enc_int64
|
|
||||||
p.dec = (*Buffer).dec_int64
|
|
||||||
p.size = size_int64
|
|
||||||
case reflect.Float32:
|
|
||||||
p.enc = (*Buffer).enc_uint32 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_int32
|
|
||||||
p.size = size_uint32
|
|
||||||
case reflect.Float64:
|
|
||||||
p.enc = (*Buffer).enc_int64 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_int64
|
|
||||||
p.size = size_int64
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_string
|
|
||||||
p.dec = (*Buffer).dec_string
|
|
||||||
p.size = size_string
|
|
||||||
case reflect.Struct:
|
|
||||||
p.stype = t1.Elem()
|
p.stype = t1.Elem()
|
||||||
p.isMarshaler = isMarshaler(t1)
|
|
||||||
p.isUnmarshaler = isUnmarshaler(t1)
|
|
||||||
if p.Wire == "bytes" {
|
|
||||||
p.enc = (*Buffer).enc_struct_message
|
|
||||||
p.dec = (*Buffer).dec_struct_message
|
|
||||||
p.size = size_struct_message
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_struct_group
|
|
||||||
p.dec = (*Buffer).dec_struct_group
|
|
||||||
p.size = size_struct_group
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
switch t2 := t1.Elem(); t2.Kind() {
|
if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct {
|
||||||
default:
|
|
||||||
logNoSliceEnc(t1, t2)
|
|
||||||
break
|
|
||||||
case reflect.Bool:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_bool
|
|
||||||
p.size = size_slice_packed_bool
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_bool
|
|
||||||
p.size = size_slice_bool
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_bool
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int32
|
|
||||||
p.size = size_slice_packed_int32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int32
|
|
||||||
p.size = size_slice_int32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
|
||||||
p.size = size_slice_packed_uint32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_uint32
|
|
||||||
p.size = size_slice_uint32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int64
|
|
||||||
p.size = size_slice_packed_int64
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int64
|
|
||||||
p.size = size_slice_int64
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int64
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
|
||||||
case reflect.Uint8:
|
|
||||||
p.dec = (*Buffer).dec_slice_byte
|
|
||||||
if p.proto3 {
|
|
||||||
p.enc = (*Buffer).enc_proto3_slice_byte
|
|
||||||
p.size = size_proto3_slice_byte
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_byte
|
|
||||||
p.size = size_slice_byte
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
switch t2.Bits() {
|
|
||||||
case 32:
|
|
||||||
// can just treat them as bits
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
|
||||||
p.size = size_slice_packed_uint32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_uint32
|
|
||||||
p.size = size_slice_uint32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case 64:
|
|
||||||
// can just treat them as bits
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int64
|
|
||||||
p.size = size_slice_packed_int64
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int64
|
|
||||||
p.size = size_slice_int64
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int64
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
|
||||||
default:
|
|
||||||
logNoSliceEnc(t1, t2)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_slice_string
|
|
||||||
p.dec = (*Buffer).dec_slice_string
|
|
||||||
p.size = size_slice_string
|
|
||||||
case reflect.Ptr:
|
|
||||||
switch t3 := t2.Elem(); t3.Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
|
|
||||||
break
|
|
||||||
case reflect.Struct:
|
|
||||||
p.stype = t2.Elem()
|
p.stype = t2.Elem()
|
||||||
p.isMarshaler = isMarshaler(t2)
|
|
||||||
p.isUnmarshaler = isUnmarshaler(t2)
|
|
||||||
if p.Wire == "bytes" {
|
|
||||||
p.enc = (*Buffer).enc_slice_struct_message
|
|
||||||
p.dec = (*Buffer).dec_slice_struct_message
|
|
||||||
p.size = size_slice_struct_message
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_struct_group
|
|
||||||
p.dec = (*Buffer).dec_slice_struct_group
|
|
||||||
p.size = size_slice_struct_group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
switch t2.Elem().Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
|
|
||||||
break
|
|
||||||
case reflect.Uint8:
|
|
||||||
p.enc = (*Buffer).enc_slice_slice_byte
|
|
||||||
p.dec = (*Buffer).dec_slice_slice_byte
|
|
||||||
p.size = size_slice_slice_byte
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
p.enc = (*Buffer).enc_new_map
|
|
||||||
p.dec = (*Buffer).dec_new_map
|
|
||||||
p.size = size_new_map
|
|
||||||
|
|
||||||
p.mtype = t1
|
p.mtype = t1
|
||||||
p.mkeyprop = &Properties{}
|
p.MapKeyProp = &Properties{}
|
||||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||||
p.mvalprop = &Properties{}
|
p.MapValProp = &Properties{}
|
||||||
vtype := p.mtype.Elem()
|
vtype := p.mtype.Elem()
|
||||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||||
// The value type is not a message (*T) or bytes ([]byte),
|
// The value type is not a message (*T) or bytes ([]byte),
|
||||||
// so we need encoders for the pointer to this type.
|
// so we need encoders for the pointer to this type.
|
||||||
vtype = reflect.PtrTo(vtype)
|
vtype = reflect.PtrTo(vtype)
|
||||||
}
|
}
|
||||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// precalculate tag code
|
|
||||||
wire := p.WireType
|
|
||||||
if p.Packed {
|
|
||||||
wire = WireBytes
|
|
||||||
}
|
|
||||||
x := uint32(p.Tag)<<3 | uint32(wire)
|
|
||||||
i := 0
|
|
||||||
for i = 0; x > 127; i++ {
|
|
||||||
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
|
|
||||||
x >>= 7
|
|
||||||
}
|
|
||||||
p.tagbuf[i] = uint8(x)
|
|
||||||
p.tagcode = p.tagbuf[0 : i+1]
|
|
||||||
|
|
||||||
if p.stype != nil {
|
if p.stype != nil {
|
||||||
if lockGetProp {
|
if lockGetProp {
|
||||||
p.sprop = GetProperties(p.stype)
|
p.sprop = GetProperties(p.stype)
|
||||||
@ -587,31 +298,8 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// isMarshaler reports whether type t implements Marshaler.
|
|
||||||
func isMarshaler(t reflect.Type) bool {
|
|
||||||
// We're checking for (likely) pointer-receiver methods
|
|
||||||
// so if t is not a pointer, something is very wrong.
|
|
||||||
// The calls above only invoke isMarshaler on pointer types.
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
panic("proto: misuse of isMarshaler")
|
|
||||||
}
|
|
||||||
return t.Implements(marshalerType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isUnmarshaler reports whether type t implements Unmarshaler.
|
|
||||||
func isUnmarshaler(t reflect.Type) bool {
|
|
||||||
// We're checking for (likely) pointer-receiver methods
|
|
||||||
// so if t is not a pointer, something is very wrong.
|
|
||||||
// The calls above only invoke isUnmarshaler on pointer types.
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
panic("proto: misuse of isUnmarshaler")
|
|
||||||
}
|
|
||||||
return t.Implements(unmarshalerType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init populates the properties from a protocol buffer struct tag.
|
// Init populates the properties from a protocol buffer struct tag.
|
||||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
||||||
p.init(typ, name, tag, f, true)
|
p.init(typ, name, tag, f, true)
|
||||||
@ -621,14 +309,11 @@ func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructF
|
|||||||
// "bytes,49,opt,def=hello!"
|
// "bytes,49,opt,def=hello!"
|
||||||
p.Name = name
|
p.Name = name
|
||||||
p.OrigName = name
|
p.OrigName = name
|
||||||
if f != nil {
|
|
||||||
p.field = toField(f)
|
|
||||||
}
|
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Parse(tag)
|
p.Parse(tag)
|
||||||
p.setEncAndDec(typ, f, lockGetProp)
|
p.setFieldProps(typ, f, lockGetProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -678,9 +363,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|||||||
propertiesMap[t] = prop
|
propertiesMap[t] = prop
|
||||||
|
|
||||||
// build properties
|
// build properties
|
||||||
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) ||
|
|
||||||
reflect.PtrTo(t).Implements(extendableProtoV1Type)
|
|
||||||
prop.unrecField = invalidField
|
|
||||||
prop.Prop = make([]*Properties, t.NumField())
|
prop.Prop = make([]*Properties, t.NumField())
|
||||||
prop.order = make([]int, t.NumField())
|
prop.order = make([]int, t.NumField())
|
||||||
|
|
||||||
@ -690,17 +372,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|||||||
name := f.Name
|
name := f.Name
|
||||||
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
||||||
|
|
||||||
if f.Name == "XXX_InternalExtensions" { // special case
|
|
||||||
p.enc = (*Buffer).enc_exts
|
|
||||||
p.dec = nil // not needed
|
|
||||||
p.size = size_exts
|
|
||||||
} else if f.Name == "XXX_extensions" { // special case
|
|
||||||
p.enc = (*Buffer).enc_map
|
|
||||||
p.dec = nil // not needed
|
|
||||||
p.size = size_map
|
|
||||||
} else if f.Name == "XXX_unrecognized" { // special case
|
|
||||||
prop.unrecField = toField(&f)
|
|
||||||
}
|
|
||||||
oneof := f.Tag.Get("protobuf_oneof") // special case
|
oneof := f.Tag.Get("protobuf_oneof") // special case
|
||||||
if oneof != "" {
|
if oneof != "" {
|
||||||
// Oneof fields don't use the traditional protobuf tag.
|
// Oneof fields don't use the traditional protobuf tag.
|
||||||
@ -715,9 +386,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
}
|
}
|
||||||
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-order prop.order.
|
// Re-order prop.order.
|
||||||
@ -728,8 +396,7 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|||||||
}
|
}
|
||||||
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
||||||
var oots []interface{}
|
var oots []interface{}
|
||||||
prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
|
_, _, _, oots = om.XXX_OneofFuncs()
|
||||||
prop.stype = t
|
|
||||||
|
|
||||||
// Interpret oneof metadata.
|
// Interpret oneof metadata.
|
||||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||||
@ -779,30 +446,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|||||||
return prop
|
return prop
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the Properties object for the x[0]'th field of the structure.
|
|
||||||
func propByIndex(t reflect.Type, x []int) *Properties {
|
|
||||||
if len(x) != 1 {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
prop := GetProperties(t)
|
|
||||||
return prop.Prop[x[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the address and type of a pointer to a struct from an interface.
|
|
||||||
func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
|
|
||||||
if pb == nil {
|
|
||||||
err = ErrNil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// get the reflect type of the pointer to the struct.
|
|
||||||
t = reflect.TypeOf(pb)
|
|
||||||
// get the address of the struct.
|
|
||||||
value := reflect.ValueOf(pb)
|
|
||||||
b = toStructPointer(value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global registry of enum types.
|
// A global registry of enum types.
|
||||||
// The generated code will register the generated maps by calling RegisterEnum.
|
// The generated code will register the generated maps by calling RegisterEnum.
|
||||||
|
|
||||||
@ -826,20 +469,42 @@ func EnumValueMap(enumType string) map[string]int32 {
|
|||||||
// A registry of all linked message types.
|
// A registry of all linked message types.
|
||||||
// The string is a fully-qualified proto name ("pkg.Message").
|
// The string is a fully-qualified proto name ("pkg.Message").
|
||||||
var (
|
var (
|
||||||
protoTypes = make(map[string]reflect.Type)
|
protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers
|
||||||
|
protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types
|
||||||
revProtoTypes = make(map[reflect.Type]string)
|
revProtoTypes = make(map[reflect.Type]string)
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterType is called from generated code and maps from the fully qualified
|
// RegisterType is called from generated code and maps from the fully qualified
|
||||||
// proto name to the type (pointer to struct) of the protocol buffer.
|
// proto name to the type (pointer to struct) of the protocol buffer.
|
||||||
func RegisterType(x Message, name string) {
|
func RegisterType(x Message, name string) {
|
||||||
if _, ok := protoTypes[name]; ok {
|
if _, ok := protoTypedNils[name]; ok {
|
||||||
// TODO: Some day, make this a panic.
|
// TODO: Some day, make this a panic.
|
||||||
log.Printf("proto: duplicate proto type registered: %s", name)
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t := reflect.TypeOf(x)
|
t := reflect.TypeOf(x)
|
||||||
protoTypes[name] = t
|
if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 {
|
||||||
|
// Generated code always calls RegisterType with nil x.
|
||||||
|
// This check is just for extra safety.
|
||||||
|
protoTypedNils[name] = x
|
||||||
|
} else {
|
||||||
|
protoTypedNils[name] = reflect.Zero(t).Interface().(Message)
|
||||||
|
}
|
||||||
|
revProtoTypes[t] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterMapType is called from generated code and maps from the fully qualified
|
||||||
|
// proto name to the native map type of the proto map definition.
|
||||||
|
func RegisterMapType(x interface{}, name string) {
|
||||||
|
if reflect.TypeOf(x).Kind() != reflect.Map {
|
||||||
|
panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name))
|
||||||
|
}
|
||||||
|
if _, ok := protoMapTypes[name]; ok {
|
||||||
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := reflect.TypeOf(x)
|
||||||
|
protoMapTypes[name] = t
|
||||||
revProtoTypes[t] = name
|
revProtoTypes[t] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,7 +520,14 @@ func MessageName(x Message) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MessageType returns the message type (pointer to struct) for a named message.
|
// MessageType returns the message type (pointer to struct) for a named message.
|
||||||
func MessageType(name string) reflect.Type { return protoTypes[name] }
|
// The type is not guaranteed to implement proto.Message if the name refers to a
|
||||||
|
// map entry.
|
||||||
|
func MessageType(name string) reflect.Type {
|
||||||
|
if t, ok := protoTypedNils[name]; ok {
|
||||||
|
return reflect.TypeOf(t)
|
||||||
|
}
|
||||||
|
return protoMapTypes[name]
|
||||||
|
}
|
||||||
|
|
||||||
// A registry of all linked proto files.
|
// A registry of all linked proto files.
|
||||||
var (
|
var (
|
||||||
|
2767
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
Normal file
2767
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
654
vendor/github.com/golang/protobuf/proto/table_merge.go
generated
vendored
Normal file
654
vendor/github.com/golang/protobuf/proto/table_merge.go
generated
vendored
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merge merges the src message into dst.
|
||||||
|
// This assumes that dst and src of the same type and are non-nil.
|
||||||
|
func (a *InternalMessageInfo) Merge(dst, src Message) {
|
||||||
|
mi := atomicLoadMergeInfo(&a.merge)
|
||||||
|
if mi == nil {
|
||||||
|
mi = getMergeInfo(reflect.TypeOf(dst).Elem())
|
||||||
|
atomicStoreMergeInfo(&a.merge, mi)
|
||||||
|
}
|
||||||
|
mi.merge(toPointer(&dst), toPointer(&src))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mergeInfo struct {
|
||||||
|
typ reflect.Type
|
||||||
|
|
||||||
|
initialized int32 // 0: only typ is valid, 1: everything is valid
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
fields []mergeFieldInfo
|
||||||
|
unrecognized field // Offset of XXX_unrecognized
|
||||||
|
}
|
||||||
|
|
||||||
|
type mergeFieldInfo struct {
|
||||||
|
field field // Offset of field, guaranteed to be valid
|
||||||
|
|
||||||
|
// isPointer reports whether the value in the field is a pointer.
|
||||||
|
// This is true for the following situations:
|
||||||
|
// * Pointer to struct
|
||||||
|
// * Pointer to basic type (proto2 only)
|
||||||
|
// * Slice (first value in slice header is a pointer)
|
||||||
|
// * String (first value in string header is a pointer)
|
||||||
|
isPointer bool
|
||||||
|
|
||||||
|
// basicWidth reports the width of the field assuming that it is directly
|
||||||
|
// embedded in the struct (as is the case for basic types in proto3).
|
||||||
|
// The possible values are:
|
||||||
|
// 0: invalid
|
||||||
|
// 1: bool
|
||||||
|
// 4: int32, uint32, float32
|
||||||
|
// 8: int64, uint64, float64
|
||||||
|
basicWidth int
|
||||||
|
|
||||||
|
// Where dst and src are pointers to the types being merged.
|
||||||
|
merge func(dst, src pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mergeInfoMap = map[reflect.Type]*mergeInfo{}
|
||||||
|
mergeInfoLock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getMergeInfo(t reflect.Type) *mergeInfo {
|
||||||
|
mergeInfoLock.Lock()
|
||||||
|
defer mergeInfoLock.Unlock()
|
||||||
|
mi := mergeInfoMap[t]
|
||||||
|
if mi == nil {
|
||||||
|
mi = &mergeInfo{typ: t}
|
||||||
|
mergeInfoMap[t] = mi
|
||||||
|
}
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge merges src into dst assuming they are both of type *mi.typ.
|
||||||
|
func (mi *mergeInfo) merge(dst, src pointer) {
|
||||||
|
if dst.isNil() {
|
||||||
|
panic("proto: nil destination")
|
||||||
|
}
|
||||||
|
if src.isNil() {
|
||||||
|
return // Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
if atomic.LoadInt32(&mi.initialized) == 0 {
|
||||||
|
mi.computeMergeInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fi := range mi.fields {
|
||||||
|
sfp := src.offset(fi.field)
|
||||||
|
|
||||||
|
// As an optimization, we can avoid the merge function call cost
|
||||||
|
// if we know for sure that the source will have no effect
|
||||||
|
// by checking if it is the zero value.
|
||||||
|
if unsafeAllowed {
|
||||||
|
if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fi.basicWidth > 0 {
|
||||||
|
switch {
|
||||||
|
case fi.basicWidth == 1 && !*sfp.toBool():
|
||||||
|
continue
|
||||||
|
case fi.basicWidth == 4 && *sfp.toUint32() == 0:
|
||||||
|
continue
|
||||||
|
case fi.basicWidth == 8 && *sfp.toUint64() == 0:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dfp := dst.offset(fi.field)
|
||||||
|
fi.merge(dfp, sfp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make this faster?
|
||||||
|
out := dst.asPointerTo(mi.typ).Elem()
|
||||||
|
in := src.asPointerTo(mi.typ).Elem()
|
||||||
|
if emIn, err := extendable(in.Addr().Interface()); err == nil {
|
||||||
|
emOut, _ := extendable(out.Addr().Interface())
|
||||||
|
mIn, muIn := emIn.extensionsRead()
|
||||||
|
if mIn != nil {
|
||||||
|
mOut := emOut.extensionsWrite()
|
||||||
|
muIn.Lock()
|
||||||
|
mergeExtension(mOut, mIn)
|
||||||
|
muIn.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mi.unrecognized.IsValid() {
|
||||||
|
if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 {
|
||||||
|
*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mi *mergeInfo) computeMergeInfo() {
|
||||||
|
mi.lock.Lock()
|
||||||
|
defer mi.lock.Unlock()
|
||||||
|
if mi.initialized != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := mi.typ
|
||||||
|
n := t.NumField()
|
||||||
|
|
||||||
|
props := GetProperties(t)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if strings.HasPrefix(f.Name, "XXX_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mfi := mergeFieldInfo{field: toField(&f)}
|
||||||
|
tf := f.Type
|
||||||
|
|
||||||
|
// As an optimization, we can avoid the merge function call cost
|
||||||
|
// if we know for sure that the source will have no effect
|
||||||
|
// by checking if it is the zero value.
|
||||||
|
if unsafeAllowed {
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Ptr, reflect.Slice, reflect.String:
|
||||||
|
// As a special case, we assume slices and strings are pointers
|
||||||
|
// since we know that the first field in the SliceSlice or
|
||||||
|
// StringHeader is a data pointer.
|
||||||
|
mfi.isPointer = true
|
||||||
|
case reflect.Bool:
|
||||||
|
mfi.basicWidth = 1
|
||||||
|
case reflect.Int32, reflect.Uint32, reflect.Float32:
|
||||||
|
mfi.basicWidth = 4
|
||||||
|
case reflect.Int64, reflect.Uint64, reflect.Float64:
|
||||||
|
mfi.basicWidth = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap tf to get at its most basic type.
|
||||||
|
var isPointer, isSlice bool
|
||||||
|
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
|
||||||
|
isSlice = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if tf.Kind() == reflect.Ptr {
|
||||||
|
isPointer = true
|
||||||
|
tf = tf.Elem()
|
||||||
|
}
|
||||||
|
if isPointer && isSlice && tf.Kind() != reflect.Struct {
|
||||||
|
panic("both pointer and slice for basic type in " + tf.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Kind() {
|
||||||
|
case reflect.Int32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
// NOTE: toInt32Slice is not defined (see pointer_reflect.go).
|
||||||
|
/*
|
||||||
|
sfsp := src.toInt32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toInt32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []int64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
sfs := src.getInt32Slice()
|
||||||
|
if sfs != nil {
|
||||||
|
dfs := dst.getInt32Slice()
|
||||||
|
dfs = append(dfs, sfs...)
|
||||||
|
if dfs == nil {
|
||||||
|
dfs = []int32{}
|
||||||
|
}
|
||||||
|
dst.setInt32Slice(dfs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
// NOTE: toInt32Ptr is not defined (see pointer_reflect.go).
|
||||||
|
/*
|
||||||
|
sfpp := src.toInt32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toInt32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Int32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
sfp := src.getInt32Ptr()
|
||||||
|
if sfp != nil {
|
||||||
|
dfp := dst.getInt32Ptr()
|
||||||
|
if dfp == nil {
|
||||||
|
dst.setInt32Ptr(*sfp)
|
||||||
|
} else {
|
||||||
|
*dfp = *sfp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., int32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toInt32(); v != 0 {
|
||||||
|
*dst.toInt32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toInt64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toInt64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []int64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toInt64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toInt64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Int64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., int64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toInt64(); v != 0 {
|
||||||
|
*dst.toInt64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toUint32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toUint32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []uint32{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toUint32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toUint32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Uint32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., uint32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toUint32(); v != 0 {
|
||||||
|
*dst.toUint32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toUint64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toUint64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []uint64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toUint64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toUint64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Uint64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., uint64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toUint64(); v != 0 {
|
||||||
|
*dst.toUint64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float32:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toFloat32Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toFloat32Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []float32{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toFloat32Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toFloat32Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Float32(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., float32
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toFloat32(); v != 0 {
|
||||||
|
*dst.toFloat32() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toFloat64Slice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toFloat64Slice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []float64{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toFloat64Ptr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toFloat64Ptr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Float64(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., float64
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toFloat64(); v != 0 {
|
||||||
|
*dst.toFloat64() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toBoolSlice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toBoolSlice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []bool{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toBoolPtr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toBoolPtr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = Bool(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., bool
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toBool(); v {
|
||||||
|
*dst.toBool() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
switch {
|
||||||
|
case isSlice: // E.g., []string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfsp := src.toStringSlice()
|
||||||
|
if *sfsp != nil {
|
||||||
|
dfsp := dst.toStringSlice()
|
||||||
|
*dfsp = append(*dfsp, *sfsp...)
|
||||||
|
if *dfsp == nil {
|
||||||
|
*dfsp = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case isPointer: // E.g., *string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sfpp := src.toStringPtr()
|
||||||
|
if *sfpp != nil {
|
||||||
|
dfpp := dst.toStringPtr()
|
||||||
|
if *dfpp == nil {
|
||||||
|
*dfpp = String(**sfpp)
|
||||||
|
} else {
|
||||||
|
**dfpp = **sfpp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., string
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
if v := *src.toString(); v != "" {
|
||||||
|
*dst.toString() = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
isProto3 := props.Prop[i].proto3
|
||||||
|
switch {
|
||||||
|
case isPointer:
|
||||||
|
panic("bad pointer in byte slice case in " + tf.Name())
|
||||||
|
case tf.Elem().Kind() != reflect.Uint8:
|
||||||
|
panic("bad element kind in byte slice case in " + tf.Name())
|
||||||
|
case isSlice: // E.g., [][]byte
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sbsp := src.toBytesSlice()
|
||||||
|
if *sbsp != nil {
|
||||||
|
dbsp := dst.toBytesSlice()
|
||||||
|
for _, sb := range *sbsp {
|
||||||
|
if sb == nil {
|
||||||
|
*dbsp = append(*dbsp, nil)
|
||||||
|
} else {
|
||||||
|
*dbsp = append(*dbsp, append([]byte{}, sb...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if *dbsp == nil {
|
||||||
|
*dbsp = [][]byte{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., []byte
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sbp := src.toBytes()
|
||||||
|
if *sbp != nil {
|
||||||
|
dbp := dst.toBytes()
|
||||||
|
if !isProto3 || len(*sbp) > 0 {
|
||||||
|
*dbp = append([]byte{}, *sbp...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
switch {
|
||||||
|
case !isPointer:
|
||||||
|
panic(fmt.Sprintf("message field %s without pointer", tf))
|
||||||
|
case isSlice: // E.g., []*pb.T
|
||||||
|
mi := getMergeInfo(tf)
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sps := src.getPointerSlice()
|
||||||
|
if sps != nil {
|
||||||
|
dps := dst.getPointerSlice()
|
||||||
|
for _, sp := range sps {
|
||||||
|
var dp pointer
|
||||||
|
if !sp.isNil() {
|
||||||
|
dp = valToPointer(reflect.New(tf))
|
||||||
|
mi.merge(dp, sp)
|
||||||
|
}
|
||||||
|
dps = append(dps, dp)
|
||||||
|
}
|
||||||
|
if dps == nil {
|
||||||
|
dps = []pointer{}
|
||||||
|
}
|
||||||
|
dst.setPointerSlice(dps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: // E.g., *pb.T
|
||||||
|
mi := getMergeInfo(tf)
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sp := src.getPointer()
|
||||||
|
if !sp.isNil() {
|
||||||
|
dp := dst.getPointer()
|
||||||
|
if dp.isNil() {
|
||||||
|
dp = valToPointer(reflect.New(tf))
|
||||||
|
dst.setPointer(dp)
|
||||||
|
}
|
||||||
|
mi.merge(dp, sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic("bad pointer or slice in map case in " + tf.Name())
|
||||||
|
default: // E.g., map[K]V
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
sm := src.asPointerTo(tf).Elem()
|
||||||
|
if sm.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dm := dst.asPointerTo(tf).Elem()
|
||||||
|
if dm.IsNil() {
|
||||||
|
dm.Set(reflect.MakeMap(tf))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf.Elem().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
val = reflect.ValueOf(Clone(val.Interface().(Message)))
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
default: // Basic type (e.g., string)
|
||||||
|
for _, key := range sm.MapKeys() {
|
||||||
|
val := sm.MapIndex(key)
|
||||||
|
dm.SetMapIndex(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
// Must be oneof field.
|
||||||
|
switch {
|
||||||
|
case isPointer || isSlice:
|
||||||
|
panic("bad pointer or slice in interface case in " + tf.Name())
|
||||||
|
default: // E.g., interface{}
|
||||||
|
// TODO: Make this faster?
|
||||||
|
mfi.merge = func(dst, src pointer) {
|
||||||
|
su := src.asPointerTo(tf).Elem()
|
||||||
|
if !su.IsNil() {
|
||||||
|
du := dst.asPointerTo(tf).Elem()
|
||||||
|
typ := su.Elem().Type()
|
||||||
|
if du.IsNil() || du.Elem().Type() != typ {
|
||||||
|
du.Set(reflect.New(typ.Elem())) // Initialize interface if empty
|
||||||
|
}
|
||||||
|
sv := su.Elem().Elem().Field(0)
|
||||||
|
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dv := du.Elem().Elem().Field(0)
|
||||||
|
if dv.Kind() == reflect.Ptr && dv.IsNil() {
|
||||||
|
dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty
|
||||||
|
}
|
||||||
|
switch sv.Type().Kind() {
|
||||||
|
case reflect.Ptr: // Proto struct (e.g., *T)
|
||||||
|
Merge(dv.Interface().(Message), sv.Interface().(Message))
|
||||||
|
case reflect.Slice: // E.g. Bytes type (e.g., []byte)
|
||||||
|
dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...)))
|
||||||
|
default: // Basic type (e.g., string)
|
||||||
|
dv.Set(sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("merger not found for type:%s", tf))
|
||||||
|
}
|
||||||
|
mi.fields = append(mi.fields, mfi)
|
||||||
|
}
|
||||||
|
|
||||||
|
mi.unrecognized = invalidField
|
||||||
|
if f, ok := t.FieldByName("XXX_unrecognized"); ok {
|
||||||
|
if f.Type != reflect.TypeOf([]byte{}) {
|
||||||
|
panic("expected XXX_unrecognized to be of type []byte")
|
||||||
|
}
|
||||||
|
mi.unrecognized = toField(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreInt32(&mi.initialized, 1)
|
||||||
|
}
|
2051
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
Normal file
2051
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
63
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
63
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
@ -50,7 +50,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
newline = []byte("\n")
|
newline = []byte("\n")
|
||||||
spaces = []byte(" ")
|
spaces = []byte(" ")
|
||||||
gtNewline = []byte(">\n")
|
|
||||||
endBraceNewline = []byte("}\n")
|
endBraceNewline = []byte("}\n")
|
||||||
backslashN = []byte{'\\', 'n'}
|
backslashN = []byte{'\\', 'n'}
|
||||||
backslashR = []byte{'\\', 'r'}
|
backslashR = []byte{'\\', 'r'}
|
||||||
@ -170,11 +169,6 @@ func writeName(w *textWriter, props *Properties) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// raw is the interface satisfied by RawMessage.
|
|
||||||
type raw interface {
|
|
||||||
Bytes() []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresQuotes(u string) bool {
|
func requiresQuotes(u string) bool {
|
||||||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
||||||
for _, ch := range u {
|
for _, ch := range u {
|
||||||
@ -269,6 +263,10 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
props := sprops.Prop[i]
|
props := sprops.Prop[i]
|
||||||
name := st.Field(i).Name
|
name := st.Field(i).Name
|
||||||
|
|
||||||
|
if name == "XXX_NoUnkeyedLiteral" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(name, "XXX_") {
|
if strings.HasPrefix(name, "XXX_") {
|
||||||
// There are two XXX_ fields:
|
// There are two XXX_ fields:
|
||||||
// XXX_unrecognized []byte
|
// XXX_unrecognized []byte
|
||||||
@ -355,7 +353,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
@ -372,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
@ -436,12 +434,6 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b, ok := fv.Interface().(raw); ok {
|
|
||||||
if err := writeRaw(w, b.Bytes()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums have a String method, so writeAny will work fine.
|
// Enums have a String method, so writeAny will work fine.
|
||||||
if err := tm.writeAny(w, fv, props); err != nil {
|
if err := tm.writeAny(w, fv, props); err != nil {
|
||||||
@ -455,7 +447,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
|
|
||||||
// Extensions (the XXX_extensions field).
|
// Extensions (the XXX_extensions field).
|
||||||
pv := sv.Addr()
|
pv := sv.Addr()
|
||||||
if _, ok := extendable(pv.Interface()); ok {
|
if _, err := extendable(pv.Interface()); err == nil {
|
||||||
if err := tm.writeExtensions(w, pv); err != nil {
|
if err := tm.writeExtensions(w, pv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -464,27 +456,6 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeRaw writes an uninterpreted raw message.
|
|
||||||
func writeRaw(w *textWriter, b []byte) error {
|
|
||||||
if err := w.WriteByte('<'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
if err := writeUnknownStruct(w, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte('>'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeAny writes an arbitrary field.
|
// writeAny writes an arbitrary field.
|
||||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||||
v = reflect.Indirect(v)
|
v = reflect.Indirect(v)
|
||||||
@ -535,6 +506,19 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.indent()
|
w.indent()
|
||||||
|
if v.CanAddr() {
|
||||||
|
// Calling v.Interface on a struct causes the reflect package to
|
||||||
|
// copy the entire struct. This is racy with the new Marshaler
|
||||||
|
// since we atomically update the XXX_sizecache.
|
||||||
|
//
|
||||||
|
// Thus, we retrieve a pointer to the struct if possible to avoid
|
||||||
|
// a race since v.Interface on the pointer doesn't copy the struct.
|
||||||
|
//
|
||||||
|
// If v is not addressable, then we are not worried about a race
|
||||||
|
// since it implies that the binary Marshaler cannot possibly be
|
||||||
|
// mutating this value.
|
||||||
|
v = v.Addr()
|
||||||
|
}
|
||||||
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
||||||
text, err := etm.MarshalText()
|
text, err := etm.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -543,9 +527,14 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
|
|||||||
if _, err = w.Write(text); err != nil {
|
if _, err = w.Write(text); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if err := tm.writeStruct(w, v); err != nil {
|
} else {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if err := tm.writeStruct(w, v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
w.unindent()
|
w.unindent()
|
||||||
if err := w.WriteByte(ket); err != nil {
|
if err := w.WriteByte(ket); err != nil {
|
||||||
return err
|
return err
|
||||||
|
81
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
81
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
@ -206,7 +206,6 @@ func (p *textParser) advance() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
errBadUTF8 = errors.New("proto: bad UTF-8")
|
errBadUTF8 = errors.New("proto: bad UTF-8")
|
||||||
errBadHex = errors.New("proto: bad hexadecimal")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func unquoteC(s string, quote rune) (string, error) {
|
func unquoteC(s string, quote rune) (string, error) {
|
||||||
@ -277,60 +276,47 @@ func unescape(s string) (ch string, tail string, err error) {
|
|||||||
return "?", s, nil // trigraph workaround
|
return "?", s, nil // trigraph workaround
|
||||||
case '\'', '"', '\\':
|
case '\'', '"', '\\':
|
||||||
return string(r), s, nil
|
return string(r), s, nil
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
if len(s) < 2 {
|
if len(s) < 2 {
|
||||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
||||||
}
|
}
|
||||||
base := 8
|
ss := string(r) + s[:2]
|
||||||
ss := s[:2]
|
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
if r == 'x' || r == 'X' {
|
i, err := strconv.ParseUint(ss, 8, 8)
|
||||||
base = 16
|
|
||||||
} else {
|
|
||||||
ss = string(r) + ss
|
|
||||||
}
|
|
||||||
i, err := strconv.ParseUint(ss, base, 8)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
|
||||||
}
|
}
|
||||||
return string([]byte{byte(i)}), s, nil
|
return string([]byte{byte(i)}), s, nil
|
||||||
case 'u', 'U':
|
case 'x', 'X', 'u', 'U':
|
||||||
n := 4
|
var n int
|
||||||
if r == 'U' {
|
switch r {
|
||||||
|
case 'x', 'X':
|
||||||
|
n = 2
|
||||||
|
case 'u':
|
||||||
|
n = 4
|
||||||
|
case 'U':
|
||||||
n = 8
|
n = 8
|
||||||
}
|
}
|
||||||
if len(s) < n {
|
if len(s) < n {
|
||||||
return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
|
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
|
||||||
}
|
|
||||||
|
|
||||||
bs := make([]byte, n/2)
|
|
||||||
for i := 0; i < n; i += 2 {
|
|
||||||
a, ok1 := unhex(s[i])
|
|
||||||
b, ok2 := unhex(s[i+1])
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
return "", "", errBadHex
|
|
||||||
}
|
|
||||||
bs[i/2] = a<<4 | b
|
|
||||||
}
|
}
|
||||||
|
ss := s[:n]
|
||||||
s = s[n:]
|
s = s[n:]
|
||||||
return string(bs), s, nil
|
i, err := strconv.ParseUint(ss, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
|
||||||
|
}
|
||||||
|
if r == 'x' || r == 'X' {
|
||||||
|
return string([]byte{byte(i)}), s, nil
|
||||||
|
}
|
||||||
|
if i > utf8.MaxRune {
|
||||||
|
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
||||||
|
}
|
||||||
|
return string(i), s, nil
|
||||||
}
|
}
|
||||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapted from src/pkg/strconv/quote.go.
|
|
||||||
func unhex(b byte) (v byte, ok bool) {
|
|
||||||
switch {
|
|
||||||
case '0' <= b && b <= '9':
|
|
||||||
return b - '0', true
|
|
||||||
case 'a' <= b && b <= 'f':
|
|
||||||
return b - 'a' + 10, true
|
|
||||||
case 'A' <= b && b <= 'F':
|
|
||||||
return b - 'A' + 10, true
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back off the parser by one token. Can only be done between calls to next().
|
// Back off the parser by one token. Can only be done between calls to next().
|
||||||
// It makes the next advance() a no-op.
|
// It makes the next advance() a no-op.
|
||||||
func (p *textParser) back() { p.backed = true }
|
func (p *textParser) back() { p.backed = true }
|
||||||
@ -644,17 +630,17 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
|||||||
if err := p.consumeToken(":"); err != nil {
|
if err := p.consumeToken(":"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
if err := p.readAny(key, props.MapKeyProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "value":
|
case "value":
|
||||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
if err := p.readAny(val, props.MapValProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
@ -728,6 +714,9 @@ func (p *textParser) consumeExtName() (string, error) {
|
|||||||
if tok.err != nil {
|
if tok.err != nil {
|
||||||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
||||||
}
|
}
|
||||||
|
if p.done && tok.value != "]" {
|
||||||
|
return "", p.errorf("unclosed type_url or extension name")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(parts, ""), nil
|
return strings.Join(parts, ""), nil
|
||||||
}
|
}
|
||||||
@ -883,13 +872,9 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
|||||||
// UnmarshalText returns *RequiredNotSetError.
|
// UnmarshalText returns *RequiredNotSetError.
|
||||||
func UnmarshalText(s string, pb Message) error {
|
func UnmarshalText(s string, pb Message) error {
|
||||||
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
||||||
err := um.UnmarshalText([]byte(s))
|
return um.UnmarshalText([]byte(s))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
pb.Reset()
|
pb.Reset()
|
||||||
v := reflect.ValueOf(pb)
|
v := reflect.ValueOf(pb)
|
||||||
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
|
return newTextParser(s).readStruct(v.Elem(), "")
|
||||||
return pe
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
36
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile
generated
vendored
36
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile
generated
vendored
@ -1,36 +0,0 @@
|
|||||||
# Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
#
|
|
||||||
# Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
# https://github.com/golang/protobuf
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
# Not stored here, but descriptor.proto is in https://github.com/google/protobuf/
|
|
||||||
# at src/google/protobuf/descriptor.proto
|
|
||||||
regenerate:
|
|
||||||
@echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION
|
|
||||||
protoc --go_out=../../../../.. -I$(HOME)/src/protobuf/include $(HOME)/src/protobuf/include/google/protobuf/descriptor.proto
|
|
1130
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
1130
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
872
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
Normal file
872
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
Normal file
@ -0,0 +1,872 @@
|
|||||||
|
// Protocol Buffers - Google's data interchange format
|
||||||
|
// Copyright 2008 Google Inc. All rights reserved.
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Author: kenton@google.com (Kenton Varda)
|
||||||
|
// Based on original Protocol Buffers design by
|
||||||
|
// Sanjay Ghemawat, Jeff Dean, and others.
|
||||||
|
//
|
||||||
|
// The messages in this file describe the definitions found in .proto files.
|
||||||
|
// A valid .proto file can be translated directly to a FileDescriptorProto
|
||||||
|
// without any other information (e.g. without reading its imports).
|
||||||
|
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package google.protobuf;
|
||||||
|
option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
|
||||||
|
option java_package = "com.google.protobuf";
|
||||||
|
option java_outer_classname = "DescriptorProtos";
|
||||||
|
option csharp_namespace = "Google.Protobuf.Reflection";
|
||||||
|
option objc_class_prefix = "GPB";
|
||||||
|
option cc_enable_arenas = true;
|
||||||
|
|
||||||
|
// descriptor.proto must be optimized for speed because reflection-based
|
||||||
|
// algorithms don't work during bootstrapping.
|
||||||
|
option optimize_for = SPEED;
|
||||||
|
|
||||||
|
// The protocol compiler can output a FileDescriptorSet containing the .proto
|
||||||
|
// files it parses.
|
||||||
|
message FileDescriptorSet {
|
||||||
|
repeated FileDescriptorProto file = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a complete .proto file.
|
||||||
|
message FileDescriptorProto {
|
||||||
|
optional string name = 1; // file name, relative to root of source tree
|
||||||
|
optional string package = 2; // e.g. "foo", "foo.bar", etc.
|
||||||
|
|
||||||
|
// Names of files imported by this file.
|
||||||
|
repeated string dependency = 3;
|
||||||
|
// Indexes of the public imported files in the dependency list above.
|
||||||
|
repeated int32 public_dependency = 10;
|
||||||
|
// Indexes of the weak imported files in the dependency list.
|
||||||
|
// For Google-internal migration only. Do not use.
|
||||||
|
repeated int32 weak_dependency = 11;
|
||||||
|
|
||||||
|
// All top-level definitions in this file.
|
||||||
|
repeated DescriptorProto message_type = 4;
|
||||||
|
repeated EnumDescriptorProto enum_type = 5;
|
||||||
|
repeated ServiceDescriptorProto service = 6;
|
||||||
|
repeated FieldDescriptorProto extension = 7;
|
||||||
|
|
||||||
|
optional FileOptions options = 8;
|
||||||
|
|
||||||
|
// This field contains optional information about the original source code.
|
||||||
|
// You may safely remove this entire field without harming runtime
|
||||||
|
// functionality of the descriptors -- the information is needed only by
|
||||||
|
// development tools.
|
||||||
|
optional SourceCodeInfo source_code_info = 9;
|
||||||
|
|
||||||
|
// The syntax of the proto file.
|
||||||
|
// The supported values are "proto2" and "proto3".
|
||||||
|
optional string syntax = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a message type.
|
||||||
|
message DescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
|
||||||
|
repeated FieldDescriptorProto field = 2;
|
||||||
|
repeated FieldDescriptorProto extension = 6;
|
||||||
|
|
||||||
|
repeated DescriptorProto nested_type = 3;
|
||||||
|
repeated EnumDescriptorProto enum_type = 4;
|
||||||
|
|
||||||
|
message ExtensionRange {
|
||||||
|
optional int32 start = 1;
|
||||||
|
optional int32 end = 2;
|
||||||
|
|
||||||
|
optional ExtensionRangeOptions options = 3;
|
||||||
|
}
|
||||||
|
repeated ExtensionRange extension_range = 5;
|
||||||
|
|
||||||
|
repeated OneofDescriptorProto oneof_decl = 8;
|
||||||
|
|
||||||
|
optional MessageOptions options = 7;
|
||||||
|
|
||||||
|
// Range of reserved tag numbers. Reserved tag numbers may not be used by
|
||||||
|
// fields or extension ranges in the same message. Reserved ranges may
|
||||||
|
// not overlap.
|
||||||
|
message ReservedRange {
|
||||||
|
optional int32 start = 1; // Inclusive.
|
||||||
|
optional int32 end = 2; // Exclusive.
|
||||||
|
}
|
||||||
|
repeated ReservedRange reserved_range = 9;
|
||||||
|
// Reserved field names, which may not be used by fields in the same message.
|
||||||
|
// A given name may only be reserved once.
|
||||||
|
repeated string reserved_name = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtensionRangeOptions {
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a field within a message.
|
||||||
|
message FieldDescriptorProto {
|
||||||
|
enum Type {
|
||||||
|
// 0 is reserved for errors.
|
||||||
|
// Order is weird for historical reasons.
|
||||||
|
TYPE_DOUBLE = 1;
|
||||||
|
TYPE_FLOAT = 2;
|
||||||
|
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
|
||||||
|
// negative values are likely.
|
||||||
|
TYPE_INT64 = 3;
|
||||||
|
TYPE_UINT64 = 4;
|
||||||
|
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
|
||||||
|
// negative values are likely.
|
||||||
|
TYPE_INT32 = 5;
|
||||||
|
TYPE_FIXED64 = 6;
|
||||||
|
TYPE_FIXED32 = 7;
|
||||||
|
TYPE_BOOL = 8;
|
||||||
|
TYPE_STRING = 9;
|
||||||
|
// Tag-delimited aggregate.
|
||||||
|
// Group type is deprecated and not supported in proto3. However, Proto3
|
||||||
|
// implementations should still be able to parse the group wire format and
|
||||||
|
// treat group fields as unknown fields.
|
||||||
|
TYPE_GROUP = 10;
|
||||||
|
TYPE_MESSAGE = 11; // Length-delimited aggregate.
|
||||||
|
|
||||||
|
// New in version 2.
|
||||||
|
TYPE_BYTES = 12;
|
||||||
|
TYPE_UINT32 = 13;
|
||||||
|
TYPE_ENUM = 14;
|
||||||
|
TYPE_SFIXED32 = 15;
|
||||||
|
TYPE_SFIXED64 = 16;
|
||||||
|
TYPE_SINT32 = 17; // Uses ZigZag encoding.
|
||||||
|
TYPE_SINT64 = 18; // Uses ZigZag encoding.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Label {
|
||||||
|
// 0 is reserved for errors
|
||||||
|
LABEL_OPTIONAL = 1;
|
||||||
|
LABEL_REQUIRED = 2;
|
||||||
|
LABEL_REPEATED = 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
optional string name = 1;
|
||||||
|
optional int32 number = 3;
|
||||||
|
optional Label label = 4;
|
||||||
|
|
||||||
|
// If type_name is set, this need not be set. If both this and type_name
|
||||||
|
// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
|
||||||
|
optional Type type = 5;
|
||||||
|
|
||||||
|
// For message and enum types, this is the name of the type. If the name
|
||||||
|
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
|
||||||
|
// rules are used to find the type (i.e. first the nested types within this
|
||||||
|
// message are searched, then within the parent, on up to the root
|
||||||
|
// namespace).
|
||||||
|
optional string type_name = 6;
|
||||||
|
|
||||||
|
// For extensions, this is the name of the type being extended. It is
|
||||||
|
// resolved in the same manner as type_name.
|
||||||
|
optional string extendee = 2;
|
||||||
|
|
||||||
|
// For numeric types, contains the original text representation of the value.
|
||||||
|
// For booleans, "true" or "false".
|
||||||
|
// For strings, contains the default text contents (not escaped in any way).
|
||||||
|
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
|
||||||
|
// TODO(kenton): Base-64 encode?
|
||||||
|
optional string default_value = 7;
|
||||||
|
|
||||||
|
// If set, gives the index of a oneof in the containing type's oneof_decl
|
||||||
|
// list. This field is a member of that oneof.
|
||||||
|
optional int32 oneof_index = 9;
|
||||||
|
|
||||||
|
// JSON name of this field. The value is set by protocol compiler. If the
|
||||||
|
// user has set a "json_name" option on this field, that option's value
|
||||||
|
// will be used. Otherwise, it's deduced from the field's name by converting
|
||||||
|
// it to camelCase.
|
||||||
|
optional string json_name = 10;
|
||||||
|
|
||||||
|
optional FieldOptions options = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a oneof.
|
||||||
|
message OneofDescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
optional OneofOptions options = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes an enum type.
|
||||||
|
message EnumDescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
|
||||||
|
repeated EnumValueDescriptorProto value = 2;
|
||||||
|
|
||||||
|
optional EnumOptions options = 3;
|
||||||
|
|
||||||
|
// Range of reserved numeric values. Reserved values may not be used by
|
||||||
|
// entries in the same enum. Reserved ranges may not overlap.
|
||||||
|
//
|
||||||
|
// Note that this is distinct from DescriptorProto.ReservedRange in that it
|
||||||
|
// is inclusive such that it can appropriately represent the entire int32
|
||||||
|
// domain.
|
||||||
|
message EnumReservedRange {
|
||||||
|
optional int32 start = 1; // Inclusive.
|
||||||
|
optional int32 end = 2; // Inclusive.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range of reserved numeric values. Reserved numeric values may not be used
|
||||||
|
// by enum values in the same enum declaration. Reserved ranges may not
|
||||||
|
// overlap.
|
||||||
|
repeated EnumReservedRange reserved_range = 4;
|
||||||
|
|
||||||
|
// Reserved enum value names, which may not be reused. A given name may only
|
||||||
|
// be reserved once.
|
||||||
|
repeated string reserved_name = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a value within an enum.
|
||||||
|
message EnumValueDescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
optional int32 number = 2;
|
||||||
|
|
||||||
|
optional EnumValueOptions options = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a service.
|
||||||
|
message ServiceDescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
repeated MethodDescriptorProto method = 2;
|
||||||
|
|
||||||
|
optional ServiceOptions options = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes a method of a service.
|
||||||
|
message MethodDescriptorProto {
|
||||||
|
optional string name = 1;
|
||||||
|
|
||||||
|
// Input and output type names. These are resolved in the same way as
|
||||||
|
// FieldDescriptorProto.type_name, but must refer to a message type.
|
||||||
|
optional string input_type = 2;
|
||||||
|
optional string output_type = 3;
|
||||||
|
|
||||||
|
optional MethodOptions options = 4;
|
||||||
|
|
||||||
|
// Identifies if client streams multiple client messages
|
||||||
|
optional bool client_streaming = 5 [default=false];
|
||||||
|
// Identifies if server streams multiple server messages
|
||||||
|
optional bool server_streaming = 6 [default=false];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Options
|
||||||
|
|
||||||
|
// Each of the definitions above may have "options" attached. These are
|
||||||
|
// just annotations which may cause code to be generated slightly differently
|
||||||
|
// or may contain hints for code that manipulates protocol messages.
|
||||||
|
//
|
||||||
|
// Clients may define custom options as extensions of the *Options messages.
|
||||||
|
// These extensions may not yet be known at parsing time, so the parser cannot
|
||||||
|
// store the values in them. Instead it stores them in a field in the *Options
|
||||||
|
// message called uninterpreted_option. This field must have the same name
|
||||||
|
// across all *Options messages. We then use this field to populate the
|
||||||
|
// extensions when we build a descriptor, at which point all protos have been
|
||||||
|
// parsed and so all extensions are known.
|
||||||
|
//
|
||||||
|
// Extension numbers for custom options may be chosen as follows:
|
||||||
|
// * For options which will only be used within a single application or
|
||||||
|
// organization, or for experimental options, use field numbers 50000
|
||||||
|
// through 99999. It is up to you to ensure that you do not use the
|
||||||
|
// same number for multiple options.
|
||||||
|
// * For options which will be published and used publicly by multiple
|
||||||
|
// independent entities, e-mail protobuf-global-extension-registry@google.com
|
||||||
|
// to reserve extension numbers. Simply provide your project name (e.g.
|
||||||
|
// Objective-C plugin) and your project website (if available) -- there's no
|
||||||
|
// need to explain how you intend to use them. Usually you only need one
|
||||||
|
// extension number. You can declare multiple options with only one extension
|
||||||
|
// number by putting them in a sub-message. See the Custom Options section of
|
||||||
|
// the docs for examples:
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/proto#options
|
||||||
|
// If this turns out to be popular, a web service will be set up
|
||||||
|
// to automatically assign option numbers.
|
||||||
|
|
||||||
|
|
||||||
|
message FileOptions {
|
||||||
|
|
||||||
|
// Sets the Java package where classes generated from this .proto will be
|
||||||
|
// placed. By default, the proto package is used, but this is often
|
||||||
|
// inappropriate because proto packages do not normally start with backwards
|
||||||
|
// domain names.
|
||||||
|
optional string java_package = 1;
|
||||||
|
|
||||||
|
|
||||||
|
// If set, all the classes from the .proto file are wrapped in a single
|
||||||
|
// outer class with the given name. This applies to both Proto1
|
||||||
|
// (equivalent to the old "--one_java_file" option) and Proto2 (where
|
||||||
|
// a .proto always translates to a single class, but you may want to
|
||||||
|
// explicitly choose the class name).
|
||||||
|
optional string java_outer_classname = 8;
|
||||||
|
|
||||||
|
// If set true, then the Java code generator will generate a separate .java
|
||||||
|
// file for each top-level message, enum, and service defined in the .proto
|
||||||
|
// file. Thus, these types will *not* be nested inside the outer class
|
||||||
|
// named by java_outer_classname. However, the outer class will still be
|
||||||
|
// generated to contain the file's getDescriptor() method as well as any
|
||||||
|
// top-level extensions defined in the file.
|
||||||
|
optional bool java_multiple_files = 10 [default=false];
|
||||||
|
|
||||||
|
// This option does nothing.
|
||||||
|
optional bool java_generate_equals_and_hash = 20 [deprecated=true];
|
||||||
|
|
||||||
|
// If set true, then the Java2 code generator will generate code that
|
||||||
|
// throws an exception whenever an attempt is made to assign a non-UTF-8
|
||||||
|
// byte sequence to a string field.
|
||||||
|
// Message reflection will do the same.
|
||||||
|
// However, an extension field still accepts non-UTF-8 byte sequences.
|
||||||
|
// This option has no effect on when used with the lite runtime.
|
||||||
|
optional bool java_string_check_utf8 = 27 [default=false];
|
||||||
|
|
||||||
|
|
||||||
|
// Generated classes can be optimized for speed or code size.
|
||||||
|
enum OptimizeMode {
|
||||||
|
SPEED = 1; // Generate complete code for parsing, serialization,
|
||||||
|
// etc.
|
||||||
|
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
|
||||||
|
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
|
||||||
|
}
|
||||||
|
optional OptimizeMode optimize_for = 9 [default=SPEED];
|
||||||
|
|
||||||
|
// Sets the Go package where structs generated from this .proto will be
|
||||||
|
// placed. If omitted, the Go package will be derived from the following:
|
||||||
|
// - The basename of the package import path, if provided.
|
||||||
|
// - Otherwise, the package statement in the .proto file, if present.
|
||||||
|
// - Otherwise, the basename of the .proto file, without extension.
|
||||||
|
optional string go_package = 11;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Should generic services be generated in each language? "Generic" services
|
||||||
|
// are not specific to any particular RPC system. They are generated by the
|
||||||
|
// main code generators in each language (without additional plugins).
|
||||||
|
// Generic services were the only kind of service generation supported by
|
||||||
|
// early versions of google.protobuf.
|
||||||
|
//
|
||||||
|
// Generic services are now considered deprecated in favor of using plugins
|
||||||
|
// that generate code specific to your particular RPC system. Therefore,
|
||||||
|
// these default to false. Old code which depends on generic services should
|
||||||
|
// explicitly set them to true.
|
||||||
|
optional bool cc_generic_services = 16 [default=false];
|
||||||
|
optional bool java_generic_services = 17 [default=false];
|
||||||
|
optional bool py_generic_services = 18 [default=false];
|
||||||
|
optional bool php_generic_services = 42 [default=false];
|
||||||
|
|
||||||
|
// Is this file deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for everything in the file, or it will be completely ignored; in the very
|
||||||
|
// least, this is a formalization for deprecating files.
|
||||||
|
optional bool deprecated = 23 [default=false];
|
||||||
|
|
||||||
|
// Enables the use of arenas for the proto messages in this file. This applies
|
||||||
|
// only to generated classes for C++.
|
||||||
|
optional bool cc_enable_arenas = 31 [default=false];
|
||||||
|
|
||||||
|
|
||||||
|
// Sets the objective c class prefix which is prepended to all objective c
|
||||||
|
// generated classes from this .proto. There is no default.
|
||||||
|
optional string objc_class_prefix = 36;
|
||||||
|
|
||||||
|
// Namespace for generated classes; defaults to the package.
|
||||||
|
optional string csharp_namespace = 37;
|
||||||
|
|
||||||
|
// By default Swift generators will take the proto package and CamelCase it
|
||||||
|
// replacing '.' with underscore and use that to prefix the types/symbols
|
||||||
|
// defined. When this options is provided, they will use this value instead
|
||||||
|
// to prefix the types/symbols defined.
|
||||||
|
optional string swift_prefix = 39;
|
||||||
|
|
||||||
|
// Sets the php class prefix which is prepended to all php generated classes
|
||||||
|
// from this .proto. Default is empty.
|
||||||
|
optional string php_class_prefix = 40;
|
||||||
|
|
||||||
|
// Use this option to change the namespace of php generated classes. Default
|
||||||
|
// is empty. When this option is empty, the package name will be used for
|
||||||
|
// determining the namespace.
|
||||||
|
optional string php_namespace = 41;
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here.
|
||||||
|
// See the documentation for the "Options" section above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message.
|
||||||
|
// See the documentation for the "Options" section above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
|
||||||
|
reserved 38;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageOptions {
|
||||||
|
// Set true to use the old proto1 MessageSet wire format for extensions.
|
||||||
|
// This is provided for backwards-compatibility with the MessageSet wire
|
||||||
|
// format. You should not use this for any other reason: It's less
|
||||||
|
// efficient, has fewer features, and is more complicated.
|
||||||
|
//
|
||||||
|
// The message must be defined exactly as follows:
|
||||||
|
// message Foo {
|
||||||
|
// option message_set_wire_format = true;
|
||||||
|
// extensions 4 to max;
|
||||||
|
// }
|
||||||
|
// Note that the message cannot have any defined fields; MessageSets only
|
||||||
|
// have extensions.
|
||||||
|
//
|
||||||
|
// All extensions of your type must be singular messages; e.g. they cannot
|
||||||
|
// be int32s, enums, or repeated messages.
|
||||||
|
//
|
||||||
|
// Because this is an option, the above two restrictions are not enforced by
|
||||||
|
// the protocol compiler.
|
||||||
|
optional bool message_set_wire_format = 1 [default=false];
|
||||||
|
|
||||||
|
// Disables the generation of the standard "descriptor()" accessor, which can
|
||||||
|
// conflict with a field of the same name. This is meant to make migration
|
||||||
|
// from proto1 easier; new code should avoid fields named "descriptor".
|
||||||
|
optional bool no_standard_descriptor_accessor = 2 [default=false];
|
||||||
|
|
||||||
|
// Is this message deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for the message, or it will be completely ignored; in the very least,
|
||||||
|
// this is a formalization for deprecating messages.
|
||||||
|
optional bool deprecated = 3 [default=false];
|
||||||
|
|
||||||
|
// Whether the message is an automatically generated map entry type for the
|
||||||
|
// maps field.
|
||||||
|
//
|
||||||
|
// For maps fields:
|
||||||
|
// map<KeyType, ValueType> map_field = 1;
|
||||||
|
// The parsed descriptor looks like:
|
||||||
|
// message MapFieldEntry {
|
||||||
|
// option map_entry = true;
|
||||||
|
// optional KeyType key = 1;
|
||||||
|
// optional ValueType value = 2;
|
||||||
|
// }
|
||||||
|
// repeated MapFieldEntry map_field = 1;
|
||||||
|
//
|
||||||
|
// Implementations may choose not to generate the map_entry=true message, but
|
||||||
|
// use a native map in the target language to hold the keys and values.
|
||||||
|
// The reflection APIs in such implementions still need to work as
|
||||||
|
// if the field is a repeated message field.
|
||||||
|
//
|
||||||
|
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
||||||
|
// instead. The option should only be implicitly set by the proto compiler
|
||||||
|
// parser.
|
||||||
|
optional bool map_entry = 7;
|
||||||
|
|
||||||
|
reserved 8; // javalite_serializable
|
||||||
|
reserved 9; // javanano_as_lite
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FieldOptions {
|
||||||
|
// The ctype option instructs the C++ code generator to use a different
|
||||||
|
// representation of the field than it normally would. See the specific
|
||||||
|
// options below. This option is not yet implemented in the open source
|
||||||
|
// release -- sorry, we'll try to include it in a future version!
|
||||||
|
optional CType ctype = 1 [default = STRING];
|
||||||
|
enum CType {
|
||||||
|
// Default mode.
|
||||||
|
STRING = 0;
|
||||||
|
|
||||||
|
CORD = 1;
|
||||||
|
|
||||||
|
STRING_PIECE = 2;
|
||||||
|
}
|
||||||
|
// The packed option can be enabled for repeated primitive fields to enable
|
||||||
|
// a more efficient representation on the wire. Rather than repeatedly
|
||||||
|
// writing the tag and type for each element, the entire array is encoded as
|
||||||
|
// a single length-delimited blob. In proto3, only explicit setting it to
|
||||||
|
// false will avoid using packed encoding.
|
||||||
|
optional bool packed = 2;
|
||||||
|
|
||||||
|
// The jstype option determines the JavaScript type used for values of the
|
||||||
|
// field. The option is permitted only for 64 bit integral and fixed types
|
||||||
|
// (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
|
||||||
|
// is represented as JavaScript string, which avoids loss of precision that
|
||||||
|
// can happen when a large value is converted to a floating point JavaScript.
|
||||||
|
// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
|
||||||
|
// use the JavaScript "number" type. The behavior of the default option
|
||||||
|
// JS_NORMAL is implementation dependent.
|
||||||
|
//
|
||||||
|
// This option is an enum to permit additional types to be added, e.g.
|
||||||
|
// goog.math.Integer.
|
||||||
|
optional JSType jstype = 6 [default = JS_NORMAL];
|
||||||
|
enum JSType {
|
||||||
|
// Use the default type.
|
||||||
|
JS_NORMAL = 0;
|
||||||
|
|
||||||
|
// Use JavaScript strings.
|
||||||
|
JS_STRING = 1;
|
||||||
|
|
||||||
|
// Use JavaScript numbers.
|
||||||
|
JS_NUMBER = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should this field be parsed lazily? Lazy applies only to message-type
|
||||||
|
// fields. It means that when the outer message is initially parsed, the
|
||||||
|
// inner message's contents will not be parsed but instead stored in encoded
|
||||||
|
// form. The inner message will actually be parsed when it is first accessed.
|
||||||
|
//
|
||||||
|
// This is only a hint. Implementations are free to choose whether to use
|
||||||
|
// eager or lazy parsing regardless of the value of this option. However,
|
||||||
|
// setting this option true suggests that the protocol author believes that
|
||||||
|
// using lazy parsing on this field is worth the additional bookkeeping
|
||||||
|
// overhead typically needed to implement it.
|
||||||
|
//
|
||||||
|
// This option does not affect the public interface of any generated code;
|
||||||
|
// all method signatures remain the same. Furthermore, thread-safety of the
|
||||||
|
// interface is not affected by this option; const methods remain safe to
|
||||||
|
// call from multiple threads concurrently, while non-const methods continue
|
||||||
|
// to require exclusive access.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Note that implementations may choose not to check required fields within
|
||||||
|
// a lazy sub-message. That is, calling IsInitialized() on the outer message
|
||||||
|
// may return true even if the inner message has missing required fields.
|
||||||
|
// This is necessary because otherwise the inner message would have to be
|
||||||
|
// parsed in order to perform the check, defeating the purpose of lazy
|
||||||
|
// parsing. An implementation which chooses not to check required fields
|
||||||
|
// must be consistent about it. That is, for any particular sub-message, the
|
||||||
|
// implementation must either *always* check its required fields, or *never*
|
||||||
|
// check its required fields, regardless of whether or not the message has
|
||||||
|
// been parsed.
|
||||||
|
optional bool lazy = 5 [default=false];
|
||||||
|
|
||||||
|
// Is this field deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for accessors, or it will be completely ignored; in the very least, this
|
||||||
|
// is a formalization for deprecating fields.
|
||||||
|
optional bool deprecated = 3 [default=false];
|
||||||
|
|
||||||
|
// For Google-internal migration only. Do not use.
|
||||||
|
optional bool weak = 10 [default=false];
|
||||||
|
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
|
||||||
|
reserved 4; // removed jtype
|
||||||
|
}
|
||||||
|
|
||||||
|
message OneofOptions {
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EnumOptions {
|
||||||
|
|
||||||
|
// Set this option to true to allow mapping different tag names to the same
|
||||||
|
// value.
|
||||||
|
optional bool allow_alias = 2;
|
||||||
|
|
||||||
|
// Is this enum deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for the enum, or it will be completely ignored; in the very least, this
|
||||||
|
// is a formalization for deprecating enums.
|
||||||
|
optional bool deprecated = 3 [default=false];
|
||||||
|
|
||||||
|
reserved 5; // javanano_as_lite
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EnumValueOptions {
|
||||||
|
// Is this enum value deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for the enum value, or it will be completely ignored; in the very least,
|
||||||
|
// this is a formalization for deprecating enum values.
|
||||||
|
optional bool deprecated = 1 [default=false];
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServiceOptions {
|
||||||
|
|
||||||
|
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
|
||||||
|
// framework. We apologize for hoarding these numbers to ourselves, but
|
||||||
|
// we were already using them long before we decided to release Protocol
|
||||||
|
// Buffers.
|
||||||
|
|
||||||
|
// Is this service deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for the service, or it will be completely ignored; in the very least,
|
||||||
|
// this is a formalization for deprecating services.
|
||||||
|
optional bool deprecated = 33 [default=false];
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MethodOptions {
|
||||||
|
|
||||||
|
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
|
||||||
|
// framework. We apologize for hoarding these numbers to ourselves, but
|
||||||
|
// we were already using them long before we decided to release Protocol
|
||||||
|
// Buffers.
|
||||||
|
|
||||||
|
// Is this method deprecated?
|
||||||
|
// Depending on the target platform, this can emit Deprecated annotations
|
||||||
|
// for the method, or it will be completely ignored; in the very least,
|
||||||
|
// this is a formalization for deprecating methods.
|
||||||
|
optional bool deprecated = 33 [default=false];
|
||||||
|
|
||||||
|
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
|
||||||
|
// or neither? HTTP based RPC implementation may choose GET verb for safe
|
||||||
|
// methods, and PUT verb for idempotent methods instead of the default POST.
|
||||||
|
enum IdempotencyLevel {
|
||||||
|
IDEMPOTENCY_UNKNOWN = 0;
|
||||||
|
NO_SIDE_EFFECTS = 1; // implies idempotent
|
||||||
|
IDEMPOTENT = 2; // idempotent, but may have side effects
|
||||||
|
}
|
||||||
|
optional IdempotencyLevel idempotency_level =
|
||||||
|
34 [default=IDEMPOTENCY_UNKNOWN];
|
||||||
|
|
||||||
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
// Clients can define custom options in extensions of this message. See above.
|
||||||
|
extensions 1000 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A message representing a option the parser does not recognize. This only
|
||||||
|
// appears in options protos created by the compiler::Parser class.
|
||||||
|
// DescriptorPool resolves these when building Descriptor objects. Therefore,
|
||||||
|
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
|
||||||
|
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
|
||||||
|
// in them.
|
||||||
|
message UninterpretedOption {
|
||||||
|
// The name of the uninterpreted option. Each string represents a segment in
|
||||||
|
// a dot-separated name. is_extension is true iff a segment represents an
|
||||||
|
// extension (denoted with parentheses in options specs in .proto files).
|
||||||
|
// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
|
||||||
|
// "foo.(bar.baz).qux".
|
||||||
|
message NamePart {
|
||||||
|
required string name_part = 1;
|
||||||
|
required bool is_extension = 2;
|
||||||
|
}
|
||||||
|
repeated NamePart name = 2;
|
||||||
|
|
||||||
|
// The value of the uninterpreted option, in whatever type the tokenizer
|
||||||
|
// identified it as during parsing. Exactly one of these should be set.
|
||||||
|
optional string identifier_value = 3;
|
||||||
|
optional uint64 positive_int_value = 4;
|
||||||
|
optional int64 negative_int_value = 5;
|
||||||
|
optional double double_value = 6;
|
||||||
|
optional bytes string_value = 7;
|
||||||
|
optional string aggregate_value = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Optional source code info
|
||||||
|
|
||||||
|
// Encapsulates information about the original source file from which a
|
||||||
|
// FileDescriptorProto was generated.
|
||||||
|
message SourceCodeInfo {
|
||||||
|
// A Location identifies a piece of source code in a .proto file which
|
||||||
|
// corresponds to a particular definition. This information is intended
|
||||||
|
// to be useful to IDEs, code indexers, documentation generators, and similar
|
||||||
|
// tools.
|
||||||
|
//
|
||||||
|
// For example, say we have a file like:
|
||||||
|
// message Foo {
|
||||||
|
// optional string foo = 1;
|
||||||
|
// }
|
||||||
|
// Let's look at just the field definition:
|
||||||
|
// optional string foo = 1;
|
||||||
|
// ^ ^^ ^^ ^ ^^^
|
||||||
|
// a bc de f ghi
|
||||||
|
// We have the following locations:
|
||||||
|
// span path represents
|
||||||
|
// [a,i) [ 4, 0, 2, 0 ] The whole field definition.
|
||||||
|
// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
|
||||||
|
// [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
|
||||||
|
// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
|
||||||
|
// [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// - A location may refer to a repeated field itself (i.e. not to any
|
||||||
|
// particular index within it). This is used whenever a set of elements are
|
||||||
|
// logically enclosed in a single code segment. For example, an entire
|
||||||
|
// extend block (possibly containing multiple extension definitions) will
|
||||||
|
// have an outer location whose path refers to the "extensions" repeated
|
||||||
|
// field without an index.
|
||||||
|
// - Multiple locations may have the same path. This happens when a single
|
||||||
|
// logical declaration is spread out across multiple places. The most
|
||||||
|
// obvious example is the "extend" block again -- there may be multiple
|
||||||
|
// extend blocks in the same scope, each of which will have the same path.
|
||||||
|
// - A location's span is not always a subset of its parent's span. For
|
||||||
|
// example, the "extendee" of an extension declaration appears at the
|
||||||
|
// beginning of the "extend" block and is shared by all extensions within
|
||||||
|
// the block.
|
||||||
|
// - Just because a location's span is a subset of some other location's span
|
||||||
|
// does not mean that it is a descendent. For example, a "group" defines
|
||||||
|
// both a type and a field in a single declaration. Thus, the locations
|
||||||
|
// corresponding to the type and field and their components will overlap.
|
||||||
|
// - Code which tries to interpret locations should probably be designed to
|
||||||
|
// ignore those that it doesn't understand, as more types of locations could
|
||||||
|
// be recorded in the future.
|
||||||
|
repeated Location location = 1;
|
||||||
|
message Location {
|
||||||
|
// Identifies which part of the FileDescriptorProto was defined at this
|
||||||
|
// location.
|
||||||
|
//
|
||||||
|
// Each element is a field number or an index. They form a path from
|
||||||
|
// the root FileDescriptorProto to the place where the definition. For
|
||||||
|
// example, this path:
|
||||||
|
// [ 4, 3, 2, 7, 1 ]
|
||||||
|
// refers to:
|
||||||
|
// file.message_type(3) // 4, 3
|
||||||
|
// .field(7) // 2, 7
|
||||||
|
// .name() // 1
|
||||||
|
// This is because FileDescriptorProto.message_type has field number 4:
|
||||||
|
// repeated DescriptorProto message_type = 4;
|
||||||
|
// and DescriptorProto.field has field number 2:
|
||||||
|
// repeated FieldDescriptorProto field = 2;
|
||||||
|
// and FieldDescriptorProto.name has field number 1:
|
||||||
|
// optional string name = 1;
|
||||||
|
//
|
||||||
|
// Thus, the above path gives the location of a field name. If we removed
|
||||||
|
// the last element:
|
||||||
|
// [ 4, 3, 2, 7 ]
|
||||||
|
// this path refers to the whole field declaration (from the beginning
|
||||||
|
// of the label to the terminating semicolon).
|
||||||
|
repeated int32 path = 1 [packed=true];
|
||||||
|
|
||||||
|
// Always has exactly three or four elements: start line, start column,
|
||||||
|
// end line (optional, otherwise assumed same as start line), end column.
|
||||||
|
// These are packed into a single field for efficiency. Note that line
|
||||||
|
// and column numbers are zero-based -- typically you will want to add
|
||||||
|
// 1 to each before displaying to a user.
|
||||||
|
repeated int32 span = 2 [packed=true];
|
||||||
|
|
||||||
|
// If this SourceCodeInfo represents a complete declaration, these are any
|
||||||
|
// comments appearing before and after the declaration which appear to be
|
||||||
|
// attached to the declaration.
|
||||||
|
//
|
||||||
|
// A series of line comments appearing on consecutive lines, with no other
|
||||||
|
// tokens appearing on those lines, will be treated as a single comment.
|
||||||
|
//
|
||||||
|
// leading_detached_comments will keep paragraphs of comments that appear
|
||||||
|
// before (but not connected to) the current element. Each paragraph,
|
||||||
|
// separated by empty lines, will be one comment element in the repeated
|
||||||
|
// field.
|
||||||
|
//
|
||||||
|
// Only the comment content is provided; comment markers (e.g. //) are
|
||||||
|
// stripped out. For block comments, leading whitespace and an asterisk
|
||||||
|
// will be stripped from the beginning of each line other than the first.
|
||||||
|
// Newlines are included in the output.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// optional int32 foo = 1; // Comment attached to foo.
|
||||||
|
// // Comment attached to bar.
|
||||||
|
// optional int32 bar = 2;
|
||||||
|
//
|
||||||
|
// optional string baz = 3;
|
||||||
|
// // Comment attached to baz.
|
||||||
|
// // Another line attached to baz.
|
||||||
|
//
|
||||||
|
// // Comment attached to qux.
|
||||||
|
// //
|
||||||
|
// // Another line attached to qux.
|
||||||
|
// optional double qux = 4;
|
||||||
|
//
|
||||||
|
// // Detached comment for corge. This is not leading or trailing comments
|
||||||
|
// // to qux or corge because there are blank lines separating it from
|
||||||
|
// // both.
|
||||||
|
//
|
||||||
|
// // Detached comment for corge paragraph 2.
|
||||||
|
//
|
||||||
|
// optional string corge = 5;
|
||||||
|
// /* Block comment attached
|
||||||
|
// * to corge. Leading asterisks
|
||||||
|
// * will be removed. */
|
||||||
|
// /* Block comment attached to
|
||||||
|
// * grault. */
|
||||||
|
// optional int32 grault = 6;
|
||||||
|
//
|
||||||
|
// // ignored detached comments.
|
||||||
|
optional string leading_comments = 3;
|
||||||
|
optional string trailing_comments = 4;
|
||||||
|
repeated string leading_detached_comments = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes the relationship between generated code and its original source
|
||||||
|
// file. A GeneratedCodeInfo message is associated with only one generated
|
||||||
|
// source file, but may contain references to different source .proto files.
|
||||||
|
message GeneratedCodeInfo {
|
||||||
|
// An Annotation connects some span of text in generated code to an element
|
||||||
|
// of its generating .proto file.
|
||||||
|
repeated Annotation annotation = 1;
|
||||||
|
message Annotation {
|
||||||
|
// Identifies the element in the original source .proto file. This field
|
||||||
|
// is formatted the same as SourceCodeInfo.Location.path.
|
||||||
|
repeated int32 path = 1 [packed=true];
|
||||||
|
|
||||||
|
// Identifies the filesystem path to the original source .proto.
|
||||||
|
optional string source_file = 2;
|
||||||
|
|
||||||
|
// Identifies the starting offset in bytes in the generated code
|
||||||
|
// that relates to the identified object.
|
||||||
|
optional int32 begin = 3;
|
||||||
|
|
||||||
|
// Identifies the ending offset in bytes in the generated code that
|
||||||
|
// relates to the identified offset. The end offset should be one past
|
||||||
|
// the last relevant byte (so the length of the text = end - begin).
|
||||||
|
optional int32 end = 4;
|
||||||
|
}
|
||||||
|
}
|
0
vendor/github.com/lrstanley/girc/go.sum
generated
vendored
Normal file
0
vendor/github.com/lrstanley/girc/go.sum
generated
vendored
Normal file
1
vendor/github.com/shurcooL/sanitized_anchor_name/go.mod
generated
vendored
Normal file
1
vendor/github.com/shurcooL/sanitized_anchor_name/go.mod
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module github.com/shurcooL/sanitized_anchor_name
|
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
Normal file
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.sw*
|
||||||
|
*.png
|
||||||
|
*.directory
|
||||||
|
qrcode/qrcode
|
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.7
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v ./...
|
||||||
|
|
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
Normal file
19
vendor/github.com/skip2/go-qrcode/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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.
|
80
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
Normal file
80
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# go-qrcode #
|
||||||
|
|
||||||
|
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'>
|
||||||
|
|
||||||
|
Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode)
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](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:
|
||||||
|
-o string
|
||||||
|
out PNG file prefix, empty for stdout
|
||||||
|
-s int
|
||||||
|
image size (pixel) (default 256)
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
Normal file
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// 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
|
||||||
|
}
|
455
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
Normal file
455
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
// 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.
|
||||||
|
d.classifyDataModes()
|
||||||
|
|
||||||
|
// Optimise segments.
|
||||||
|
err := d.optimiseDataModes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, "#!#!"].
|
||||||
|
func (d *dataEncoder) classifyDataModes() {
|
||||||
|
var start int
|
||||||
|
mode := dataModeNone
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
589
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
Normal file
589
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
Normal file
@ -0,0 +1,589 @@
|
|||||||
|
// 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"
|
||||||
|
"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
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))
|
||||||
|
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
log.Fatalf("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")
|
||||||
|
}
|
||||||
|
|
||||||
|
q := &QRCode{
|
||||||
|
Content: content,
|
||||||
|
|
||||||
|
Level: level,
|
||||||
|
VersionNumber: chosenVersion.version,
|
||||||
|
|
||||||
|
ForegroundColor: color.Black,
|
||||||
|
BackgroundColor: color.White,
|
||||||
|
|
||||||
|
encoder: encoder,
|
||||||
|
data: encoded,
|
||||||
|
version: *chosenVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size of each module drawn.
|
||||||
|
pixelsPerModule := size / realSize
|
||||||
|
|
||||||
|
// Center the symbol within the image.
|
||||||
|
offset := (size - realSize*pixelsPerModule) / 2
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
bitmap := q.symbol.bitmap()
|
||||||
|
for y, row := range bitmap {
|
||||||
|
for x, v := range row {
|
||||||
|
if v {
|
||||||
|
startX := x*pixelsPerModule + offset
|
||||||
|
startY := y*pixelsPerModule + offset
|
||||||
|
for i := startX; i < startX+pixelsPerModule; i++ {
|
||||||
|
for j := startY; j < startY+pixelsPerModule; j++ {
|
||||||
|
pos := img.PixOffset(i, j)
|
||||||
|
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 int) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
Normal file
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
// 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
Normal file
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// 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
Normal file
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// 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
|
||||||
|
}
|
309
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
Normal file
309
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
// 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) (*symbol, error) {
|
||||||
|
m := ®ularSymbol{
|
||||||
|
version: version,
|
||||||
|
mask: mask,
|
||||||
|
data: data,
|
||||||
|
|
||||||
|
symbol: newSymbol(version.symbolSize(), version.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
Normal file
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
// 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
Normal file
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
93
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
93
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2014 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 hkdf implements the HMAC-based Extract-and-Expand Key Derivation
|
||||||
|
// Function (HKDF) as defined in RFC 5869.
|
||||||
|
//
|
||||||
|
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
||||||
|
// expanding limited input keying material into one or more cryptographically
|
||||||
|
// strong secret keys.
|
||||||
|
package hkdf // import "golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extract generates a pseudorandom key for use with Expand from an input secret
|
||||||
|
// and an optional independent salt.
|
||||||
|
//
|
||||||
|
// Only use this function if you need to reuse the extracted key with multiple
|
||||||
|
// Expand invocations and different context values. Most common scenarios,
|
||||||
|
// including the generation of multiple keys, should use New instead.
|
||||||
|
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||||
|
if salt == nil {
|
||||||
|
salt = make([]byte, hash().Size())
|
||||||
|
}
|
||||||
|
extractor := hmac.New(hash, salt)
|
||||||
|
extractor.Write(secret)
|
||||||
|
return extractor.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hkdf struct {
|
||||||
|
expander hash.Hash
|
||||||
|
size int
|
||||||
|
|
||||||
|
info []byte
|
||||||
|
counter byte
|
||||||
|
|
||||||
|
prev []byte
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *hkdf) Read(p []byte) (int, error) {
|
||||||
|
// Check whether enough data can be generated
|
||||||
|
need := len(p)
|
||||||
|
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||||
|
if remains < need {
|
||||||
|
return 0, errors.New("hkdf: entropy limit reached")
|
||||||
|
}
|
||||||
|
// Read any leftover from the buffer
|
||||||
|
n := copy(p, f.buf)
|
||||||
|
p = p[n:]
|
||||||
|
|
||||||
|
// Fill the rest of the buffer
|
||||||
|
for len(p) > 0 {
|
||||||
|
f.expander.Reset()
|
||||||
|
f.expander.Write(f.prev)
|
||||||
|
f.expander.Write(f.info)
|
||||||
|
f.expander.Write([]byte{f.counter})
|
||||||
|
f.prev = f.expander.Sum(f.prev[:0])
|
||||||
|
f.counter++
|
||||||
|
|
||||||
|
// Copy the new batch into p
|
||||||
|
f.buf = f.prev
|
||||||
|
n = copy(p, f.buf)
|
||||||
|
p = p[n:]
|
||||||
|
}
|
||||||
|
// Save leftovers for next run
|
||||||
|
f.buf = f.buf[n:]
|
||||||
|
|
||||||
|
return need, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand returns a Reader, from which keys can be read, using the given
|
||||||
|
// pseudorandom key and optional context info, skipping the extraction step.
|
||||||
|
//
|
||||||
|
// The pseudorandomKey should have been generated by Extract, or be a uniformly
|
||||||
|
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
|
||||||
|
// 3.3. Most common scenarios will want to use New instead.
|
||||||
|
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||||
|
expander := hmac.New(hash, pseudorandomKey)
|
||||||
|
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Reader, from which keys can be read, using the given hash,
|
||||||
|
// secret, salt and context info. Salt and info can be nil.
|
||||||
|
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader {
|
||||||
|
prk := Extract(hash, secret, salt)
|
||||||
|
return Expand(hash, prk, info)
|
||||||
|
}
|
3
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
3
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
@ -241,7 +241,10 @@ func (ws *Conn) Close() error {
|
|||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsClientConn reports whether ws is a client-side connection.
|
||||||
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
||||||
|
|
||||||
|
// IsServerConn reports whether ws is a server-side connection.
|
||||||
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||||
|
|
||||||
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||||
|
661
vendor/maunium.net/go/mautrix-whatsapp/LICENSE
generated
vendored
Normal file
661
vendor/maunium.net/go/mautrix-whatsapp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,661 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
147
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/chat.go
generated
vendored
Normal file
147
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/chat.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatUpdateCommand string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChatUpdateCommandAction ChatUpdateCommand = "action"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatUpdate struct {
|
||||||
|
JID string `json:"id"`
|
||||||
|
Command ChatUpdateCommand `json:"cmd"`
|
||||||
|
Data ChatUpdateData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatActionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChatActionNameChange ChatActionType = "subject"
|
||||||
|
ChatActionAddTopic ChatActionType = "desc_add"
|
||||||
|
ChatActionRemoveTopic ChatActionType = "desc_remove"
|
||||||
|
ChatActionRestrict ChatActionType = "restrict"
|
||||||
|
ChatActionAnnounce ChatActionType = "announce"
|
||||||
|
ChatActionPromote ChatActionType = "promote"
|
||||||
|
ChatActionDemote ChatActionType = "demote"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatUpdateData struct {
|
||||||
|
Action ChatActionType
|
||||||
|
SenderJID string
|
||||||
|
|
||||||
|
NameChange struct {
|
||||||
|
Name string `json:"subject"`
|
||||||
|
SetAt int64 `json:"s_t"`
|
||||||
|
SetBy string `json:"s_o"`
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTopic struct {
|
||||||
|
Topic string `json:"desc"`
|
||||||
|
ID string `json:"descId"`
|
||||||
|
SetAt int64 `json:"descTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveTopic struct {
|
||||||
|
ID string `json:"descId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Restrict bool
|
||||||
|
|
||||||
|
Announce bool
|
||||||
|
|
||||||
|
PermissionChange struct {
|
||||||
|
JIDs []string `json:"participants"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
|
||||||
|
var arr []json.RawMessage
|
||||||
|
err := json.Unmarshal(data, &arr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(arr) < 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(arr[0], &cud.Action)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(arr[1], &cud.SenderJID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cud.SenderJID = strings.Replace(cud.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
|
||||||
|
var unmarshalTo interface{}
|
||||||
|
switch cud.Action {
|
||||||
|
case ChatActionNameChange:
|
||||||
|
unmarshalTo = &cud.NameChange
|
||||||
|
case ChatActionAddTopic:
|
||||||
|
unmarshalTo = &cud.AddTopic
|
||||||
|
case ChatActionRemoveTopic:
|
||||||
|
unmarshalTo = &cud.RemoveTopic
|
||||||
|
case ChatActionRestrict:
|
||||||
|
unmarshalTo = &cud.Restrict
|
||||||
|
case ChatActionAnnounce:
|
||||||
|
unmarshalTo = &cud.Announce
|
||||||
|
case ChatActionPromote, ChatActionDemote:
|
||||||
|
unmarshalTo = &cud.PermissionChange
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(arr[2], unmarshalTo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cud.NameChange.SetBy = strings.Replace(cud.NameChange.SetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
for index, jid := range cud.PermissionChange.JIDs {
|
||||||
|
cud.PermissionChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatUpdateHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleChatUpdate(ChatUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageChatUpdate(message []byte) {
|
||||||
|
var event ChatUpdate
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
chatUpdateHandler, ok := handler.(ChatUpdateHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go chatUpdateHandler.HandleChatUpdate(event)
|
||||||
|
}
|
||||||
|
}
|
59
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/cmd.go
generated
vendored
Normal file
59
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/cmd.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CommandPicture CommandType = "picture"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Type CommandType `json:"type"`
|
||||||
|
JID string `json:"jid"`
|
||||||
|
|
||||||
|
*ProfilePicInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleCommand(Command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageCommand(message []byte) {
|
||||||
|
var event Command
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
commandHandler, ok := handler.(CommandHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go commandHandler.HandleCommand(event)
|
||||||
|
}
|
||||||
|
}
|
60
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/conn.go
generated
vendored
Normal file
60
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/conn.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnInfo struct {
|
||||||
|
ProtocolVersion []int `json:"protoVersion"`
|
||||||
|
BinaryVersion int `json:"binVersion"`
|
||||||
|
Phone struct {
|
||||||
|
WhatsAppVersion string `json:"wa_version"`
|
||||||
|
MCC string `json:"mcc"`
|
||||||
|
MNC string `json:"mnc"`
|
||||||
|
OSVersion string `json:"os_version"`
|
||||||
|
DeviceManufacturer string `json:"device_manufacturer"`
|
||||||
|
DeviceModel string `json:"device_model"`
|
||||||
|
OSBuildNumber string `json:"os_build_number"`
|
||||||
|
} `json:"phone"`
|
||||||
|
Features map[string]interface{} `json:"features"`
|
||||||
|
PushName string `json:"pushname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnInfoHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleConnInfo(ConnInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageConn(message []byte) {
|
||||||
|
var event ConnInfo
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
connInfoHandler, ok := handler.(ConnInfoHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
connInfoHandler.HandleConnInfo(event)
|
||||||
|
}
|
||||||
|
}
|
102
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/jsonmessage.go
generated
vendored
Normal file
102
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/jsonmessage.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONMessage []json.RawMessage
|
||||||
|
|
||||||
|
type JSONMessageType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MessageMsgInfo JSONMessageType = "MsgInfo"
|
||||||
|
MessageMsg JSONMessageType = "Msg"
|
||||||
|
MessagePresence JSONMessageType = "Presence"
|
||||||
|
MessageStream JSONMessageType = "Stream"
|
||||||
|
MessageConn JSONMessageType = "Conn"
|
||||||
|
MessageProps JSONMessageType = "Props"
|
||||||
|
MessageCmd JSONMessageType = "Cmd"
|
||||||
|
MessageChat JSONMessageType = "Chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) AddHandler(handler whatsapp.Handler) {
|
||||||
|
ext.Conn.AddHandler(handler)
|
||||||
|
ext.handlers = append(ext.handlers, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) HandleError(error) {}
|
||||||
|
|
||||||
|
type UnhandledJSONMessageHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleUnhandledJSONMessage(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONParseErrorHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleJSONParseError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) jsonParseError(err error) {
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
errorHandler, ok := handler.(JSONParseErrorHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errorHandler.HandleJSONParseError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) HandleJsonMessage(message string) {
|
||||||
|
msg := JSONMessage{}
|
||||||
|
err := json.Unmarshal([]byte(message), &msg)
|
||||||
|
if err != nil || len(msg) < 2 {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgType JSONMessageType
|
||||||
|
json.Unmarshal(msg[0], &msgType)
|
||||||
|
|
||||||
|
switch msgType {
|
||||||
|
case MessagePresence:
|
||||||
|
ext.handleMessagePresence(msg[1])
|
||||||
|
case MessageStream:
|
||||||
|
ext.handleMessageStream(msg[1:])
|
||||||
|
case MessageConn:
|
||||||
|
ext.handleMessageConn(msg[1])
|
||||||
|
case MessageProps:
|
||||||
|
ext.handleMessageProps(msg[1])
|
||||||
|
case MessageMsgInfo, MessageMsg:
|
||||||
|
ext.handleMessageMsgInfo(msgType, msg[1])
|
||||||
|
case MessageCmd:
|
||||||
|
ext.handleMessageCommand(msg[1])
|
||||||
|
case MessageChat:
|
||||||
|
ext.handleMessageChatUpdate(msg[1])
|
||||||
|
default:
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ujmHandler.HandleUnhandledJSONMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/msginfo.go
generated
vendored
Normal file
90
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/msginfo.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgInfoCommand string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgInfoCommandAck MsgInfoCommand = "ack"
|
||||||
|
MsgInfoCommandAcks MsgInfoCommand = "acks"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Acknowledgement int
|
||||||
|
|
||||||
|
const (
|
||||||
|
AckMessageSent Acknowledgement = 1
|
||||||
|
AckMessageDelivered Acknowledgement = 2
|
||||||
|
AckMessageRead Acknowledgement = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONStringOrArray []string
|
||||||
|
|
||||||
|
func (jsoa *JSONStringOrArray) UnmarshalJSON(data []byte) error {
|
||||||
|
var str string
|
||||||
|
if json.Unmarshal(data, &str) == nil {
|
||||||
|
*jsoa = []string{str}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var strs []string
|
||||||
|
json.Unmarshal(data, &strs)
|
||||||
|
*jsoa = strs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MsgInfo struct {
|
||||||
|
Command MsgInfoCommand `json:"cmd"`
|
||||||
|
IDs JSONStringOrArray `json:"id"`
|
||||||
|
Acknowledgement Acknowledgement `json:"ack"`
|
||||||
|
MessageFromJID string `json:"from"`
|
||||||
|
SenderJID string `json:"participant"`
|
||||||
|
ToJID string `json:"to"`
|
||||||
|
Timestamp int64 `json:"t"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MsgInfoHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleMsgInfo(MsgInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message []byte) {
|
||||||
|
var event MsgInfo
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
if msgType == MessageMsg {
|
||||||
|
event.SenderJID = event.ToJID
|
||||||
|
}
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
msgInfoHandler, ok := handler.(MsgInfoHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go msgInfoHandler.HandleMsgInfo(event)
|
||||||
|
}
|
||||||
|
}
|
67
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/presence.go
generated
vendored
Normal file
67
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/presence.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PresenceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PresenceUnavailable PresenceType = "unavailable"
|
||||||
|
PresenceAvailable PresenceType = "available"
|
||||||
|
PresenceComposing PresenceType = "composing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Presence struct {
|
||||||
|
JID string `json:"id"`
|
||||||
|
SenderJID string `json:"participant"`
|
||||||
|
Status PresenceType `json:"type"`
|
||||||
|
Timestamp int64 `json:"t"`
|
||||||
|
Deny bool `json:"deny"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PresenceHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandlePresence(Presence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessagePresence(message []byte) {
|
||||||
|
var event Presence
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
if len(event.SenderJID) == 0 {
|
||||||
|
event.SenderJID = event.JID
|
||||||
|
} else {
|
||||||
|
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
}
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
presenceHandler, ok := handler.(PresenceHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go presenceHandler.HandlePresence(event)
|
||||||
|
}
|
||||||
|
}
|
68
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/props.go
generated
vendored
Normal file
68
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/props.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProtocolProps struct {
|
||||||
|
WebPresence bool `json:"webPresence"`
|
||||||
|
NotificationQuery bool `json:"notificationQuery"`
|
||||||
|
FacebookCrashLog bool `json:"fbCrashlog"`
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
GIFSearch string `json:"gifSearch"`
|
||||||
|
Spam bool `json:"SPAM"`
|
||||||
|
SetBlock bool `json:"SET_BLOCK"`
|
||||||
|
MessageInfo bool `json:"MESSAGE_INFO"`
|
||||||
|
MaxFileSize int `json:"maxFileSize"`
|
||||||
|
Media int `json:"media"`
|
||||||
|
GroupNameLength int `json:"maxSubject"`
|
||||||
|
GroupDescriptionLength int `json:"groupDescLength"`
|
||||||
|
MaxParticipants int `json:"maxParticipants"`
|
||||||
|
VideoMaxEdge int `json:"videoMaxEdge"`
|
||||||
|
ImageMaxEdge int `json:"imageMaxEdge"`
|
||||||
|
ImageMaxKilobytes int `json:"imageMaxKBytes"`
|
||||||
|
Edit int `json:"edit"`
|
||||||
|
FwdUIStartTimestamp int `json:"fwdUiStartTs"`
|
||||||
|
GroupsV3 int `json:"groupsV3"`
|
||||||
|
RestrictGroups int `json:"restrictGroups"`
|
||||||
|
AnnounceGroups int `json:"announceGroups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProtocolPropsHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleProtocolProps(ProtocolProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageProps(message []byte) {
|
||||||
|
var event ProtocolProps
|
||||||
|
err := json.Unmarshal(message, &event)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
protocolPropsHandler, ok := handler.(ProtocolPropsHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go protocolPropsHandler.HandleProtocolProps(event)
|
||||||
|
}
|
||||||
|
}
|
63
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/stream.go
generated
vendored
Normal file
63
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/stream.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamUpdate = "update"
|
||||||
|
StreamSleep = "asleep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamEvent struct {
|
||||||
|
Type StreamType
|
||||||
|
Boolean bool
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamEventHandler interface {
|
||||||
|
whatsapp.Handler
|
||||||
|
HandleStreamEvent(StreamEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
|
||||||
|
var event StreamEvent
|
||||||
|
err := json.Unmarshal(message[0], &event.Type)
|
||||||
|
if err != nil {
|
||||||
|
ext.jsonParseError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Type == StreamUpdate && len(message) > 4 {
|
||||||
|
json.Unmarshal(message[1], event.Boolean)
|
||||||
|
json.Unmarshal(message[2], event.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, handler := range ext.handlers {
|
||||||
|
streamHandler, ok := handler.(StreamEventHandler)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go streamHandler.HandleStreamEvent(event)
|
||||||
|
}
|
||||||
|
}
|
132
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/whatsapp.go
generated
vendored
Normal file
132
vendor/maunium.net/go/mautrix-whatsapp/whatsapp-ext/whatsapp.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||||
|
// Copyright (C) 2019 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package whatsappExt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OldUserSuffix = "@c.us"
|
||||||
|
NewUserSuffix = "@s.whatsapp.net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtendedConn struct {
|
||||||
|
*whatsapp.Conn
|
||||||
|
|
||||||
|
handlers []whatsapp.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtendConn(conn *whatsapp.Conn) *ExtendedConn {
|
||||||
|
ext := &ExtendedConn{
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
ext.Conn.AddHandler(ext)
|
||||||
|
return ext
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupInfo struct {
|
||||||
|
JID string `json:"jid"`
|
||||||
|
OwnerJID string `json:"owner"`
|
||||||
|
|
||||||
|
Name string `json:"subject"`
|
||||||
|
NameSetTime int64 `json:"subjectTime"`
|
||||||
|
NameSetBy string `json:"subjectOwner"`
|
||||||
|
|
||||||
|
Topic string `json:"desc"`
|
||||||
|
TopicID string `json:"descId"`
|
||||||
|
TopicSetAt int64 `json:"descTime"`
|
||||||
|
TopicSetBy string `json:"descOwner"`
|
||||||
|
|
||||||
|
GroupCreated int64 `json:"creation"`
|
||||||
|
|
||||||
|
Status int16 `json:"status"`
|
||||||
|
|
||||||
|
Participants []struct {
|
||||||
|
JID string `json:"id"`
|
||||||
|
IsAdmin bool `json:"isAdmin"`
|
||||||
|
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||||
|
} `json:"participants"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) GetGroupMetaData(jid string) (*GroupInfo, error) {
|
||||||
|
data, err := ext.Conn.GetGroupMetaData(jid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get group metadata: %v", err)
|
||||||
|
}
|
||||||
|
content := <-data
|
||||||
|
|
||||||
|
info := &GroupInfo{}
|
||||||
|
err = json.Unmarshal([]byte(content), info)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("failed to unmarshal group metadata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, participant := range info.Participants {
|
||||||
|
info.Participants[index].JID = strings.Replace(participant.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
}
|
||||||
|
info.NameSetBy = strings.Replace(info.NameSetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
info.TopicSetBy = strings.Replace(info.TopicSetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProfilePicInfo struct {
|
||||||
|
URL string `json:"eurl"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
|
||||||
|
Status int16 `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ppi *ProfilePicInfo) Download() (io.ReadCloser, error) {
|
||||||
|
resp, err := http.Get(ppi.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ppi *ProfilePicInfo) DownloadBytes() ([]byte, error) {
|
||||||
|
body, err := ppi.Download()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
data, err := ioutil.ReadAll(body)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ext *ExtendedConn) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
|
||||||
|
data, err := ext.Conn.GetProfilePicThumb(jid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
||||||
|
}
|
||||||
|
content := <-data
|
||||||
|
info := &ProfilePicInfo{}
|
||||||
|
err = json.Unmarshal([]byte(content), info)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("failed to unmarshal avatar info: %v", err)
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
25
vendor/modules.txt
vendored
25
vendor/modules.txt
vendored
@ -1,5 +1,7 @@
|
|||||||
# github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
# github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
||||||
github.com/42wim/go-gitter
|
github.com/42wim/go-gitter
|
||||||
|
# github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||||
|
github.com/Baozisoftware/qrcode-terminal-go
|
||||||
# github.com/Jeffail/gabs v1.1.1
|
# github.com/Jeffail/gabs v1.1.1
|
||||||
github.com/Jeffail/gabs
|
github.com/Jeffail/gabs
|
||||||
# github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
# github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
||||||
@ -13,6 +15,14 @@ github.com/Philipp15b/go-steam/protocol/gamecoordinator
|
|||||||
github.com/Philipp15b/go-steam/protocol/protobuf
|
github.com/Philipp15b/go-steam/protocol/protobuf
|
||||||
github.com/Philipp15b/go-steam/rwu
|
github.com/Philipp15b/go-steam/rwu
|
||||||
github.com/Philipp15b/go-steam/socialcache
|
github.com/Philipp15b/go-steam/socialcache
|
||||||
|
# github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884
|
||||||
|
github.com/Rhymen/go-whatsapp
|
||||||
|
github.com/Rhymen/go-whatsapp/binary
|
||||||
|
github.com/Rhymen/go-whatsapp/binary/proto
|
||||||
|
github.com/Rhymen/go-whatsapp/crypto/cbc
|
||||||
|
github.com/Rhymen/go-whatsapp/crypto/curve25519
|
||||||
|
github.com/Rhymen/go-whatsapp/crypto/hkdf
|
||||||
|
github.com/Rhymen/go-whatsapp/binary/token
|
||||||
# github.com/bwmarrin/discordgo v0.19.0
|
# github.com/bwmarrin/discordgo v0.19.0
|
||||||
github.com/bwmarrin/discordgo
|
github.com/bwmarrin/discordgo
|
||||||
# github.com/davecgh/go-spew v1.1.1
|
# github.com/davecgh/go-spew v1.1.1
|
||||||
@ -25,7 +35,7 @@ github.com/dgrijalva/jwt-go
|
|||||||
github.com/fsnotify/fsnotify
|
github.com/fsnotify/fsnotify
|
||||||
# github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
# github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api
|
github.com/go-telegram-bot-api/telegram-bot-api
|
||||||
# github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc
|
# github.com/golang/protobuf v1.2.0
|
||||||
github.com/golang/protobuf/proto
|
github.com/golang/protobuf/proto
|
||||||
github.com/golang/protobuf/protoc-gen-go/descriptor
|
github.com/golang/protobuf/protoc-gen-go/descriptor
|
||||||
# github.com/google/gops v0.3.5
|
# github.com/google/gops v0.3.5
|
||||||
@ -133,10 +143,14 @@ github.com/shazow/rateio
|
|||||||
# github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
|
# github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
|
||||||
github.com/shazow/ssh-chat/sshd
|
github.com/shazow/ssh-chat/sshd
|
||||||
github.com/shazow/ssh-chat/internal/sanitize
|
github.com/shazow/ssh-chat/internal/sanitize
|
||||||
# github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95
|
# github.com/shurcooL/sanitized_anchor_name v1.0.0
|
||||||
github.com/shurcooL/sanitized_anchor_name
|
github.com/shurcooL/sanitized_anchor_name
|
||||||
# github.com/sirupsen/logrus v1.3.0
|
# github.com/sirupsen/logrus v1.3.0
|
||||||
github.com/sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
|
# github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9
|
||||||
|
github.com/skip2/go-qrcode
|
||||||
|
github.com/skip2/go-qrcode/bitset
|
||||||
|
github.com/skip2/go-qrcode/reedsolomon
|
||||||
# github.com/spf13/afero v1.1.2
|
# github.com/spf13/afero v1.1.2
|
||||||
github.com/spf13/afero
|
github.com/spf13/afero
|
||||||
github.com/spf13/afero/mem
|
github.com/spf13/afero/mem
|
||||||
@ -179,7 +193,7 @@ go.uber.org/zap/internal/bufferpool
|
|||||||
go.uber.org/zap/buffer
|
go.uber.org/zap/buffer
|
||||||
go.uber.org/zap/internal/color
|
go.uber.org/zap/internal/color
|
||||||
go.uber.org/zap/internal/exit
|
go.uber.org/zap/internal/exit
|
||||||
# golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664
|
# golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
|
||||||
golang.org/x/crypto/ssh/terminal
|
golang.org/x/crypto/ssh/terminal
|
||||||
golang.org/x/crypto/acme/autocert
|
golang.org/x/crypto/acme/autocert
|
||||||
golang.org/x/crypto/nacl/secretbox
|
golang.org/x/crypto/nacl/secretbox
|
||||||
@ -193,8 +207,9 @@ golang.org/x/crypto/blowfish
|
|||||||
golang.org/x/crypto/curve25519
|
golang.org/x/crypto/curve25519
|
||||||
golang.org/x/crypto/ed25519
|
golang.org/x/crypto/ed25519
|
||||||
golang.org/x/crypto/internal/chacha20
|
golang.org/x/crypto/internal/chacha20
|
||||||
|
golang.org/x/crypto/hkdf
|
||||||
golang.org/x/crypto/ed25519/internal/edwards25519
|
golang.org/x/crypto/ed25519/internal/edwards25519
|
||||||
# golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37
|
# golang.org/x/net v0.0.0-20190110200230-915654e7eabc
|
||||||
golang.org/x/net/websocket
|
golang.org/x/net/websocket
|
||||||
# golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc
|
# golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
@ -214,3 +229,5 @@ golang.org/x/text/encoding/internal
|
|||||||
gopkg.in/natefinch/lumberjack.v2
|
gopkg.in/natefinch/lumberjack.v2
|
||||||
# gopkg.in/yaml.v2 v2.2.2
|
# gopkg.in/yaml.v2 v2.2.2
|
||||||
gopkg.in/yaml.v2
|
gopkg.in/yaml.v2
|
||||||
|
# maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3
|
||||||
|
maunium.net/go/mautrix-whatsapp/whatsapp-ext
|
||||||
|
Loading…
Reference in New Issue
Block a user