Add initial WhatsApp support (#711)

This commit is contained in:
Krzysiek Madejski 2019-02-21 20:28:13 +01:00 committed by Wim
parent 46f4bbb3b5
commit 55e79063d6
83 changed files with 24231 additions and 3500 deletions

View File

@ -18,6 +18,7 @@
[Rocket.Chat][mb-rocketchat] |
[XMPP][mb-xmpp] |
[Twitch][mb-twitch] |
[WhatsApp][mb-whatsapp] |
[Zulip][mb-zulip] |
And more...
</sup>
@ -79,6 +80,7 @@
* [Steam](https://store.steampowered.com/)
* [Twitch](https://twitch.tv)
* [Ssh-chat](https://github.com/shazow/ssh-chat)
* [WhatsApp](https://www.whatsapp.com/)
* [Zulip](https://zulipchat.com)
### 3rd party via matterbridge api
@ -276,6 +278,7 @@ Matterbridge wouldn't exist without these libraries:
* steam - https://github.com/Philipp15b/go-steam
* telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
* xmpp - https://github.com/mattn/go-xmpp
* whatsapp - https://github.com/Rhymen/go-whatsapp/
* zulip - https://github.com/ifo/gozulipbot
<!-- Links -->
@ -289,4 +292,5 @@ Matterbridge wouldn't exist without these libraries:
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
[mb-xmpp]: https://inverse.chat/
[mb-twitch]: https://www.twitch.tv/matterbridge
[mb-whatsapp]: https://www.whatsapp.com/
[mb-zulip]: https://matterbridge.zulipchat.com/register/

View File

@ -185,6 +185,7 @@ type BridgeValues struct {
Telegram map[string]Protocol
Rocketchat 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
General Protocol
Gateway []Gateway

104
bridge/whatsapp/handlers.go Normal file
View 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

View 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
View 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 ""
//}

View File

@ -13,6 +13,7 @@ import (
"github.com/42wim/matterbridge/bridge/sshchat"
"github.com/42wim/matterbridge/bridge/steam"
"github.com/42wim/matterbridge/bridge/telegram"
"github.com/42wim/matterbridge/bridge/whatsapp"
"github.com/42wim/matterbridge/bridge/xmpp"
"github.com/42wim/matterbridge/bridge/zulip"
)
@ -30,6 +31,7 @@ var FullMap = map[string]bridge.Factory{
"sshchat": bsshchat.New,
"steam": bsteam.New,
"telegram": btelegram.New,
"whatsapp": bwhatsapp.New,
"xmpp": bxmpp.New,
"zulip": bzulip.New,
}

8
go.mod
View File

@ -2,14 +2,15 @@ module github.com/42wim/matterbridge
require (
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/Jeffail/gabs v1.1.1 // indirect
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/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
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/golang/protobuf v0.0.0-20170613224224-e325f446bebc // indirect
github.com/google/gops v0.3.5
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // 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/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
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/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // 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/multierr v1.1.0 // 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/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3
)

32
go.sum
View File

@ -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/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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/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/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/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
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/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/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/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/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/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/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/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/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/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
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-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
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/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
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/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 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/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
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/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 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/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/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
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/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/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/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/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-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/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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=

View File

@ -1172,10 +1172,45 @@ StripNick=false
#OPTIONAL (default 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]
#You can configure multiple servers "[zulip.name]" or "[zulip.name2]"
#In this example we use [zulip.streamchat]
#REQUIRED
@ -1380,6 +1415,7 @@ enable=true
# account specified above
# REQUIRED
account="irc.freenode"
# channel to connect on that account
# How to specify them for the different bridges:
#
@ -1400,6 +1436,10 @@ enable=true
# - encrypted rooms are not supported in matrix
# steam - chatid (a large number).
# 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 #)
#
# REQUIRED

View 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

View 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.

View 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)

View 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
View File

@ -0,0 +1,2 @@
.idea/
docs/

21
vendor/github.com/Rhymen/go-whatsapp/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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
View 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
View 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
View 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
}

View 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 DiffieHellman (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 DiffieHellman (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[:]
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"],
}
}
}

View File

@ -1,7 +1,4 @@
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

View File

@ -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

View File

@ -35,22 +35,39 @@
package proto
import (
"fmt"
"log"
"reflect"
"strings"
)
// Clone returns a deep copy of a protocol buffer.
func Clone(pb Message) Message {
in := reflect.ValueOf(pb)
func Clone(src Message) Message {
in := reflect.ValueOf(src)
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())
// out is empty so a merge is a deep copy.
mergeStruct(out.Elem(), in.Elem())
return out.Interface().(Message)
// Merger is the interface representing objects that can merge messages of the same type.
type Merger interface {
// Merge merges src into this 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.
@ -58,17 +75,24 @@ func Clone(pb Message) Message {
// Elements of repeated fields will be appended.
// Merge panics if src and dst are not the same type, or if dst is nil.
func Merge(dst, src Message) {
if m, ok := dst.(Merger); ok {
m.Merge(src)
return
}
in := reflect.ValueOf(src)
out := reflect.ValueOf(dst)
if out.IsNil() {
panic("proto: nil destination")
}
if in.Type() != out.Type() {
// Explicit test prior to mergeStruct so that mistyped nils will fail
panic("proto: type mismatch")
panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src))
}
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
}
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])
}
if emIn, ok := extendable(in.Addr().Interface()); ok {
if emIn, err := extendable(in.Addr().Interface()); err == nil {
emOut, _ := extendable(out.Addr().Interface())
mIn, muIn := emIn.extensionsRead()
if mIn != nil {

View File

@ -39,8 +39,6 @@ import (
"errors"
"fmt"
"io"
"os"
"reflect"
)
// 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.
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.
// It returns the integer and the number of bytes consumed, or
// zero if there is not enough.
@ -267,9 +261,6 @@ func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
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.
// This is the format used for the bytes protocol buffer
// type and for embedded messages.
@ -311,81 +302,29 @@ func (p *Buffer) DecodeStringBytes() (s string, err error) {
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
// unmarshal themselves. The method should reset the receiver before
// decoding starts. The argument points to data that may be
// unmarshal themselves. The argument points to data that may be
// overwritten, so implementations should not keep references to the
// 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 {
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
// decoded result in pb. If the struct underlying pb does not match
// the data in buf, the results can be unpredictable.
@ -395,7 +334,13 @@ type Unmarshaler interface {
// to preserve and append to existing data.
func Unmarshal(buf []byte, pb Message) error {
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
@ -405,8 +350,16 @@ func Unmarshal(buf []byte, pb Message) error {
// UnmarshalMerge merges into existing data in pb.
// Most code should use Unmarshal instead.
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 {
// 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 NewBuffer(buf).Unmarshal(pb)
@ -422,12 +375,17 @@ func (p *Buffer) DecodeMessage(pb Message) error {
}
// 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 {
typ, base, err := getbase(pb)
if err != nil {
return err
b := p.buf[p.index:]
x, y := findEndGroup(b)
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
@ -438,533 +396,33 @@ func (p *Buffer) DecodeGroup(pb Message) error {
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
func (p *Buffer) Unmarshal(pb Message) error {
// 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 {
// 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:])
p.index = len(p.buf)
return err
}
typ, base, err := getbase(pb)
if err != nil {
return err
}
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
if collectStats {
stats.Decode++
}
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
// Slow workaround for messages that aren't Unmarshalers.
// This includes some hand-coded .pb.go files and
// bootstrap protos.
// TODO: fix all of those and then add Unmarshal to
// the Message interface. Then:
// The cast above and code below can be deleted.
// The old unmarshaler can be deleted.
// Clients can call Unmarshal directly (can already do that, actually).
var info InternalMessageInfo
err := info.Unmarshal(pb, p.buf[p.index:])
p.index = len(p.buf)
return err
}

350
vendor/github.com/golang/protobuf/proto/discard.go generated vendored Normal file
View 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)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -109,15 +109,6 @@ func equalStruct(v1, v2 reflect.Value) bool {
// set/unset mismatch
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()
}
if !equalAny(f1, f2, sprop.Prop[i]) {
@ -146,11 +137,7 @@ func equalStruct(v1, v2 reflect.Value) bool {
u1 := uf.Bytes()
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
if !bytes.Equal(u1, u2) {
return false
}
return true
return bytes.Equal(u1, u2)
}
// 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
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 {
// Both are unencoded.
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]
}
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)
continue
return false
}
var err error
if m1 == nil {

View File

@ -38,6 +38,7 @@ package proto
import (
"errors"
"fmt"
"io"
"reflect"
"strconv"
"sync"
@ -91,14 +92,29 @@ func (n notLocker) Unlock() {}
// 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
// the extendableProto interface.
func extendable(p interface{}) (extendableProto, bool) {
if ep, ok := p.(extendableProto); ok {
return ep, ok
func extendable(p interface{}) (extendableProto, error) {
switch p := p.(type) {
case extendableProto:
if isNilPtr(p) {
return nil, fmt.Errorf("proto: nil %T is not extendable", p)
}
if ep, ok := p.(extendableProtoV1); ok {
return extensionAdapter{ep}, ok
return p, nil
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.
@ -143,9 +159,6 @@ func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Loc
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.
// Used in generated code from the protocol compiler.
type ExtensionDesc struct {
@ -179,8 +192,8 @@ type Extension struct {
// SetRawExtension is for testing only.
func SetRawExtension(base Message, id int32, b []byte) {
epb, ok := extendable(base)
if !ok {
epb, err := extendable(base)
if err != nil {
return
}
extmap := epb.extensionsWrite()
@ -205,7 +218,7 @@ func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
pbi = ea.extendableProtoV1
}
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.
if !isExtensionField(pb, extension.Field) {
@ -250,85 +263,11 @@ func extensionProperties(ed *ExtensionDesc) *Properties {
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.
func HasExtension(pb Message, extension *ExtensionDesc) bool {
// TODO: Check types, field numbers, etc.?
epb, ok := extendable(pb)
if !ok {
epb, err := extendable(pb)
if err != nil {
return false
}
extmap, mu := epb.extensionsRead()
@ -336,15 +275,15 @@ func HasExtension(pb Message, extension *ExtensionDesc) bool {
return false
}
mu.Lock()
_, ok = extmap[extension.Field]
_, ok := extmap[extension.Field]
mu.Unlock()
return ok
}
// ClearExtension removes the given extension from pb.
func ClearExtension(pb Message, extension *ExtensionDesc) {
epb, ok := extendable(pb)
if !ok {
epb, err := extendable(pb)
if err != nil {
return
}
// TODO: Check types, field numbers, etc.?
@ -352,17 +291,27 @@ func ClearExtension(pb Message, extension *ExtensionDesc) {
delete(extmap, extension.Field)
}
// GetExtension parses and returns the given extension of pb.
// If the extension is not present and has no default value it returns ErrMissingExtension.
// GetExtension retrieves a proto2 extended field from pb.
//
// 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) {
epb, ok := extendable(pb)
if !ok {
return nil, errors.New("proto: not an extendable proto")
epb, err := extendable(pb)
if err != nil {
return nil, err
}
if extension.ExtendedType != nil {
// can only check type if this is a complete descriptor
if err := checkExtensionTypes(epb, extension); err != nil {
return nil, err
}
}
emap, mu := epb.extensionsRead()
if emap == nil {
@ -388,6 +337,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
return e.value, nil
}
if extension.ExtensionType == nil {
// incomplete descriptor
return e.enc, nil
}
v, err := decodeExtension(e.enc, extension)
if err != nil {
return nil, err
@ -405,6 +359,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
// defaultExtensionValue returns the default value for extension.
// If no default for an extension is defined ErrMissingExtension is returned.
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
if extension.ExtensionType == nil {
// incomplete descriptor, so no default
return nil, ErrMissingExtension
}
t := reflect.TypeOf(extension.ExtensionType)
props := extensionProperties(extension)
@ -439,31 +398,28 @@ func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
// decodeExtension decodes an extension encoded in b.
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
o := NewBuffer(b)
t := reflect.TypeOf(extension.ExtensionType)
props := extensionProperties(extension)
unmarshal := typeUnmarshaler(t, extension.Tag)
// t is a pointer to a struct, pointer to basic type or a slice.
// Allocate a "field" to store the pointer/slice itself; the
// 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 }.
// Allocate space to store the pointer/slice.
value := reflect.New(t).Elem()
var err error
for {
// Discard wire type and field number varint. It isn't needed.
if _, err := o.DecodeVarint(); err != nil {
x, n := decodeVarint(b)
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
}
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
return nil, err
}
if o.index >= len(o.buf) {
if len(b) == 0 {
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.
// 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) {
epb, ok := extendable(pb)
if !ok {
return nil, errors.New("proto: not an extendable proto")
epb, err := extendable(pb)
if err != nil {
return nil, err
}
extensions = make([]interface{}, len(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
// just the Field field, which defines the extension's field number.
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
epb, ok := extendable(pb)
if !ok {
return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
epb, err := extendable(pb)
if err != nil {
return nil, err
}
registeredExtensions := RegisteredExtensions(pb)
@ -523,9 +479,9 @@ func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
// SetExtension sets the specified extension of pb to the specified value.
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
epb, ok := extendable(pb)
if !ok {
return errors.New("proto: not an extendable proto")
epb, err := extendable(pb)
if err != nil {
return err
}
if err := checkExtensionTypes(epb, extension); err != nil {
return err
@ -550,8 +506,8 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
// ClearAllExtensions clears all extensions from pb.
func ClearAllExtensions(pb Message) {
epb, ok := extendable(pb)
if !ok {
epb, err := extendable(pb)
if err != nil {
return
}
m := epb.extensionsWrite()

View File

@ -273,6 +273,67 @@ import (
"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.
type Message interface {
Reset()
@ -309,16 +370,7 @@ type Buffer struct {
buf []byte // encode/decode byte stream
index int // read point
// pools of basic types to amortize allocation.
bools []bool
uint32s []uint32
uint64s []uint64
// extra pools, only used with pointer_reflect.go
int32s []int32
int64s []int64
float32s []float32
float64s []float64
deterministic bool
}
// 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.
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.
*/
@ -831,22 +907,12 @@ func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMes
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.
// 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 {
s := mapKeySorter{
vs: vs,
// default Less function: textual comparison
less: func(a, b reflect.Value) bool {
return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
},
}
s := mapKeySorter{vs: vs}
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
// numeric keys are sorted numerically.
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps.
if len(vs) == 0 {
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() }
case reflect.Uint32, reflect.Uint64:
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
@ -895,3 +967,13 @@ const ProtoPackageIsVersion2 = true
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
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
}

View File

@ -42,6 +42,7 @@ import (
"fmt"
"reflect"
"sort"
"sync"
)
// 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 {
if ms.find(pb) != nil {
return true
}
return false
return ms.find(pb) != nil
}
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.
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
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) {
case *XXX_InternalExtensions:
if err := encodeExtensions(exts); err != nil {
return nil, err
}
m, _ = exts.extensionsRead()
var u marshalInfo
siz := u.sizeMessageSet(exts)
b := make([]byte, 0, siz)
return u.appendMessageSet(b, exts, deterministic)
case map[int32]Extension:
if err := encodeExtensionsMap(exts); err != nil {
return nil, err
// This is an old-style extension map.
// 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:
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.
// 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 {
var m map[int32]Extension
switch exts := exts.(type) {
@ -235,7 +229,15 @@ func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
var m map[int32]Extension
switch exts := exts.(type) {
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:
m = exts
default:
@ -253,15 +255,16 @@ func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
for i, id := range ids {
ext := m[id]
if i > 0 {
b.WriteByte(',')
}
msd, ok := messageSetMap[id]
if !ok {
// Unknown type; we can't render it, so skip it.
continue
}
if i > 0 && b.Len() > 1 {
b.WriteByte(',')
}
fmt.Fprintf(&b, `"[%s]":`, msd.name)
x := ext.value

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// 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.
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
@ -38,32 +38,13 @@
package proto
import (
"math"
"reflect"
"sync"
)
// A structPointer is a pointer to a struct.
type structPointer struct {
v reflect.Value
}
const unsafeAllowed = false
// toStructPointer returns a structPointer equivalent to the given reflect value.
// 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.
// A field identifies a field in a struct, accessible from a pointer.
// In this implementation, a field is identified by the sequence of field indices
// passed to reflect's FieldByIndex.
type field []int
@ -76,409 +57,301 @@ func toField(f *reflect.StructField) field {
// invalidField is an invalid field identifier.
var invalidField = field(nil)
// zeroField is a noop when calling pointer.offset.
var zeroField = field([]int{})
// IsValid reports whether the field identifier is valid.
func (f field) IsValid() bool { return f != nil }
// field returns the given field in the struct as a reflect value.
func structPointer_field(p structPointer, f field) reflect.Value {
// Special case: an extension map entry with a value of type T
// passes a *T to the struct-handling code with a zero field,
// expecting that it will be treated as equivalent to *struct{ X T },
// 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 {
// The pointer type is for the table-driven decoder.
// The implementation here uses a reflect.Value of pointer type to
// create a generic pointer. In pointer_unsafe.go we use unsafe
// instead of reflect to implement the same (but faster) interface.
type pointer struct {
v reflect.Value
}
func (p structPointerSlice) Len() int { return p.v.Len() }
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
func (p structPointerSlice) Append(q structPointer) {
p.v.Set(reflect.Append(p.v, q.v))
// toPointer converts an interface of pointer type to a pointer
// that points to the same target.
func toPointer(i *Message) pointer {
return pointer{v: reflect.ValueOf(*i)}
}
var (
int32Type = reflect.TypeOf(int32(0))
uint32Type = reflect.TypeOf(uint32(0))
float32Type = reflect.TypeOf(float32(0))
int64Type = reflect.TypeOf(int64(0))
uint64Type = reflect.TypeOf(uint64(0))
float64Type = reflect.TypeOf(float64(0))
)
// 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
// toAddrPointer converts an interface to a pointer that points to
// the interface data.
func toAddrPointer(i *interface{}, isptr bool) pointer {
v := reflect.ValueOf(*i)
u := reflect.New(v.Type())
u.Elem().Set(v)
return pointer{v: u}
}
// IsNil reports whether p is nil.
func word32_IsNil(p word32) bool {
// valToPointer converts v to a pointer. v must be of pointer type.
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()
}
// Set sets p to point at a newly allocated word with bits set to x.
func word32_Set(p word32, o *Buffer, x uint32) {
t := p.v.Type().Elem()
switch t {
case int32Type:
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()
// grow updates the slice s in place to make it one element longer.
// s must be addressable.
// Returns the (addressable) new element.
func grow(s reflect.Value) reflect.Value {
n, m := s.Len(), s.Cap()
if n < m {
p.v.SetLen(n + 1)
s.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.Int32:
elem.SetInt(int64(int32(x)))
case reflect.Uint32:
elem.SetUint(uint64(x))
case reflect.Float32:
elem.SetFloat(float64(math.Float32frombits(x)))
s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem())))
}
return s.Index(n)
}
func (p word32Slice) Len() int {
return p.v.Len()
func (p pointer) toInt64() *int64 {
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 {
elem := p.v.Index(i)
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")
var int32ptr = reflect.TypeOf((*int32)(nil))
func (p pointer) toInt32() *int32 {
return p.v.Convert(int32ptr).Interface().(*int32)
}
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
return word32Slice{structPointer_field(p, f)}
// The toInt32Ptr/Slice methods don't work because of enums.
// Instead, we must use set/get methods for the int32ptr/slice case.
/*
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.
type word64 struct {
v reflect.Value
// getInt32Slice copies []int32 from p as a new slice.
// This behavior differs from the implementation in pointer_unsafe.go.
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) {
t := p.v.Type().Elem()
switch t {
case int64Type:
if len(o.int64s) == 0 {
o.int64s = make([]int64, uint64PoolSize)
}
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:]
// setInt32Slice copies []int32 into p as a new slice.
// This behavior differs from the implementation in pointer_unsafe.go.
func (p pointer) setInt32Slice(v []int32) {
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
// raw int32 type
p.v.Elem().Set(reflect.ValueOf(v))
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 {
return p.v.IsNil()
func (p pointer) toUint64() *uint64 {
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 {
elem := p.v.Elem()
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return elem.Uint()
case reflect.Float64:
return math.Float64bits(elem.Float())
// getPointerSlice copies []*T from p as a new []pointer.
// This behavior differs from the implementation in pointer_unsafe.go.
func (p pointer) getPointerSlice() []pointer {
if p.v.IsNil() {
return nil
}
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 {
return word64{structPointer_field(p, f)}
}
// word64Val is like word32Val but for 64-bit values.
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))
// setPointerSlice copies []pointer into p as a new []*T.
// This behavior differs from the implementation in pointer_unsafe.go.
func (p pointer) setPointerSlice(v []pointer) {
if v == nil {
p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem())
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 {
elem := p.v
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return elem.Uint()
case reflect.Float64:
return math.Float64bits(elem.Float())
// getInterfacePointer returns a pointer that points to the
// interface data of the interface pointed by p.
func (p pointer) getInterfacePointer() pointer {
if p.v.Elem().IsNil() {
return pointer{v: p.v.Elem()}
}
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 {
return word64Val{structPointer_field(p, f)}
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
// TODO: check that p.v.Type().Elem() == t?
return p.v
}
type word64Slice struct {
v reflect.Value
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
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) {
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)}
}
var atomicLock sync.Mutex

View File

@ -29,7 +29,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// 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.
@ -37,38 +37,13 @@ package proto
import (
"reflect"
"sync/atomic"
"unsafe"
)
// NOTE: These type_Foo functions would more idiomatically be methods,
// 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.
const unsafeAllowed = true
// A structPointer is a pointer to a struct.
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.
// A field identifies a field in a struct, accessible from a pointer.
// In this implementation, a field is identified by its byte offset from the start of the struct.
type field uintptr
@ -80,191 +55,254 @@ func toField(f *reflect.StructField) field {
// invalidField is an invalid field identifier.
const invalidField = ^field(0)
// zeroField is a noop when calling pointer.offset.
const zeroField = field(0)
// IsValid reports whether the field identifier is valid.
func (f field) IsValid() bool {
return f != ^field(0)
return f != invalidField
}
// Bytes returns the address of a []byte field in the struct.
func structPointer_Bytes(p structPointer, f field) *[]byte {
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// The pointer type below is for the new table-driven encoder/decoder.
// The implementation here uses unsafe.Pointer to create a generic pointer.
// 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.
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// size of pointer
var ptrSize = unsafe.Sizeof(uintptr(0))
// 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.
func structPointer_Bool(p structPointer, f field) **bool {
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// toAddrPointer converts an interface to a pointer that points to
// the interface data.
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.
func structPointer_BoolVal(p structPointer, f field) *bool {
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// valToPointer converts v to a pointer. v must be of pointer type.
func valToPointer(v reflect.Value) pointer {
return pointer{p: unsafe.Pointer(v.Pointer())}
}
// BoolSlice returns the address of a []bool field in the struct.
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// offset converts from a pointer to a structure to a pointer to
// one of its fields.
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 structPointer_String(p structPointer, f field) **string {
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
func (p pointer) isNil() bool {
return p.p == nil
}
// StringVal returns the address of a string field in the struct.
func structPointer_StringVal(p structPointer, f field) *string {
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
func (p pointer) toInt64() *int64 {
return (*int64)(p.p)
}
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.
func structPointer_StringSlice(p structPointer, f field) *[]string {
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist.
/*
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.
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// getInt32Slice loads a []int32 from p.
// The value returned is aliased with the original slice.
// 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 {
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// setInt32Slice stores a []int32 to p.
// 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.
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead?
func (p pointer) appendInt32Slice(v int32) {
s := (*[]int32)(p.p)
*s = append(*s, v)
}
// SetStructPointer writes a *struct field in the struct.
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
func (p pointer) toUint64() *uint64 {
return (*uint64)(p.p)
}
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.
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// getPointerSlice loads []*T from p as a []pointer.
// The value returned is aliased with the original slice.
// 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.
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
// setPointerSlice stores []pointer into p as a []*T.
// The value set is aliased with the input slice.
// 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).
type structPointerSlice []structPointer
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
// getPointer loads the pointer at p and returns it.
func (p pointer) getPointer() pointer {
return pointer{p: *(*unsafe.Pointer)(p.p)}
}
// Set sets *v to point at a newly allocated word set to x.
func word32_Set(p word32, o *Buffer, x uint32) {
if len(o.uint32s) == 0 {
o.uint32s = make([]uint32, uint32PoolSize)
}
o.uint32s[0] = x
*p = &o.uint32s[0]
o.uint32s = o.uint32s[1:]
// setPointer stores the pointer q at p.
func (p pointer) setPointer(q pointer) {
*(*unsafe.Pointer)(p.p) = q.p
}
// Get gets the value pointed at by *v.
func word32_Get(p word32) uint32 {
return **p
// append q to the slice pointed to by p.
func (p pointer) appendPointer(q pointer) {
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.
func structPointer_Word32(p structPointer, f field) word32 {
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
// getInterfacePointer returns a pointer that points to the
// interface data of the interface pointed by p.
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.
type word32Val *uint32
// Set sets *p to x.
func word32Val_Set(p word32Val, x uint32) {
*p = x
// asPointerTo returns a reflect.Value that is a pointer to an
// object of type t stored at p.
func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
return reflect.NewAt(t, p.p)
}
// Get gets the value pointed at by p.
func word32Val_Get(p word32Val) uint32 {
return *p
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
}
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
func structPointer_Word32Val(p structPointer, f field) word32Val {
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
}
// A word32Slice is a slice of 32-bit values.
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 atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
}
// word64 is like word32 but for 64-bit values.
type word64 **uint64
func word64_Set(p word64, o *Buffer, x uint64) {
if len(o.uint64s) == 0 {
o.uint64s = make([]uint64, uint64PoolSize)
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
}
o.uint64s[0] = x
*p = &o.uint64s[0]
o.uint64s = o.uint64s[1:]
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
}
func word64_IsNil(p word64) bool {
return *p == nil
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
}
func word64_Get(p word64) uint64 {
return **p
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
}
func structPointer_Word64(p structPointer, f field) word64 {
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)))
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
}

View File

@ -58,42 +58,6 @@ const (
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
// use-cases. Encoded protocol buffers are often in tag order with small tag
// numbers.
@ -140,13 +104,6 @@ type StructProperties struct {
decoderTags tagMap // map from proto tag 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
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.
// It is keyed by the original name of a field.
@ -182,41 +139,24 @@ type Properties struct {
Repeated bool
Packed bool // relevant for repeated primitives 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
Default string // default value
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
sprop *StructProperties // set for struct types only
isMarshaler bool
isUnmarshaler bool
mtype reflect.Type // set for map types only
mkeyprop *Properties // set for map types only
mvalprop *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
MapKeyProp *Properties // set for map types only
MapValProp *Properties // set for map types only
}
// String formats the properties in the protobuf struct field tag style.
func (p *Properties) String() string {
s := p.Wire
s = ","
s += ","
s += strconv.Itoa(p.Tag)
if p.Required {
s += ",req"
@ -262,29 +202,14 @@ func (p *Properties) Parse(s string) {
switch p.Wire {
case "varint":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeVarint
p.valDec = (*Buffer).DecodeVarint
p.valSize = sizeVarint
case "fixed32":
p.WireType = WireFixed32
p.valEnc = (*Buffer).EncodeFixed32
p.valDec = (*Buffer).DecodeFixed32
p.valSize = sizeFixed32
case "fixed64":
p.WireType = WireFixed64
p.valEnc = (*Buffer).EncodeFixed64
p.valDec = (*Buffer).DecodeFixed64
p.valSize = sizeFixed64
case "zigzag32":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeZigzag32
p.valDec = (*Buffer).DecodeZigzag32
p.valSize = sizeZigzag32
case "zigzag64":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeZigzag64
p.valDec = (*Buffer).DecodeZigzag64
p.valSize = sizeZigzag64
case "bytes", "group":
p.WireType = WireBytes
// no numeric converter for non-numeric types
@ -299,6 +224,7 @@ func (p *Properties) Parse(s string) {
return
}
outer:
for i := 2; i < len(fields); i++ {
f := fields[i]
switch {
@ -326,256 +252,41 @@ func (p *Properties) Parse(s string) {
if i+1 < len(fields) {
// Commas aren't escaped, and def is always last.
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()
// Initialize the fields for encoding and decoding.
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
p.enc = nil
p.dec = nil
p.size = nil
// setFieldProps initializes the field properties for submessages and maps.
func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
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:
switch t2 := t1.Elem(); t2.Kind() {
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:
if t1.Elem().Kind() == reflect.Struct {
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:
switch t2 := t1.Elem(); t2.Kind() {
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:
if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct {
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:
p.enc = (*Buffer).enc_new_map
p.dec = (*Buffer).dec_new_map
p.size = size_new_map
p.mtype = t1
p.mkeyprop = &Properties{}
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
p.mvalprop = &Properties{}
p.MapKeyProp = &Properties{}
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
p.MapValProp = &Properties{}
vtype := p.mtype.Elem()
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
// The value type is not a message (*T) or bytes ([]byte),
// so we need encoders for the pointer to this type.
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 lockGetProp {
p.sprop = GetProperties(p.stype)
@ -587,31 +298,8 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock
var (
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.
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
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!"
p.Name = name
p.OrigName = name
if f != nil {
p.field = toField(f)
}
if tag == "" {
return
}
p.Parse(tag)
p.setEncAndDec(typ, f, lockGetProp)
p.setFieldProps(typ, f, lockGetProp)
}
var (
@ -678,9 +363,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
propertiesMap[t] = prop
// build properties
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) ||
reflect.PtrTo(t).Implements(extendableProtoV1Type)
prop.unrecField = invalidField
prop.Prop = make([]*Properties, t.NumField())
prop.order = make([]int, t.NumField())
@ -690,17 +372,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
name := f.Name
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
if oneof != "" {
// Oneof fields don't use the traditional protobuf tag.
@ -715,9 +386,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
}
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.
@ -728,8 +396,7 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
}
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
var oots []interface{}
prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
prop.stype = t
_, _, _, oots = om.XXX_OneofFuncs()
// Interpret oneof metadata.
prop.OneofTypes = make(map[string]*OneofProperties)
@ -779,30 +446,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
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.
// 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.
// The string is a fully-qualified proto name ("pkg.Message").
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)
)
// RegisterType is called from generated code and maps from the fully qualified
// proto name to the type (pointer to struct) of the protocol buffer.
func RegisterType(x Message, name string) {
if _, ok := protoTypes[name]; ok {
if _, ok := protoTypedNils[name]; ok {
// TODO: Some day, make this a panic.
log.Printf("proto: duplicate proto type registered: %s", name)
return
}
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
}
@ -855,7 +520,14 @@ func MessageName(x Message) string {
}
// 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.
var (

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
View 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)
}

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,6 @@ import (
var (
newline = []byte("\n")
spaces = []byte(" ")
gtNewline = []byte(">\n")
endBraceNewline = []byte("}\n")
backslashN = []byte{'\\', 'n'}
backslashR = []byte{'\\', 'r'}
@ -170,11 +169,6 @@ func writeName(w *textWriter, props *Properties) error {
return nil
}
// raw is the interface satisfied by RawMessage.
type raw interface {
Bytes() []byte
}
func requiresQuotes(u string) bool {
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
for _, ch := range u {
@ -269,6 +263,10 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
props := sprops.Prop[i]
name := st.Field(i).Name
if name == "XXX_NoUnkeyedLiteral" {
continue
}
if strings.HasPrefix(name, "XXX_") {
// There are two XXX_ fields:
// XXX_unrecognized []byte
@ -355,7 +353,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return err
}
}
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
@ -372,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return err
}
}
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
@ -436,12 +434,6 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
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.
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).
pv := sv.Addr()
if _, ok := extendable(pv.Interface()); ok {
if _, err := extendable(pv.Interface()); err == nil {
if err := tm.writeExtensions(w, pv); err != nil {
return err
}
@ -464,27 +456,6 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
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.
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v)
@ -535,6 +506,19 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
}
}
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 {
text, err := etm.MarshalText()
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 {
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
}
}
w.unindent()
if err := w.WriteByte(ket); err != nil {
return err

View File

@ -206,7 +206,6 @@ func (p *textParser) advance() {
var (
errBadUTF8 = errors.New("proto: bad UTF-8")
errBadHex = errors.New("proto: bad hexadecimal")
)
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
case '\'', '"', '\\':
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 {
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
}
base := 8
ss := s[:2]
ss := string(r) + s[:2]
s = s[2:]
if r == 'x' || r == 'X' {
base = 16
} else {
ss = string(r) + ss
}
i, err := strconv.ParseUint(ss, base, 8)
i, err := strconv.ParseUint(ss, 8, 8)
if err != nil {
return "", "", err
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
}
return string([]byte{byte(i)}), s, nil
case 'u', 'U':
n := 4
if r == 'U' {
case 'x', 'X', 'u', 'U':
var n int
switch r {
case 'x', 'X':
n = 2
case 'u':
n = 4
case 'U':
n = 8
}
if len(s) < n {
return "", "", fmt.Errorf(`\%c requires %d 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
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
}
ss := 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)
}
// 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().
// It makes the next advance() a no-op.
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 {
return err
}
if err := p.readAny(key, props.mkeyprop); err != nil {
if err := p.readAny(key, props.MapKeyProp); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
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
}
if err := p.readAny(val, props.mvalprop); err != nil {
if err := p.readAny(val, props.MapValProp); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
@ -728,6 +714,9 @@ func (p *textParser) consumeExtName() (string, error) {
if tok.err != nil {
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
}
@ -883,13 +872,9 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error {
// UnmarshalText returns *RequiredNotSetError.
func UnmarshalText(s string, pb Message) error {
if um, ok := pb.(encoding.TextUnmarshaler); ok {
err := um.UnmarshalText([]byte(s))
return err
return um.UnmarshalText([]byte(s))
}
pb.Reset()
v := reflect.ValueOf(pb)
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
return pe
}
return nil
return newTextParser(s).readStruct(v.Elem(), "")
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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
View File

View File

@ -0,0 +1 @@
module github.com/shurcooL/sanitized_anchor_name

4
vendor/github.com/skip2/go-qrcode/.gitignore generated vendored Normal file
View File

@ -0,0 +1,4 @@
*.sw*
*.png
*.directory
qrcode/qrcode

8
vendor/github.com/skip2/go-qrcode/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
}

View 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
View 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 := &regularSymbol{
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
View 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

File diff suppressed because it is too large Load Diff

93
vendor/golang.org/x/crypto/hkdf/hkdf.go generated vendored Normal file
View 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)
}

View File

@ -241,7 +241,10 @@ func (ws *Conn) Close() error {
return err1
}
// IsClientConn reports whether ws is a client-side connection.
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 }
// LocalAddr returns the WebSocket Origin for the connection for client, or

661
vendor/maunium.net/go/mautrix-whatsapp/LICENSE generated vendored Normal file
View 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/>.

View 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)
}
}

View 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)
}
}

View 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)
}
}

View 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)
}
}
}

View 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)
}
}

View 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)
}
}

View 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)
}
}

View 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)
}
}

View 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
View File

@ -1,5 +1,7 @@
# github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
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
# 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/rwu
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
# github.com/davecgh/go-spew v1.1.1
@ -25,7 +35,7 @@ github.com/dgrijalva/jwt-go
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
# 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/protoc-gen-go/descriptor
# 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/sshd
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/sirupsen/logrus v1.3.0
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
github.com/spf13/afero/mem
@ -179,7 +193,7 @@ go.uber.org/zap/internal/bufferpool
go.uber.org/zap/buffer
go.uber.org/zap/internal/color
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/acme/autocert
golang.org/x/crypto/nacl/secretbox
@ -193,8 +207,9 @@ golang.org/x/crypto/blowfish
golang.org/x/crypto/curve25519
golang.org/x/crypto/ed25519
golang.org/x/crypto/internal/chacha20
golang.org/x/crypto/hkdf
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/sys v0.0.0-20190129075346-302c3dd5f1cc
golang.org/x/sys/unix
@ -214,3 +229,5 @@ golang.org/x/text/encoding/internal
gopkg.in/natefinch/lumberjack.v2
# gopkg.in/yaml.v2 v2.2.2
gopkg.in/yaml.v2
# maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3
maunium.net/go/mautrix-whatsapp/whatsapp-ext