diff options
author | Wim <wim@42.be> | 2016-09-19 20:53:26 +0200 |
---|---|---|
committer | Wim <wim@42.be> | 2016-09-19 20:53:26 +0200 |
commit | a0b84beb9bae3514940a0cde40948e885184f555 (patch) | |
tree | 0e8d22c5e2292c24e46699e59f442795b6158b3a /vendor | |
parent | 0816e968318be5a4b165ac8fd30c032c6ecce61c (diff) | |
download | matterbridge-msglm-a0b84beb9bae3514940a0cde40948e885184f555.tar.gz matterbridge-msglm-a0b84beb9bae3514940a0cde40948e885184f555.tar.bz2 matterbridge-msglm-a0b84beb9bae3514940a0cde40948e885184f555.zip |
Add Discord support
Diffstat (limited to 'vendor')
34 files changed, 9777 insertions, 0 deletions
diff --git a/vendor/github.com/bwmarrin/discordgo/LICENSE b/vendor/github.com/bwmarrin/discordgo/LICENSE new file mode 100644 index 00000000..8d062ea5 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, Bruce Marriner +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 discordgo 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. + diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go new file mode 100644 index 00000000..d1cfddf5 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/discord.go @@ -0,0 +1,257 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains high level helper functions and easy entry points for the +// entire discordgo package. These functions are beling developed and are very +// experimental at this point. They will most likley change so please use the +// low level functions if that's a problem. + +// Package discordgo provides Discord binding for Go +package discordgo + +import ( + "fmt" + "reflect" +) + +// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/) +const VERSION = "0.13.0" + +// New creates a new Discord session and will automate some startup +// tasks if given enough information to do so. Currently you can pass zero +// arguments and it will return an empty Discord session. +// There are 3 ways to call New: +// With a single auth token - All requests will use the token blindly, +// no verification of the token will be done and requests may fail. +// With an email and password - Discord will sign in with the provided +// credentials. +// With an email, password and auth token - Discord will verify the auth +// token, if it is invalid it will sign in with the provided +// credentials. This is the Discord recommended way to sign in. +func New(args ...interface{}) (s *Session, err error) { + + // Create an empty Session interface. + s = &Session{ + State: NewState(), + StateEnabled: true, + Compress: true, + ShouldReconnectOnError: true, + ShardID: 0, + ShardCount: 1, + } + + // If no arguments are passed return the empty Session interface. + if args == nil { + return + } + + // Variables used below when parsing func arguments + var auth, pass string + + // Parse passed arguments + for _, arg := range args { + + switch v := arg.(type) { + + case []string: + if len(v) > 3 { + err = fmt.Errorf("Too many string parameters provided.") + return + } + + // First string is either token or username + if len(v) > 0 { + auth = v[0] + } + + // If second string exists, it must be a password. + if len(v) > 1 { + pass = v[1] + } + + // If third string exists, it must be an auth token. + if len(v) > 2 { + s.Token = v[2] + } + + case string: + // First string must be either auth token or username. + // Second string must be a password. + // Only 2 input strings are supported. + + if auth == "" { + auth = v + } else if pass == "" { + pass = v + } else if s.Token == "" { + s.Token = v + } else { + err = fmt.Errorf("Too many string parameters provided.") + return + } + + // case Config: + // TODO: Parse configuration struct + + default: + err = fmt.Errorf("Unsupported parameter type provided.") + return + } + } + + // If only one string was provided, assume it is an auth token. + // Otherwise get auth token from Discord, if a token was specified + // Discord will verify it for free, or log the user in if it is + // invalid. + if pass == "" { + s.Token = auth + } else { + err = s.Login(auth, pass) + if err != nil || s.Token == "" { + err = fmt.Errorf("Unable to fetch discord authentication token. %v", err) + return + } + } + + // The Session is now able to have RestAPI methods called on it. + // It is recommended that you now call Open() so that events will trigger. + + return +} + +// validateHandler takes an event handler func, and returns the type of event. +// eg. +// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate)) +// will return the reflect.Type of *discordgo.MessageCreate +func (s *Session) validateHandler(handler interface{}) reflect.Type { + + handlerType := reflect.TypeOf(handler) + + if handlerType.NumIn() != 2 { + panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).") + } + + if handlerType.In(0) != reflect.TypeOf(s) { + panic("Unable to add event handler, first argument must be of type *discordgo.Session.") + } + + eventType := handlerType.In(1) + + // Support handlers of type interface{}, this is a special handler, which is triggered on every event. + if eventType.Kind() == reflect.Interface { + eventType = nil + } + + return eventType +} + +// AddHandler allows you to add an event handler that will be fired anytime +// the Discord WSAPI event that matches the interface fires. +// eventToInterface in events.go has a list of all the Discord WSAPI events +// and their respective interface. +// eg: +// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) { +// }) +// +// or: +// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) { +// }) +// The return value of this method is a function, that when called will remove the +// event handler. +func (s *Session) AddHandler(handler interface{}) func() { + + s.initialize() + + eventType := s.validateHandler(handler) + + s.handlersMu.Lock() + defer s.handlersMu.Unlock() + + h := reflect.ValueOf(handler) + + s.handlers[eventType] = append(s.handlers[eventType], h) + + // This must be done as we need a consistent reference to the + // reflected value, otherwise a RemoveHandler method would have + // been nice. + return func() { + s.handlersMu.Lock() + defer s.handlersMu.Unlock() + + handlers := s.handlers[eventType] + for i, v := range handlers { + if h == v { + s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...) + return + } + } + } +} + +// handle calls any handlers that match the event type and any handlers of +// interface{}. +func (s *Session) handle(event interface{}) { + + s.handlersMu.RLock() + defer s.handlersMu.RUnlock() + + if s.handlers == nil { + return + } + + handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)} + + if handlers, ok := s.handlers[nil]; ok { + for _, handler := range handlers { + go handler.Call(handlerParameters) + } + } + + if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok { + for _, handler := range handlers { + go handler.Call(handlerParameters) + } + } +} + +// initialize adds all internal handlers and state tracking handlers. +func (s *Session) initialize() { + + s.log(LogInformational, "called") + + s.handlersMu.Lock() + if s.handlers != nil { + s.handlersMu.Unlock() + return + } + + s.handlers = map[interface{}][]reflect.Value{} + s.handlersMu.Unlock() + + s.AddHandler(s.onReady) + s.AddHandler(s.onResumed) + s.AddHandler(s.onVoiceServerUpdate) + s.AddHandler(s.onVoiceStateUpdate) + s.AddHandler(s.State.onInterface) +} + +// onReady handles the ready event. +func (s *Session) onReady(se *Session, r *Ready) { + + // Store the SessionID within the Session struct. + s.sessionID = r.SessionID + + // Start the heartbeat to keep the connection alive. + go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval) +} + +// onResumed handles the resumed event. +func (s *Session) onResumed(se *Session, r *Resumed) { + + // Start the heartbeat to keep the connection alive. + go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval) +} diff --git a/vendor/github.com/bwmarrin/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go new file mode 100644 index 00000000..682433d6 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/endpoints.go @@ -0,0 +1,99 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains variables for all known Discord end points. All functions +// throughout the Discordgo package use these variables for all connections +// to Discord. These are all exported and you may modify them if needed. + +package discordgo + +// Known Discord API Endpoints. +var ( + EndpointStatus = "https://status.discordapp.com/api/v2/" + EndpointSm = EndpointStatus + "scheduled-maintenances/" + EndpointSmActive = EndpointSm + "active.json" + EndpointSmUpcoming = EndpointSm + "upcoming.json" + + EndpointDiscord = "https://discordapp.com/" + EndpointAPI = EndpointDiscord + "api/" + EndpointGuilds = EndpointAPI + "guilds/" + EndpointChannels = EndpointAPI + "channels/" + EndpointUsers = EndpointAPI + "users/" + EndpointGateway = EndpointAPI + "gateway" + + EndpointAuth = EndpointAPI + "auth/" + EndpointLogin = EndpointAuth + "login" + EndpointLogout = EndpointAuth + "logout" + EndpointVerify = EndpointAuth + "verify" + EndpointVerifyResend = EndpointAuth + "verify/resend" + EndpointForgotPassword = EndpointAuth + "forgot" + EndpointResetPassword = EndpointAuth + "reset" + EndpointRegister = EndpointAuth + "register" + + EndpointVoice = EndpointAPI + "/voice/" + EndpointVoiceRegions = EndpointVoice + "regions" + EndpointVoiceIce = EndpointVoice + "ice" + + EndpointTutorial = EndpointAPI + "tutorial/" + EndpointTutorialIndicators = EndpointTutorial + "indicators" + + EndpointTrack = EndpointAPI + "track" + EndpointSso = EndpointAPI + "sso" + EndpointReport = EndpointAPI + "report" + EndpointIntegrations = EndpointAPI + "integrations" + + EndpointUser = func(uID string) string { return EndpointUsers + uID } + EndpointUserAvatar = func(uID, aID string) string { return EndpointUsers + uID + "/avatars/" + aID + ".jpg" } + EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" } + EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" } + EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID } + EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" } + EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } + EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" } + EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } + + EndpointGuild = func(gID string) string { return EndpointGuilds + gID } + EndpointGuildInivtes = func(gID string) string { return EndpointGuilds + gID + "/invites" } + EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } + EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } + EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } + EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } + EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } + EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } + EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID } + EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" } + EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } + EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } + EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } + EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" } + EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } + EndpointGuildIcon = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" } + EndpointGuildSplash = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" } + + EndpointChannel = func(cID string) string { return EndpointChannels + cID } + EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" } + EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID } + EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" } + EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" } + EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" } + EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID } + EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" } + EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" } + EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" } + EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID } + + EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID } + + EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" } + + EndpointEmoji = func(eID string) string { return EndpointAPI + "emojis/" + eID + ".png" } + + EndpointOauth2 = EndpointAPI + "oauth2/" + EndpointApplications = EndpointOauth2 + "applications" + EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" } +) diff --git a/vendor/github.com/bwmarrin/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go new file mode 100644 index 00000000..72aabf67 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/events.go @@ -0,0 +1,159 @@ +package discordgo + +// eventToInterface is a mapping of Discord WSAPI events to their +// DiscordGo event container. +// Each Discord WSAPI event maps to a unique interface. +// Use Session.AddHandler with one of these types to handle that +// type of event. +// eg: +// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) { +// }) +// +// or: +// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) { +// }) +var eventToInterface = map[string]interface{}{ + "CHANNEL_CREATE": ChannelCreate{}, + "CHANNEL_UPDATE": ChannelUpdate{}, + "CHANNEL_DELETE": ChannelDelete{}, + "GUILD_CREATE": GuildCreate{}, + "GUILD_UPDATE": GuildUpdate{}, + "GUILD_DELETE": GuildDelete{}, + "GUILD_BAN_ADD": GuildBanAdd{}, + "GUILD_BAN_REMOVE": GuildBanRemove{}, + "GUILD_MEMBER_ADD": GuildMemberAdd{}, + "GUILD_MEMBER_UPDATE": GuildMemberUpdate{}, + "GUILD_MEMBER_REMOVE": GuildMemberRemove{}, + "GUILD_ROLE_CREATE": GuildRoleCreate{}, + "GUILD_ROLE_UPDATE": GuildRoleUpdate{}, + "GUILD_ROLE_DELETE": GuildRoleDelete{}, + "GUILD_INTEGRATIONS_UPDATE": GuildIntegrationsUpdate{}, + "GUILD_EMOJIS_UPDATE": GuildEmojisUpdate{}, + "MESSAGE_ACK": MessageAck{}, + "MESSAGE_CREATE": MessageCreate{}, + "MESSAGE_UPDATE": MessageUpdate{}, + "MESSAGE_DELETE": MessageDelete{}, + "PRESENCE_UPDATE": PresenceUpdate{}, + "PRESENCES_REPLACE": PresencesReplace{}, + "READY": Ready{}, + "USER_UPDATE": UserUpdate{}, + "USER_SETTINGS_UPDATE": UserSettingsUpdate{}, + "USER_GUILD_SETTINGS_UPDATE": UserGuildSettingsUpdate{}, + "TYPING_START": TypingStart{}, + "VOICE_SERVER_UPDATE": VoiceServerUpdate{}, + "VOICE_STATE_UPDATE": VoiceStateUpdate{}, + "RESUMED": Resumed{}, +} + +// Connect is an empty struct for an event. +type Connect struct{} + +// Disconnect is an empty struct for an event. +type Disconnect struct{} + +// RateLimit is a struct for the RateLimited event +type RateLimit struct { + *TooManyRequests + URL string +} + +// MessageCreate is a wrapper struct for an event. +type MessageCreate struct { + *Message +} + +// MessageUpdate is a wrapper struct for an event. +type MessageUpdate struct { + *Message +} + +// MessageDelete is a wrapper struct for an event. +type MessageDelete struct { + *Message +} + +// ChannelCreate is a wrapper struct for an event. +type ChannelCreate struct { + *Channel +} + +// ChannelUpdate is a wrapper struct for an event. +type ChannelUpdate struct { + *Channel +} + +// ChannelDelete is a wrapper struct for an event. +type ChannelDelete struct { + *Channel +} + +// GuildCreate is a wrapper struct for an event. +type GuildCreate struct { + *Guild +} + +// GuildUpdate is a wrapper struct for an event. +type GuildUpdate struct { + *Guild +} + +// GuildDelete is a wrapper struct for an event. +type GuildDelete struct { + *Guild +} + +// GuildBanAdd is a wrapper struct for an event. +type GuildBanAdd struct { + *GuildBan +} + +// GuildBanRemove is a wrapper struct for an event. +type GuildBanRemove struct { + *GuildBan +} + +// GuildMemberAdd is a wrapper struct for an event. +type GuildMemberAdd struct { + *Member +} + +// GuildMemberUpdate is a wrapper struct for an event. +type GuildMemberUpdate struct { + *Member +} + +// GuildMemberRemove is a wrapper struct for an event. +type GuildMemberRemove struct { + *Member +} + +// GuildRoleCreate is a wrapper struct for an event. +type GuildRoleCreate struct { + *GuildRole +} + +// GuildRoleUpdate is a wrapper struct for an event. +type GuildRoleUpdate struct { + *GuildRole +} + +// PresencesReplace is an array of Presences for an event. +type PresencesReplace []*Presence + +// VoiceStateUpdate is a wrapper struct for an event. +type VoiceStateUpdate struct { + *VoiceState +} + +// UserUpdate is a wrapper struct for an event. +type UserUpdate struct { + *User +} + +// UserSettingsUpdate is a map for an event. +type UserSettingsUpdate map[string]interface{} + +// UserGuildSettingsUpdate is a map for an event. +type UserGuildSettingsUpdate struct { + *UserGuildSettings +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go b/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go new file mode 100644 index 00000000..cc61301c --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "encoding/binary" + "flag" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/bwmarrin/discordgo" +) + +func init() { + flag.StringVar(&token, "t", "", "Account Token") + flag.Parse() +} + +var token string +var buffer = make([][]byte, 0) + +func main() { + if token == "" { + fmt.Println("No token provided. Please run: airhorn -t <bot token>") + return + } + + // Load the sound file. + err := loadSound() + if err != nil { + fmt.Println("Error loading sound: ", err) + fmt.Println("Please copy $GOPATH/src/github.com/bwmarrin/examples/airhorn/airhorn.dca to this directory.") + return + } + + // Create a new Discord session using the provided token. + dg, err := discordgo.New(token) + if err != nil { + fmt.Println("Error creating Discord session: ", err) + return + } + + // Register ready as a callback for the ready events. + dg.AddHandler(ready) + + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) + + // Register guildCreate as a callback for the guildCreate events. + dg.AddHandler(guildCreate) + + // Open the websocket and begin listening. + err = dg.Open() + if err != nil { + fmt.Println("Error opening Discord session: ", err) + } + + fmt.Println("Airhorn is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +func ready(s *discordgo.Session, event *discordgo.Ready) { + // Set the playing status. + _ = s.UpdateStatus(0, "!airhorn") +} + +// This function will be called (due to AddHandler above) every time a new +// message is created on any channel that the autenticated bot has access to. +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + if strings.HasPrefix(m.Content, "!airhorn") { + // Find the channel that the message came from. + c, err := s.State.Channel(m.ChannelID) + if err != nil { + // Could not find channel. + return + } + + // Find the guild for that channel. + g, err := s.State.Guild(c.GuildID) + if err != nil { + // Could not find guild. + return + } + + // Look for the message sender in that guilds current voice states. + for _, vs := range g.VoiceStates { + if vs.UserID == m.Author.ID { + err = playSound(s, g.ID, vs.ChannelID) + if err != nil { + fmt.Println("Error playing sound:", err) + } + + return + } + } + } +} + +// This function will be called (due to AddHandler above) every time a new +// guild is joined. +func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { + if event.Guild.Unavailable != nil { + return + } + + for _, channel := range event.Guild.Channels { + if channel.ID == event.Guild.ID { + _, _ = s.ChannelMessageSend(channel.ID, "Airhorn is ready! Type !airhorn while in a voice channel to play a sound.") + return + } + } +} + +// loadSound attempts to load an encoded sound file from disk. +func loadSound() error { + file, err := os.Open("airhorn.dca") + + if err != nil { + fmt.Println("Error opening dca file :", err) + return err + } + + var opuslen int16 + + for { + // Read opus frame length from dca file. + err = binary.Read(file, binary.LittleEndian, &opuslen) + + // If this is the end of the file, just return. + if err == io.EOF || err == io.ErrUnexpectedEOF { + return nil + } + + if err != nil { + fmt.Println("Error reading from dca file :", err) + return err + } + + // Read encoded pcm from dca file. + InBuf := make([]byte, opuslen) + err = binary.Read(file, binary.LittleEndian, &InBuf) + + // Should not be any end of file errors + if err != nil { + fmt.Println("Error reading from dca file :", err) + return err + } + + // Append encoded pcm data to the buffer. + buffer = append(buffer, InBuf) + } +} + +// playSound plays the current buffer to the provided channel. +func playSound(s *discordgo.Session, guildID, channelID string) (err error) { + // Join the provided voice channel. + vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true) + if err != nil { + return err + } + + // Sleep for a specified amount of time before playing the sound + time.Sleep(250 * time.Millisecond) + + // Start speaking. + _ = vc.Speaking(true) + + // Send the buffer data. + for _, buff := range buffer { + vc.OpusSend <- buff + } + + // Stop speaking + _ = vc.Speaking(false) + + // Sleep for a specificed amount of time before ending. + time.Sleep(250 * time.Millisecond) + + // Disconnect from the provided voice channel. + _ = vc.Disconnect() + + return nil +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go b/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go new file mode 100644 index 00000000..bd0e3b88 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line options +var ( + Email string + Password string + Token string + AppName string + DeleteID string + ListOnly bool +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.StringVar(&Token, "t", "", "Account Token") + flag.StringVar(&DeleteID, "d", "", "Application ID to delete") + flag.BoolVar(&ListOnly, "l", false, "List Applications Only") + flag.StringVar(&AppName, "a", "", "App/Bot Name") + flag.Parse() +} + +func main() { + + var err error + // Create a new Discord session using the provided login information. + dg, err := discordgo.New(Email, Password, Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + // If -l set, only display a list of existing applications + // for the given account. + if ListOnly { + aps, err2 := dg.Applications() + if err2 != nil { + fmt.Println("error fetching applications,", err) + return + } + + for k, v := range aps { + fmt.Printf("%d : --------------------------------------\n", k) + fmt.Printf("ID: %s\n", v.ID) + fmt.Printf("Name: %s\n", v.Name) + fmt.Printf("Secret: %s\n", v.Secret) + fmt.Printf("Description: %s\n", v.Description) + } + return + } + + // if -d set, delete the given Application + if DeleteID != "" { + err = dg.ApplicationDelete(DeleteID) + if err != nil { + fmt.Println("error deleting application,", err) + } + return + } + + // Create a new application. + ap := &discordgo.Application{} + ap.Name = AppName + ap, err = dg.ApplicationCreate(ap) + if err != nil { + fmt.Println("error creating new applicaiton,", err) + return + } + + fmt.Printf("Application created successfully:\n") + fmt.Printf("ID: %s\n", ap.ID) + fmt.Printf("Name: %s\n", ap.Name) + fmt.Printf("Secret: %s\n\n", ap.Secret) + + // Create the bot account under the application we just created + bot, err := dg.ApplicationBotCreate(ap.ID) + if err != nil { + fmt.Println("error creating bot account,", err) + return + } + + fmt.Printf("Bot account created successfully.\n") + fmt.Printf("ID: %s\n", bot.ID) + fmt.Printf("Username: %s\n", bot.Username) + fmt.Printf("Token: %s\n\n", bot.Token) + fmt.Println("Please save the above posted info in a secure place.") + fmt.Println("You will need that information to login with your bot account.") + + return +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go b/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go new file mode 100644 index 00000000..adfe0b1d --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "net/http" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line parameters +var ( + Email string + Password string + Token string + Avatar string + BotID string + BotUsername string +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.StringVar(&Token, "t", "", "Account Token") + flag.StringVar(&Avatar, "f", "./avatar.jpg", "Avatar File Name") + flag.Parse() +} + +func main() { + + // Create a new Discord session using the provided login information. + // Use discordgo.New(Token) to just use a token for login. + dg, err := discordgo.New(Email, Password, Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + bot, err := dg.User("@me") + if err != nil { + fmt.Println("error fetching the bot details,", err) + return + } + + BotID = bot.ID + BotUsername = bot.Username + changeAvatar(dg) + + fmt.Println("Bot is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +// Helper function to change the avatar +func changeAvatar(s *discordgo.Session) { + img, err := ioutil.ReadFile(Avatar) + if err != nil { + fmt.Println(err) + } + + base64 := base64.StdEncoding.EncodeToString(img) + + avatar := fmt.Sprintf("data:%s;base64,%s", http.DetectContentType(img), base64) + + _, err = s.UserUpdate("", "", BotUsername, avatar, "") + if err != nil { + fmt.Println(err) + } +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go b/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go new file mode 100644 index 00000000..26170df5 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "net/http" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line parameters +var ( + Email string + Password string + Token string + URL string + BotID string + BotUsername string +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.StringVar(&Token, "t", "", "Account Token") + flag.StringVar(&URL, "l", "http://bwmarrin.github.io/discordgo/img/discordgo.png", "Link to the avatar image") + flag.Parse() +} + +func main() { + + // Create a new Discord session using the provided login information. + // Use discordgo.New(Token) to just use a token for login. + dg, err := discordgo.New(Email, Password, Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + bot, err := dg.User("@me") + if err != nil { + fmt.Println("error fetching the bot details,", err) + return + } + + BotID = bot.ID + BotUsername = bot.Username + changeAvatar(dg) + + fmt.Println("Bot is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +// Helper function to change the avatar +func changeAvatar(s *discordgo.Session) { + + resp, err := http.Get(URL) + if err != nil { + fmt.Println("Error retrieving the file, ", err) + return + } + + defer func() { + _ = resp.Body.Close() + }() + + img, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading the response, ", err) + return + } + + base64 := base64.StdEncoding.EncodeToString(img) + + avatar := fmt.Sprintf("data:%s;base64,%s", http.DetectContentType(img), base64) + + _, err = s.UserUpdate("", "", BotUsername, avatar, "") + if err != nil { + fmt.Println("Error setting the avatar, ", err) + } + +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go b/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go new file mode 100644 index 00000000..5914fc8a --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line parameters +var ( + Email string + Password string +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.Parse() +} + +func main() { + + // Create a new Discord session using the provided login information. + dg, err := discordgo.New(Email, Password) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + fmt.Printf("Your Authentication Token is:\n\n%s\n", dg.Token) +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go b/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go new file mode 100644 index 00000000..c3861ac0 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "flag" + "fmt" + "time" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line parameters +var ( + Email string + Password string + Token string +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.StringVar(&Token, "t", "", "Account Token") + flag.Parse() +} + +func main() { + + // Create a new Discord session using the provided login information. + // Use discordgo.New(Token) to just use a token for login. + dg, err := discordgo.New(Email, Password, Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) + + // Open the websocket and begin listening. + err = dg.Open() + if err != nil { + fmt.Println("error opening connection,", err) + return + } + + fmt.Println("Bot is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +// This function will be called (due to AddHandler above) every time a new +// message is created on any channel that the autenticated bot has access to. +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + + // Print message to stdout. + fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content) +} diff --git a/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go b/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go new file mode 100644 index 00000000..e6893ca1 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go @@ -0,0 +1,78 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/bwmarrin/discordgo" +) + +// Variables used for command line parameters +var ( + Email string + Password string + Token string + BotID string +) + +func init() { + + flag.StringVar(&Email, "e", "", "Account Email") + flag.StringVar(&Password, "p", "", "Account Password") + flag.StringVar(&Token, "t", "", "Account Token") + flag.Parse() +} + +func main() { + + // Create a new Discord session using the provided login information. + dg, err := discordgo.New(Email, Password, Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + // Get the account information. + u, err := dg.User("@me") + if err != nil { + fmt.Println("error obtaining account details,", err) + } + + // Store the account ID for later use. + BotID = u.ID + + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) + + // Open the websocket and begin listening. + err = dg.Open() + if err != nil { + fmt.Println("error opening connection,", err) + return + } + + fmt.Println("Bot is now running. Press CTRL-C to exit.") + // Simple way to keep program running until CTRL-C is pressed. + <-make(chan struct{}) + return +} + +// This function will be called (due to AddHandler above) every time a new +// message is created on any channel that the autenticated bot has access to. +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + + // Ignore all messages created by the bot itself + if m.Author.ID == BotID { + return + } + + // If the message is "ping" reply with "Pong!" + if m.Content == "ping" { + _, _ = s.ChannelMessageSend(m.ChannelID, "Pong!") + } + + // If the message is "pong" reply with "Ping!" + if m.Content == "pong" { + _, _ = s.ChannelMessageSend(m.ChannelID, "Ping!") + } +} diff --git a/vendor/github.com/bwmarrin/discordgo/logging.go b/vendor/github.com/bwmarrin/discordgo/logging.go new file mode 100644 index 00000000..70d78d60 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/logging.go @@ -0,0 +1,95 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains code related to discordgo package logging + +package discordgo + +import ( + "fmt" + "log" + "runtime" + "strings" +) + +const ( + + // LogError level is used for critical errors that could lead to data loss + // or panic that would not be returned to a calling function. + LogError int = iota + + // LogWarning level is used for very abnormal events and errors that are + // also returend to a calling function. + LogWarning + + // LogInformational level is used for normal non-error activity + LogInformational + + // LogDebug level is for very detailed non-error activity. This is + // very spammy and will impact performance. + LogDebug +) + +// msglog provides package wide logging consistancy for discordgo +// the format, a... portion this command follows that of fmt.Printf +// msgL : LogLevel of the message +// caller : 1 + the number of callers away from the message source +// format : Printf style message format +// a ... : comma seperated list of values to pass +func msglog(msgL, caller int, format string, a ...interface{}) { + + pc, file, line, _ := runtime.Caller(caller) + + files := strings.Split(file, "/") + file = files[len(files)-1] + + name := runtime.FuncForPC(pc).Name() + fns := strings.Split(name, ".") + name = fns[len(fns)-1] + + msg := fmt.Sprintf(format, a...) + + log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg) +} + +// helper function that wraps msglog for the Session struct +// This adds a check to insure the message is only logged +// if the session log level is equal or higher than the +// message log level +func (s *Session) log(msgL int, format string, a ...interface{}) { + + if msgL > s.LogLevel { + return + } + + msglog(msgL, 2, format, a...) +} + +// helper function that wraps msglog for the VoiceConnection struct +// This adds a check to insure the message is only logged +// if the voice connection log level is equal or higher than the +// message log level +func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) { + + if msgL > v.LogLevel { + return + } + + msglog(msgL, 2, format, a...) +} + +// printJSON is a helper function to display JSON data in a easy to read format. +/* NOT USED ATM +func printJSON(body []byte) { + var prettyJSON bytes.Buffer + error := json.Indent(&prettyJSON, body, "", "\t") + if error != nil { + log.Print("JSON parse error: ", error) + } + log.Println(string(prettyJSON.Bytes())) +} +*/ diff --git a/vendor/github.com/bwmarrin/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go new file mode 100644 index 00000000..8966c161 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/message.go @@ -0,0 +1,82 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains code related to the Message struct + +package discordgo + +import ( + "fmt" + "regexp" +) + +// A Message stores all data related to a specific Discord message. +type Message struct { + ID string `json:"id"` + ChannelID string `json:"channel_id"` + Content string `json:"content"` + Timestamp string `json:"timestamp"` + EditedTimestamp string `json:"edited_timestamp"` + MentionRoles []string `json:"mention_roles"` + Tts bool `json:"tts"` + MentionEveryone bool `json:"mention_everyone"` + Author *User `json:"author"` + Attachments []*MessageAttachment `json:"attachments"` + Embeds []*MessageEmbed `json:"embeds"` + Mentions []*User `json:"mentions"` +} + +// A MessageAttachment stores data for message attachments. +type MessageAttachment struct { + ID string `json:"id"` + URL string `json:"url"` + ProxyURL string `json:"proxy_url"` + Filename string `json:"filename"` + Width int `json:"width"` + Height int `json:"height"` + Size int `json:"size"` +} + +// An MessageEmbed stores data for message embeds. +type MessageEmbed struct { + URL string `json:"url"` + Type string `json:"type"` + Title string `json:"title"` + Description string `json:"description"` + Thumbnail *struct { + URL string `json:"url"` + ProxyURL string `json:"proxy_url"` + Width int `json:"width"` + Height int `json:"height"` + } `json:"thumbnail"` + Provider *struct { + URL string `json:"url"` + Name string `json:"name"` + } `json:"provider"` + Author *struct { + URL string `json:"url"` + Name string `json:"name"` + } `json:"author"` + Video *struct { + URL string `json:"url"` + Width int `json:"width"` + Height int `json:"height"` + } `json:"video"` +} + +// ContentWithMentionsReplaced will replace all @<id> mentions with the +// username of the mention. +func (m *Message) ContentWithMentionsReplaced() string { + if m.Mentions == nil { + return m.Content + } + content := m.Content + for _, user := range m.Mentions { + content = regexp.MustCompile(fmt.Sprintf("<@!?(%s)>", user.ID)).ReplaceAllString(content, "@"+user.Username) + } + return content +} diff --git a/vendor/github.com/bwmarrin/discordgo/oauth2.go b/vendor/github.com/bwmarrin/discordgo/oauth2.go new file mode 100644 index 00000000..de2848db --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/oauth2.go @@ -0,0 +1,120 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains functions related to Discord OAuth2 endpoints + +package discordgo + +// ------------------------------------------------------------------------------------------------ +// Code specific to Discord OAuth2 Applications +// ------------------------------------------------------------------------------------------------ + +// An Application struct stores values for a Discord OAuth2 Application +type Application struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Icon string `json:"icon,omitempty"` + Secret string `json:"secret,omitempty"` + RedirectURIs *[]string `json:"redirect_uris,omitempty"` +} + +// Application returns an Application structure of a specific Application +// appID : The ID of an Application +func (s *Session) Application(appID string) (st *Application, err error) { + + body, err := s.Request("GET", EndpointApplication(appID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// Applications returns all applications for the authenticated user +func (s *Session) Applications() (st []*Application, err error) { + + body, err := s.Request("GET", EndpointApplications, nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ApplicationCreate creates a new Application +// name : Name of Application / Bot +// uris : Redirect URIs (Not required) +func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error) { + + data := struct { + Name string `json:"name"` + Description string `json:"description"` + RedirectURIs *[]string `json:"redirect_uris,omitempty"` + }{ap.Name, ap.Description, ap.RedirectURIs} + + body, err := s.Request("POST", EndpointApplications, data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ApplicationUpdate updates an existing Application +// var : desc +func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Application, err error) { + + data := struct { + Name string `json:"name"` + Description string `json:"description"` + RedirectURIs *[]string `json:"redirect_uris,omitempty"` + }{ap.Name, ap.Description, ap.RedirectURIs} + + body, err := s.Request("PUT", EndpointApplication(appID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ApplicationDelete deletes an existing Application +// appID : The ID of an Application +func (s *Session) ApplicationDelete(appID string) (err error) { + + _, err = s.Request("DELETE", EndpointApplication(appID), nil) + if err != nil { + return + } + + return +} + +// ------------------------------------------------------------------------------------------------ +// Code specific to Discord OAuth2 Application Bots +// ------------------------------------------------------------------------------------------------ + +// ApplicationBotCreate creates an Application Bot Account +// +// appID : The ID of an Application +// +// NOTE: func name may change, if I can think up something better. +func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) { + + body, err := s.Request("POST", EndpointApplicationsBot(appID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go new file mode 100644 index 00000000..337a7f82 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/restapi.go @@ -0,0 +1,1403 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains functions for interacting with the Discord REST/JSON API +// at the lowest level. + +package discordgo + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "image" + _ "image/jpeg" // For JPEG decoding + _ "image/png" // For PNG decoding + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" +) + +// ErrJSONUnmarshal is returned for JSON Unmarshall errors. +var ErrJSONUnmarshal = errors.New("json unmarshal") + +// Request makes a (GET/POST/...) Requests to Discord REST API with JSON data. +// All the other Discord REST Calls in this file use this function. +func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) { + + var body []byte + if data != nil { + body, err = json.Marshal(data) + if err != nil { + return + } + } + + return s.request(method, urlStr, "application/json", body) +} + +// request makes a (GET/POST/...) Requests to Discord REST API. +func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) { + + // rate limit mutex for this url + // TODO: review for performance improvements + // ideally we just ignore endpoints that we've never + // received a 429 on. But this simple method works and + // is a lot less complex :) It also might even be more + // performat due to less checks and maps. + var mu *sync.Mutex + + s.rateLimit.Lock() + if s.rateLimit.url == nil { + s.rateLimit.url = make(map[string]*sync.Mutex) + } + + bu := strings.Split(urlStr, "?") + mu, _ = s.rateLimit.url[bu[0]] + if mu == nil { + mu = new(sync.Mutex) + s.rateLimit.url[urlStr] = mu + } + s.rateLimit.Unlock() + + mu.Lock() // lock this URL for ratelimiting + if s.Debug { + log.Printf("API REQUEST %8s :: %s\n", method, urlStr) + log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b)) + } + + req, err := http.NewRequest(method, urlStr, bytes.NewBuffer(b)) + if err != nil { + return + } + + // Not used on initial login.. + // TODO: Verify if a login, otherwise complain about no-token + if s.Token != "" { + req.Header.Set("authorization", s.Token) + } + + req.Header.Set("Content-Type", contentType) + // TODO: Make a configurable static variable. + req.Header.Set("User-Agent", fmt.Sprintf("DiscordBot (https://github.com/bwmarrin/discordgo, v%s)", VERSION)) + + if s.Debug { + for k, v := range req.Header { + log.Printf("API REQUEST HEADER :: [%s] = %+v\n", k, v) + } + } + + client := &http.Client{Timeout: (20 * time.Second)} + + resp, err := client.Do(req) + mu.Unlock() // unlock ratelimit mutex + if err != nil { + return + } + defer func() { + err2 := resp.Body.Close() + if err2 != nil { + log.Println("error closing resp body") + } + }() + + response, err = ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + if s.Debug { + + log.Printf("API RESPONSE STATUS :: %s\n", resp.Status) + for k, v := range resp.Header { + log.Printf("API RESPONSE HEADER :: [%s] = %+v\n", k, v) + } + log.Printf("API RESPONSE BODY :: [%s]\n\n\n", response) + } + + switch resp.StatusCode { + + case http.StatusOK: + case http.StatusCreated: + case http.StatusNoContent: + + // TODO check for 401 response, invalidate token if we get one. + + case 429: // TOO MANY REQUESTS - Rate limiting + + mu.Lock() // lock URL ratelimit mutex + + rl := TooManyRequests{} + err = json.Unmarshal(response, &rl) + if err != nil { + s.log(LogError, "rate limit unmarshal error, %s", err) + mu.Unlock() + return + } + s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter) + s.handle(RateLimit{TooManyRequests: &rl, URL: urlStr}) + + time.Sleep(rl.RetryAfter * time.Millisecond) + // we can make the above smarter + // this method can cause longer delays then required + + mu.Unlock() // we have to unlock here + response, err = s.request(method, urlStr, contentType, b) + + default: // Error condition + err = fmt.Errorf("HTTP %s, %s", resp.Status, response) + } + + return +} + +func unmarshal(data []byte, v interface{}) error { + err := json.Unmarshal(data, v) + if err != nil { + return ErrJSONUnmarshal + } + + return nil +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Sessions +// ------------------------------------------------------------------------------------------------ + +// Login asks the Discord server for an authentication token. +func (s *Session) Login(email, password string) (err error) { + + data := struct { + Email string `json:"email"` + Password string `json:"password"` + }{email, password} + + response, err := s.Request("POST", EndpointLogin, data) + if err != nil { + return + } + + temp := struct { + Token string `json:"token"` + }{} + + err = unmarshal(response, &temp) + if err != nil { + return + } + + s.Token = temp.Token + return +} + +// Register sends a Register request to Discord, and returns the authentication token +// Note that this account is temporary and should be verified for future use. +// Another option is to save the authentication token external, but this isn't recommended. +func (s *Session) Register(username string) (token string, err error) { + + data := struct { + Username string `json:"username"` + }{username} + + response, err := s.Request("POST", EndpointRegister, data) + if err != nil { + return + } + + temp := struct { + Token string `json:"token"` + }{} + + err = unmarshal(response, &temp) + if err != nil { + return + } + + token = temp.Token + return +} + +// Logout sends a logout request to Discord. +// This does not seem to actually invalidate the token. So you can still +// make API calls even after a Logout. So, it seems almost pointless to +// even use. +func (s *Session) Logout() (err error) { + + // _, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token)) + + if s.Token == "" { + return + } + + data := struct { + Token string `json:"token"` + }{s.Token} + + _, err = s.Request("POST", EndpointLogout, data) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Users +// ------------------------------------------------------------------------------------------------ + +// User returns the user details of the given userID +// userID : A user ID or "@me" which is a shortcut of current user ID +func (s *Session) User(userID string) (st *User, err error) { + + body, err := s.Request("GET", EndpointUser(userID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserAvatar returns an image.Image of a users Avatar. +// userID : A user ID or "@me" which is a shortcut of current user ID +func (s *Session) UserAvatar(userID string) (img image.Image, err error) { + u, err := s.User(userID) + if err != nil { + return + } + + body, err := s.Request("GET", EndpointUserAvatar(userID, u.Avatar), nil) + if err != nil { + return + } + + img, _, err = image.Decode(bytes.NewReader(body)) + return +} + +// UserUpdate updates a users settings. +func (s *Session) UserUpdate(email, password, username, avatar, newPassword string) (st *User, err error) { + + // NOTE: Avatar must be either the hash/id of existing Avatar or + // data:image/png;base64,BASE64_STRING_OF_NEW_AVATAR_PNG + // to set a new avatar. + // If left blank, avatar will be set to null/blank + + data := struct { + Email string `json:"email"` + Password string `json:"password"` + Username string `json:"username"` + Avatar string `json:"avatar,omitempty"` + NewPassword string `json:"new_password,omitempty"` + }{email, password, username, avatar, newPassword} + + body, err := s.Request("PATCH", EndpointUser("@me"), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserSettings returns the settings for a given user +func (s *Session) UserSettings() (st *Settings, err error) { + + body, err := s.Request("GET", EndpointUserSettings("@me"), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserChannels returns an array of Channel structures for all private +// channels. +func (s *Session) UserChannels() (st []*Channel, err error) { + + body, err := s.Request("GET", EndpointUserChannels("@me"), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserChannelCreate creates a new User (Private) Channel with another User +// recipientID : A user ID for the user to which this channel is opened with. +func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) { + + data := struct { + RecipientID string `json:"recipient_id"` + }{recipientID} + + body, err := s.Request("POST", EndpointUserChannels("@me"), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserGuilds returns an array of Guild structures for all guilds. +func (s *Session) UserGuilds() (st []*Guild, err error) { + + body, err := s.Request("GET", EndpointUserGuilds("@me"), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserGuildSettingsEdit Edits the users notification settings for a guild +// guildID : The ID of the guild to edit the settings on +// settings : The settings to update +func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSettingsEdit) (st *UserGuildSettings, err error) { + + body, err := s.Request("PATCH", EndpointUserGuildSettings("@me", guildID), settings) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// NOTE: This function is now deprecated and will be removed in the future. +// Please see the same function inside state.go +// UserChannelPermissions returns the permission of a user in a channel. +// userID : The ID of the user to calculate permissions for. +// channelID : The ID of the channel to calculate permission for. +func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { + channel, err := s.State.Channel(channelID) + if err != nil || channel == nil { + channel, err = s.Channel(channelID) + if err != nil { + return + } + } + + guild, err := s.State.Guild(channel.GuildID) + if err != nil || guild == nil { + guild, err = s.Guild(channel.GuildID) + if err != nil { + return + } + } + + if userID == guild.OwnerID { + apermissions = PermissionAll + return + } + + member, err := s.State.Member(guild.ID, userID) + if err != nil || member == nil { + member, err = s.GuildMember(guild.ID, userID) + if err != nil { + return + } + } + + for _, role := range guild.Roles { + for _, roleID := range member.Roles { + if role.ID == roleID { + apermissions |= role.Permissions + break + } + } + } + + if apermissions&PermissionManageRoles > 0 { + apermissions |= PermissionAll + } + + // Member overwrites can override role overrides, so do two passes + for _, overwrite := range channel.PermissionOverwrites { + for _, roleID := range member.Roles { + if overwrite.Type == "role" && roleID == overwrite.ID { + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + } + + for _, overwrite := range channel.PermissionOverwrites { + if overwrite.Type == "member" && overwrite.ID == userID { + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + + if apermissions&PermissionManageRoles > 0 { + apermissions |= PermissionAllChannel + } + + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Guilds +// ------------------------------------------------------------------------------------------------ + +// Guild returns a Guild structure of a specific Guild. +// guildID : The ID of a Guild +func (s *Session) Guild(guildID string) (st *Guild, err error) { + if s.StateEnabled { + // Attempt to grab the guild from State first. + st, err = s.State.Guild(guildID) + if err == nil { + return + } + } + + body, err := s.Request("GET", EndpointGuild(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildCreate creates a new Guild +// name : A name for the Guild (2-100 characters) +func (s *Session) GuildCreate(name string) (st *Guild, err error) { + + data := struct { + Name string `json:"name"` + }{name} + + body, err := s.Request("POST", EndpointGuilds, data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildEdit edits a new Guild +// guildID : The ID of a Guild +// g : A GuildParams struct with the values Name, Region and VerificationLevel defined. +func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) { + + // Bounds checking for VerificationLevel, interval: [0, 3] + if g.VerificationLevel != nil { + val := *g.VerificationLevel + if val < 0 || val > 3 { + err = errors.New("VerificationLevel out of bounds, should be between 0 and 3") + return + } + } + + //Bounds checking for regions + if g.Region != "" { + isValid := false + regions, _ := s.VoiceRegions() + for _, r := range regions { + if g.Region == r.ID { + isValid = true + } + } + if !isValid { + var valid []string + for _, r := range regions { + valid = append(valid, r.ID) + } + err = fmt.Errorf("Region not a valid region (%q)", valid) + return + } + } + + data := struct { + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` + VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` + }{g.Name, g.Region, g.VerificationLevel} + + body, err := s.Request("PATCH", EndpointGuild(guildID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildDelete deletes a Guild. +// guildID : The ID of a Guild +func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { + + body, err := s.Request("DELETE", EndpointGuild(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildLeave leaves a Guild. +// guildID : The ID of a Guild +func (s *Session) GuildLeave(guildID string) (err error) { + + _, err = s.Request("DELETE", EndpointUserGuild("@me", guildID), nil) + return +} + +// GuildBans returns an array of User structures for all bans of a +// given guild. +// guildID : The ID of a Guild. +func (s *Session) GuildBans(guildID string) (st []*User, err error) { + + body, err := s.Request("GET", EndpointGuildBans(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildBanCreate bans the given user from the given guild. +// guildID : The ID of a Guild. +// userID : The ID of a User +// days : The number of days of previous comments to delete. +func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { + + uri := EndpointGuildBan(guildID, userID) + + if days > 0 { + uri = fmt.Sprintf("%s?delete-message-days=%d", uri, days) + } + + _, err = s.Request("PUT", uri, nil) + return +} + +// GuildBanDelete removes the given user from the guild bans +// guildID : The ID of a Guild. +// userID : The ID of a User +func (s *Session) GuildBanDelete(guildID, userID string) (err error) { + + _, err = s.Request("DELETE", EndpointGuildBan(guildID, userID), nil) + return +} + +// GuildMembers returns a list of members for a guild. +// guildID : The ID of a Guild. +// offset : A number of members to skip +// limit : max number of members to return (max 1000) +func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, err error) { + + uri := EndpointGuildMembers(guildID) + + v := url.Values{} + + if offset > 0 { + v.Set("offset", strconv.Itoa(offset)) + } + + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + + if len(v) > 0 { + uri = fmt.Sprintf("%s?%s", uri, v.Encode()) + } + + body, err := s.Request("GET", uri, nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildMember returns a member of a guild. +// guildID : The ID of a Guild. +// userID : The ID of a User +func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { + + body, err := s.Request("GET", EndpointGuildMember(guildID, userID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildMemberDelete removes the given user from the given guild. +// guildID : The ID of a Guild. +// userID : The ID of a User +func (s *Session) GuildMemberDelete(guildID, userID string) (err error) { + + _, err = s.Request("DELETE", EndpointGuildMember(guildID, userID), nil) + return +} + +// GuildMemberEdit edits the roles of a member. +// guildID : The ID of a Guild. +// userID : The ID of a User. +// roles : A list of role ID's to set on the member. +func (s *Session) GuildMemberEdit(guildID, userID string, roles []string) (err error) { + + data := struct { + Roles []string `json:"roles"` + }{roles} + + _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + if err != nil { + return + } + + return +} + +// GuildMemberMove moves a guild member from one voice channel to another/none +// guildID : The ID of a Guild. +// userID : The ID of a User. +// channelID : The ID of a channel to move user to, or null? +// NOTE : I am not entirely set on the name of this function and it may change +// prior to the final 1.0.0 release of Discordgo +func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error) { + + data := struct { + ChannelID string `json:"channel_id"` + }{channelID} + + _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + if err != nil { + return + } + + return +} + +// GuildMemberNickname updates the nickname of a guild member +// guildID : The ID of a guild +// userID : The ID of a user +func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err error) { + + data := struct { + Nick string `json:"nick"` + }{nickname} + + _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data) + return +} + +// GuildChannels returns an array of Channel structures for all channels of a +// given guild. +// guildID : The ID of a Guild. +func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) { + + body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildChannelCreate creates a new channel in the given guild +// guildID : The ID of a Guild. +// name : Name of the channel (2-100 chars length) +// ctype : Tpye of the channel (voice or text) +func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, err error) { + + data := struct { + Name string `json:"name"` + Type string `json:"type"` + }{name, ctype} + + body, err := s.Request("POST", EndpointGuildChannels(guildID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildChannelsReorder updates the order of channels in a guild +// guildID : The ID of a Guild. +// channels : Updated channels. +func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err error) { + + _, err = s.Request("PATCH", EndpointGuildChannels(guildID), channels) + return +} + +// GuildInvites returns an array of Invite structures for the given guild +// guildID : The ID of a Guild. +func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { + body, err := s.Request("GET", EndpointGuildInvites(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildRoles returns all roles for a given guild. +// guildID : The ID of a Guild. +func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { + + body, err := s.Request("GET", EndpointGuildRoles(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return // TODO return pointer +} + +// GuildRoleCreate returns a new Guild Role. +// guildID: The ID of a Guild. +func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) { + + body, err := s.Request("POST", EndpointGuildRoles(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildRoleEdit updates an existing Guild Role with new values +// guildID : The ID of a Guild. +// roleID : The ID of a Role. +// name : The name of the Role. +// color : The color of the role (decimal, not hex). +// hoist : Whether to display the role's users separately. +// perm : The permissions for the role. +func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int) (st *Role, err error) { + + // Prevent sending a color int that is too big. + if color > 0xFFFFFF { + err = fmt.Errorf("color value cannot be larger than 0xFFFFFF") + } + + data := struct { + Name string `json:"name"` // The color the role should have (as a decimal, not hex) + Color int `json:"color"` // Whether to display the role's users separately + Hoist bool `json:"hoist"` // The role's name (overwrites existing) + Permissions int `json:"permissions"` // The overall permissions number of the role (overwrites existing) + }{name, color, hoist, perm} + + body, err := s.Request("PATCH", EndpointGuildRole(guildID, roleID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildRoleReorder reoders guild roles +// guildID : The ID of a Guild. +// roles : A list of ordered roles. +func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) { + + body, err := s.Request("PATCH", EndpointGuildRoles(guildID), roles) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildRoleDelete deletes an existing role. +// guildID : The ID of a Guild. +// roleID : The ID of a Role. +func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { + + _, err = s.Request("DELETE", EndpointGuildRole(guildID, roleID), nil) + + return +} + +// GuildIntegrations returns an array of Integrations for a guild. +// guildID : The ID of a Guild. +func (s *Session) GuildIntegrations(guildID string) (st []*GuildIntegration, err error) { + + body, err := s.Request("GET", EndpointGuildIntegrations(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + +// GuildIntegrationCreate creates a Guild Integration. +// guildID : The ID of a Guild. +// integrationType : The Integration type. +// integrationID : The ID of an integration. +func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string) (err error) { + + data := struct { + Type string `json:"type"` + ID string `json:"id"` + }{integrationType, integrationID} + + _, err = s.Request("POST", EndpointGuildIntegrations(guildID), data) + return +} + +// GuildIntegrationEdit edits a Guild Integration. +// guildID : The ID of a Guild. +// integrationType : The Integration type. +// integrationID : The ID of an integration. +// expireBehavior : The behavior when an integration subscription lapses (see the integration object documentation). +// expireGracePeriod : Period (in seconds) where the integration will ignore lapsed subscriptions. +// enableEmoticons : Whether emoticons should be synced for this integration (twitch only currently). +func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool) (err error) { + + data := struct { + ExpireBehavior int `json:"expire_behavior"` + ExpireGracePeriod int `json:"expire_grace_period"` + EnableEmoticons bool `json:"enable_emoticons"` + }{expireBehavior, expireGracePeriod, enableEmoticons} + + _, err = s.Request("PATCH", EndpointGuildIntegration(guildID, integrationID), data) + return +} + +// GuildIntegrationDelete removes the given integration from the Guild. +// guildID : The ID of a Guild. +// integrationID : The ID of an integration. +func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) { + + _, err = s.Request("DELETE", EndpointGuildIntegration(guildID, integrationID), nil) + return +} + +// GuildIntegrationSync syncs an integration. +// guildID : The ID of a Guild. +// integrationID : The ID of an integration. +func (s *Session) GuildIntegrationSync(guildID, integrationID string) (err error) { + + _, err = s.Request("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil) + return +} + +// GuildIcon returns an image.Image of a guild icon. +// guildID : The ID of a Guild. +func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { + g, err := s.Guild(guildID) + if err != nil { + return + } + + if g.Icon == "" { + err = errors.New("Guild does not have an icon set.") + return + } + + body, err := s.Request("GET", EndpointGuildIcon(guildID, g.Icon), nil) + if err != nil { + return + } + + img, _, err = image.Decode(bytes.NewReader(body)) + return +} + +// GuildSplash returns an image.Image of a guild splash image. +// guildID : The ID of a Guild. +func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { + g, err := s.Guild(guildID) + if err != nil { + return + } + + if g.Splash == "" { + err = errors.New("Guild does not have a splash set.") + return + } + + body, err := s.Request("GET", EndpointGuildSplash(guildID, g.Splash), nil) + if err != nil { + return + } + + img, _, err = image.Decode(bytes.NewReader(body)) + return +} + +// GuildEmbed returns the embed for a Guild. +// guildID : The ID of a Guild. +func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) { + + body, err := s.Request("GET", EndpointGuildEmbed(guildID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildEmbedEdit returns the embed for a Guild. +// guildID : The ID of a Guild. +func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) (err error) { + + data := GuildEmbed{enabled, channelID} + + _, err = s.Request("PATCH", EndpointGuildEmbed(guildID), data) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Channels +// ------------------------------------------------------------------------------------------------ + +// Channel returns a Channel strucutre of a specific Channel. +// channelID : The ID of the Channel you want returned. +func (s *Session) Channel(channelID string) (st *Channel, err error) { + body, err := s.Request("GET", EndpointChannel(channelID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelEdit edits the given channel +// channelID : The ID of a Channel +// name : The new name to assign the channel. +func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) { + + data := struct { + Name string `json:"name"` + }{name} + + body, err := s.Request("PATCH", EndpointChannel(channelID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelDelete deletes the given channel +// channelID : The ID of a Channel +func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { + + body, err := s.Request("DELETE", EndpointChannel(channelID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelTyping broadcasts to all members that authenticated user is typing in +// the given channel. +// channelID : The ID of a Channel +func (s *Session) ChannelTyping(channelID string) (err error) { + + _, err = s.Request("POST", EndpointChannelTyping(channelID), nil) + return +} + +// ChannelMessages returns an array of Message structures for messages within +// a given channel. +// channelID : The ID of a Channel. +// limit : The number messages that can be returned. (max 100) +// beforeID : If provided all messages returned will be before given ID. +// afterID : If provided all messages returned will be after given ID. +func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID string) (st []*Message, err error) { + + uri := EndpointChannelMessages(channelID) + + v := url.Values{} + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + if afterID != "" { + v.Set("after", afterID) + } + if beforeID != "" { + v.Set("before", beforeID) + } + if len(v) > 0 { + uri = fmt.Sprintf("%s?%s", uri, v.Encode()) + } + + body, err := s.Request("GET", uri, nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelMessage gets a single message by ID from a given channel. +// channeld : The ID of a Channel +// messageID : the ID of a Message +func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) { + + response, err := s.Request("GET", EndpointChannelMessage(channelID, messageID), nil) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + +// ChannelMessageAck acknowledges and marks the given message as read +// channeld : The ID of a Channel +// messageID : the ID of a Message +func (s *Session) ChannelMessageAck(channelID, messageID string) (err error) { + + _, err = s.request("POST", EndpointChannelMessageAck(channelID, messageID), "", nil) + return +} + +// channelMessageSend sends a message to the given channel. +// channelID : The ID of a Channel. +// content : The message to send. +// tts : Whether to send the message with TTS. +func (s *Session) channelMessageSend(channelID, content string, tts bool) (st *Message, err error) { + + // TODO: nonce string ? + data := struct { + Content string `json:"content"` + TTS bool `json:"tts"` + }{content, tts} + + // Send the message to the given channel + response, err := s.Request("POST", EndpointChannelMessages(channelID), data) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + +// ChannelMessageSend sends a message to the given channel. +// channelID : The ID of a Channel. +// content : The message to send. +func (s *Session) ChannelMessageSend(channelID string, content string) (st *Message, err error) { + + return s.channelMessageSend(channelID, content, false) +} + +// ChannelMessageSendTTS sends a message to the given channel with Text to Speech. +// channelID : The ID of a Channel. +// content : The message to send. +func (s *Session) ChannelMessageSendTTS(channelID string, content string) (st *Message, err error) { + + return s.channelMessageSend(channelID, content, true) +} + +// ChannelMessageEdit edits an existing message, replacing it entirely with +// the given content. +// channeld : The ID of a Channel +// messageID : the ID of a Message +func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st *Message, err error) { + + data := struct { + Content string `json:"content"` + }{content} + + response, err := s.Request("PATCH", EndpointChannelMessage(channelID, messageID), data) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + +// ChannelMessageDelete deletes a message from the Channel. +func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) { + + _, err = s.Request("DELETE", EndpointChannelMessage(channelID, messageID), nil) + return +} + +// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs. +// If only one messageID is in the slice call channelMessageDelete funciton. +// If the slice is empty do nothing. +// channelID : The ID of the channel for the messages to delete. +// messages : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages. +func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) (err error) { + + if len(messages) == 0 { + return + } + + if len(messages) == 1 { + err = s.ChannelMessageDelete(channelID, messages[0]) + return + } + + if len(messages) > 100 { + messages = messages[:100] + } + + data := struct { + Messages []string `json:"messages"` + }{messages} + + _, err = s.Request("POST", EndpointChannelMessagesBulkDelete(channelID), data) + return +} + +// ChannelMessagePin pins a message within a given channel. +// channelID: The ID of a channel. +// messageID: The ID of a message. +func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) { + + _, err = s.Request("PUT", EndpointChannelMessagePin(channelID, messageID), nil) + return +} + +// ChannelMessageUnpin unpins a message within a given channel. +// channelID: The ID of a channel. +// messageID: The ID of a message. +func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) { + + _, err = s.Request("DELETE", EndpointChannelMessagePin(channelID, messageID), nil) + return +} + +// ChannelMessagesPinned returns an array of Message structures for pinned messages +// within a given channel +// channelID : The ID of a Channel. +func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) { + + body, err := s.Request("GET", EndpointChannelMessagesPins(channelID), nil) + + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelFileSend sends a file to the given channel. +// channelID : The ID of a Channel. +// io.Reader : A reader for the file contents. +func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (st *Message, err error) { + + body := &bytes.Buffer{} + bodywriter := multipart.NewWriter(body) + + writer, err := bodywriter.CreateFormFile("file", name) + if err != nil { + return nil, err + } + + _, err = io.Copy(writer, r) + if err != nil { + return + } + + err = bodywriter.Close() + if err != nil { + return + } + + response, err := s.request("POST", EndpointChannelMessages(channelID), bodywriter.FormDataContentType(), body.Bytes()) + if err != nil { + return + } + + err = unmarshal(response, &st) + return +} + +// ChannelInvites returns an array of Invite structures for the given channel +// channelID : The ID of a Channel +func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) { + + body, err := s.Request("GET", EndpointChannelInvites(channelID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelInviteCreate creates a new invite for the given channel. +// channelID : The ID of a Channel +// i : An Invite struct with the values MaxAge, MaxUses, Temporary, +// and XkcdPass defined. +func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) { + + data := struct { + MaxAge int `json:"max_age"` + MaxUses int `json:"max_uses"` + Temporary bool `json:"temporary"` + XKCDPass string `json:"xkcdpass"` + }{i.MaxAge, i.MaxUses, i.Temporary, i.XkcdPass} + + body, err := s.Request("POST", EndpointChannelInvites(channelID), data) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ChannelPermissionSet creates a Permission Override for the given channel. +// NOTE: This func name may changed. Using Set instead of Create because +// you can both create a new override or update an override with this function. +func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, allow, deny int) (err error) { + + data := struct { + ID string `json:"id"` + Type string `json:"type"` + Allow int `json:"allow"` + Deny int `json:"deny"` + }{targetID, targetType, allow, deny} + + _, err = s.Request("PUT", EndpointChannelPermission(channelID, targetID), data) + return +} + +// ChannelPermissionDelete deletes a specific permission override for the given channel. +// NOTE: Name of this func may change. +func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) { + + _, err = s.Request("DELETE", EndpointChannelPermission(channelID, targetID), nil) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Invites +// ------------------------------------------------------------------------------------------------ + +// Invite returns an Invite structure of the given invite +// inviteID : The invite code (or maybe xkcdpass?) +func (s *Session) Invite(inviteID string) (st *Invite, err error) { + + body, err := s.Request("GET", EndpointInvite(inviteID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// InviteDelete deletes an existing invite +// inviteID : the code (or maybe xkcdpass?) of an invite +func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { + + body, err := s.Request("DELETE", EndpointInvite(inviteID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// InviteAccept accepts an Invite to a Guild or Channel +// inviteID : The invite code (or maybe xkcdpass?) +func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { + + body, err := s.Request("POST", EndpointInvite(inviteID), nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Voice +// ------------------------------------------------------------------------------------------------ + +// VoiceRegions returns the voice server regions +func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { + + body, err := s.Request("GET", EndpointVoiceRegions, nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// VoiceICE returns the voice server ICE information +func (s *Session) VoiceICE() (st *VoiceICE, err error) { + + body, err := s.Request("GET", EndpointVoiceIce, nil) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to Discord Websockets +// ------------------------------------------------------------------------------------------------ + +// Gateway returns the a websocket Gateway address +func (s *Session) Gateway() (gateway string, err error) { + + response, err := s.Request("GET", EndpointGateway, nil) + if err != nil { + return + } + + temp := struct { + URL string `json:"url"` + }{} + + err = unmarshal(response, &temp) + if err != nil { + return + } + + gateway = temp.URL + return +} diff --git a/vendor/github.com/bwmarrin/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go new file mode 100644 index 00000000..e9eb4d67 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/state.go @@ -0,0 +1,746 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains code related to state tracking. If enabled, state +// tracking will capture the initial READY packet and many other websocket +// events and maintain an in-memory state of of guilds, channels, users, and +// so forth. This information can be accessed through the Session.State struct. + +package discordgo + +import ( + "errors" + "sync" +) + +// ErrNilState is returned when the state is nil. +var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.") + +// A State contains the current known state. +// As discord sends this in a READY blob, it seems reasonable to simply +// use that struct as the data store. +type State struct { + sync.RWMutex + Ready + + MaxMessageCount int + TrackChannels bool + TrackEmojis bool + TrackMembers bool + TrackRoles bool + TrackVoice bool + + guildMap map[string]*Guild + channelMap map[string]*Channel +} + +// NewState creates an empty state. +func NewState() *State { + return &State{ + Ready: Ready{ + PrivateChannels: []*Channel{}, + Guilds: []*Guild{}, + }, + TrackChannels: true, + TrackEmojis: true, + TrackMembers: true, + TrackRoles: true, + TrackVoice: true, + guildMap: make(map[string]*Guild), + channelMap: make(map[string]*Channel), + } +} + +// OnReady takes a Ready event and updates all internal state. +func (s *State) OnReady(r *Ready) error { + if s == nil { + return ErrNilState + } + + s.Lock() + defer s.Unlock() + + s.Ready = *r + + for _, g := range s.Guilds { + s.guildMap[g.ID] = g + + for _, c := range g.Channels { + c.GuildID = g.ID + s.channelMap[c.ID] = c + } + } + + for _, c := range s.PrivateChannels { + s.channelMap[c.ID] = c + } + + return nil +} + +// GuildAdd adds a guild to the current world state, or +// updates it if it already exists. +func (s *State) GuildAdd(guild *Guild) error { + if s == nil { + return ErrNilState + } + + s.Lock() + defer s.Unlock() + + // Update the channels to point to the right guild, adding them to the channelMap as we go + for _, c := range guild.Channels { + c.GuildID = guild.ID + s.channelMap[c.ID] = c + } + + // If the guild exists, replace it. + if g, ok := s.guildMap[guild.ID]; ok { + // If this guild already exists with data, don't stomp on props. + if g.Unavailable != nil && !*g.Unavailable { + guild.Members = g.Members + guild.Presences = g.Presences + guild.Channels = g.Channels + guild.VoiceStates = g.VoiceStates + } + + *g = *guild + return nil + } + + s.Guilds = append(s.Guilds, guild) + s.guildMap[guild.ID] = guild + + return nil +} + +// GuildRemove removes a guild from current world state. +func (s *State) GuildRemove(guild *Guild) error { + if s == nil { + return ErrNilState + } + + _, err := s.Guild(guild.ID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + delete(s.guildMap, guild.ID) + + for i, g := range s.Guilds { + if g.ID == guild.ID { + s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...) + return nil + } + } + + return nil +} + +// Guild gets a guild by ID. +// Useful for querying if @me is in a guild: +// _, err := discordgo.Session.State.Guild(guildID) +// isInGuild := err == nil +func (s *State) Guild(guildID string) (*Guild, error) { + if s == nil { + return nil, ErrNilState + } + + s.RLock() + defer s.RUnlock() + + if g, ok := s.guildMap[guildID]; ok { + return g, nil + } + + return nil, errors.New("Guild not found.") +} + +// TODO: Consider moving Guild state update methods onto *Guild. + +// MemberAdd adds a member to the current world state, or +// updates it if it already exists. +func (s *State) MemberAdd(member *Member) error { + if s == nil { + return ErrNilState + } + + guild, err := s.Guild(member.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, m := range guild.Members { + if m.User.ID == member.User.ID { + guild.Members[i] = member + return nil + } + } + + guild.Members = append(guild.Members, member) + return nil +} + +// MemberRemove removes a member from current world state. +func (s *State) MemberRemove(member *Member) error { + if s == nil { + return ErrNilState + } + + guild, err := s.Guild(member.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, m := range guild.Members { + if m.User.ID == member.User.ID { + guild.Members = append(guild.Members[:i], guild.Members[i+1:]...) + return nil + } + } + + return errors.New("Member not found.") +} + +// Member gets a member by ID from a guild. +func (s *State) Member(guildID, userID string) (*Member, error) { + if s == nil { + return nil, ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return nil, err + } + + s.RLock() + defer s.RUnlock() + + for _, m := range guild.Members { + if m.User.ID == userID { + return m, nil + } + } + + return nil, errors.New("Member not found.") +} + +// RoleAdd adds a role to the current world state, or +// updates it if it already exists. +func (s *State) RoleAdd(guildID string, role *Role) error { + if s == nil { + return ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, r := range guild.Roles { + if r.ID == role.ID { + guild.Roles[i] = role + return nil + } + } + + guild.Roles = append(guild.Roles, role) + return nil +} + +// RoleRemove removes a role from current world state by ID. +func (s *State) RoleRemove(guildID, roleID string) error { + if s == nil { + return ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, r := range guild.Roles { + if r.ID == roleID { + guild.Roles = append(guild.Roles[:i], guild.Roles[i+1:]...) + return nil + } + } + + return errors.New("Role not found.") +} + +// Role gets a role by ID from a guild. +func (s *State) Role(guildID, roleID string) (*Role, error) { + if s == nil { + return nil, ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return nil, err + } + + s.RLock() + defer s.RUnlock() + + for _, r := range guild.Roles { + if r.ID == roleID { + return r, nil + } + } + + return nil, errors.New("Role not found.") +} + +// ChannelAdd adds a guild to the current world state, or +// updates it if it already exists. +// Channels may exist either as PrivateChannels or inside +// a guild. +func (s *State) ChannelAdd(channel *Channel) error { + if s == nil { + return ErrNilState + } + + s.Lock() + defer s.Unlock() + + // If the channel exists, replace it + if c, ok := s.channelMap[channel.ID]; ok { + channel.Messages = c.Messages + channel.PermissionOverwrites = c.PermissionOverwrites + + *c = *channel + return nil + } + + if channel.IsPrivate { + s.PrivateChannels = append(s.PrivateChannels, channel) + } else { + guild, ok := s.guildMap[channel.GuildID] + if !ok { + return errors.New("Guild for channel not found.") + } + + guild.Channels = append(guild.Channels, channel) + } + + s.channelMap[channel.ID] = channel + + return nil +} + +// ChannelRemove removes a channel from current world state. +func (s *State) ChannelRemove(channel *Channel) error { + if s == nil { + return ErrNilState + } + + _, err := s.Channel(channel.ID) + if err != nil { + return err + } + + if channel.IsPrivate { + s.Lock() + defer s.Unlock() + + for i, c := range s.PrivateChannels { + if c.ID == channel.ID { + s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...) + break + } + } + } else { + guild, err := s.Guild(channel.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, c := range guild.Channels { + if c.ID == channel.ID { + guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...) + break + } + } + } + + delete(s.channelMap, channel.ID) + + return nil +} + +// GuildChannel gets a channel by ID from a guild. +// This method is Deprecated, use Channel(channelID) +func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) { + return s.Channel(channelID) +} + +// PrivateChannel gets a private channel by ID. +// This method is Deprecated, use Channel(channelID) +func (s *State) PrivateChannel(channelID string) (*Channel, error) { + return s.Channel(channelID) +} + +// Channel gets a channel by ID, it will look in all guilds an private channels. +func (s *State) Channel(channelID string) (*Channel, error) { + if s == nil { + return nil, ErrNilState + } + + s.RLock() + defer s.RUnlock() + + if c, ok := s.channelMap[channelID]; ok { + return c, nil + } + + return nil, errors.New("Channel not found.") +} + +// Emoji returns an emoji for a guild and emoji id. +func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) { + if s == nil { + return nil, ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return nil, err + } + + s.RLock() + defer s.RUnlock() + + for _, e := range guild.Emojis { + if e.ID == emojiID { + return e, nil + } + } + + return nil, errors.New("Emoji not found.") +} + +// EmojiAdd adds an emoji to the current world state. +func (s *State) EmojiAdd(guildID string, emoji *Emoji) error { + if s == nil { + return ErrNilState + } + + guild, err := s.Guild(guildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, e := range guild.Emojis { + if e.ID == emoji.ID { + guild.Emojis[i] = emoji + return nil + } + } + + guild.Emojis = append(guild.Emojis, emoji) + return nil +} + +// EmojisAdd adds multiple emojis to the world state. +func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error { + for _, e := range emojis { + if err := s.EmojiAdd(guildID, e); err != nil { + return err + } + } + return nil +} + +// MessageAdd adds a message to the current world state, or updates it if it exists. +// If the channel cannot be found, the message is discarded. +// Messages are kept in state up to s.MaxMessageCount +func (s *State) MessageAdd(message *Message) error { + if s == nil { + return ErrNilState + } + + c, err := s.Channel(message.ChannelID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + // If the message exists, merge in the new message contents. + for _, m := range c.Messages { + if m.ID == message.ID { + if message.Content != "" { + m.Content = message.Content + } + if message.EditedTimestamp != "" { + m.EditedTimestamp = message.EditedTimestamp + } + if message.Mentions != nil { + m.Mentions = message.Mentions + } + if message.Embeds != nil { + m.Embeds = message.Embeds + } + if message.Attachments != nil { + m.Attachments = message.Attachments + } + + return nil + } + } + + c.Messages = append(c.Messages, message) + + if len(c.Messages) > s.MaxMessageCount { + c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:] + } + return nil +} + +// MessageRemove removes a message from the world state. +func (s *State) MessageRemove(message *Message) error { + if s == nil { + return ErrNilState + } + + c, err := s.Channel(message.ChannelID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + for i, m := range c.Messages { + if m.ID == message.ID { + c.Messages = append(c.Messages[:i], c.Messages[i+1:]...) + return nil + } + } + + return errors.New("Message not found.") +} + +func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error { + guild, err := s.Guild(update.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + // Handle Leaving Channel + if update.ChannelID == "" { + for i, state := range guild.VoiceStates { + if state.UserID == update.UserID { + guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...) + return nil + } + } + } else { + for i, state := range guild.VoiceStates { + if state.UserID == update.UserID { + guild.VoiceStates[i] = update.VoiceState + return nil + } + } + + guild.VoiceStates = append(guild.VoiceStates, update.VoiceState) + } + + return nil +} + +// Message gets a message by channel and message ID. +func (s *State) Message(channelID, messageID string) (*Message, error) { + if s == nil { + return nil, ErrNilState + } + + c, err := s.Channel(channelID) + if err != nil { + return nil, err + } + + s.RLock() + defer s.RUnlock() + + for _, m := range c.Messages { + if m.ID == messageID { + return m, nil + } + } + + return nil, errors.New("Message not found.") +} + +// onInterface handles all events related to states. +func (s *State) onInterface(se *Session, i interface{}) (err error) { + if s == nil { + return ErrNilState + } + if !se.StateEnabled { + return nil + } + + switch t := i.(type) { + case *Ready: + err = s.OnReady(t) + case *GuildCreate: + err = s.GuildAdd(t.Guild) + case *GuildUpdate: + err = s.GuildAdd(t.Guild) + case *GuildDelete: + err = s.GuildRemove(t.Guild) + case *GuildMemberAdd: + if s.TrackMembers { + err = s.MemberAdd(t.Member) + } + case *GuildMemberUpdate: + if s.TrackMembers { + err = s.MemberAdd(t.Member) + } + case *GuildMemberRemove: + if s.TrackMembers { + err = s.MemberRemove(t.Member) + } + case *GuildRoleCreate: + if s.TrackRoles { + err = s.RoleAdd(t.GuildID, t.Role) + } + case *GuildRoleUpdate: + if s.TrackRoles { + err = s.RoleAdd(t.GuildID, t.Role) + } + case *GuildRoleDelete: + if s.TrackRoles { + err = s.RoleRemove(t.GuildID, t.RoleID) + } + case *GuildEmojisUpdate: + if s.TrackEmojis { + err = s.EmojisAdd(t.GuildID, t.Emojis) + } + case *ChannelCreate: + if s.TrackChannels { + err = s.ChannelAdd(t.Channel) + } + case *ChannelUpdate: + if s.TrackChannels { + err = s.ChannelAdd(t.Channel) + } + case *ChannelDelete: + if s.TrackChannels { + err = s.ChannelRemove(t.Channel) + } + case *MessageCreate: + if s.MaxMessageCount != 0 { + err = s.MessageAdd(t.Message) + } + case *MessageUpdate: + if s.MaxMessageCount != 0 { + err = s.MessageAdd(t.Message) + } + case *MessageDelete: + if s.MaxMessageCount != 0 { + err = s.MessageRemove(t.Message) + } + case *VoiceStateUpdate: + if s.TrackVoice { + err = s.voiceStateUpdate(t) + } + } + + return +} + +// UserChannelPermissions returns the permission of a user in a channel. +// userID : The ID of the user to calculate permissions for. +// channelID : The ID of the channel to calculate permission for. +func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { + + channel, err := s.Channel(channelID) + if err != nil { + return + } + + guild, err := s.Guild(channel.GuildID) + if err != nil { + return + } + + if userID == guild.OwnerID { + apermissions = PermissionAll + return + } + + member, err := s.Member(guild.ID, userID) + if err != nil { + return + } + + for _, role := range guild.Roles { + for _, roleID := range member.Roles { + if role.ID == roleID { + apermissions |= role.Permissions + break + } + } + } + + if apermissions&PermissionManageRoles > 0 { + apermissions |= PermissionAll + } + + // Member overwrites can override role overrides, so do two passes + for _, overwrite := range channel.PermissionOverwrites { + for _, roleID := range member.Roles { + if overwrite.Type == "role" && roleID == overwrite.ID { + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + } + + for _, overwrite := range channel.PermissionOverwrites { + if overwrite.Type == "member" && overwrite.ID == userID { + apermissions &= ^overwrite.Deny + apermissions |= overwrite.Allow + break + } + } + + if apermissions&PermissionManageRoles > 0 { + apermissions |= PermissionAllChannel + } + + return +} diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go new file mode 100644 index 00000000..19a291f8 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/structs.go @@ -0,0 +1,521 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains all structures for the discordgo package. These +// may be moved about later into separate files but I find it easier to have +// them all located together. + +package discordgo + +import ( + "encoding/json" + "reflect" + "sync" + "time" + + "github.com/gorilla/websocket" +) + +// A Session represents a connection to the Discord API. +type Session struct { + sync.RWMutex + + // General configurable settings. + + // Authentication token for this session + Token string + + // Debug for printing JSON request/responses + Debug bool // Deprecated, will be removed. + LogLevel int + + // Should the session reconnect the websocket on errors. + ShouldReconnectOnError bool + + // Should the session request compressed websocket data. + Compress bool + + // Sharding + ShardID int + ShardCount int + + // Should state tracking be enabled. + // State tracking is the best way for getting the the users + // active guilds and the members of the guilds. + StateEnabled bool + + // Exposed but should not be modified by User. + + // Whether the Data Websocket is ready + DataReady bool // NOTE: Maye be deprecated soon + + // Status stores the currect status of the websocket connection + // this is being tested, may stay, may go away. + status int32 + + // Whether the Voice Websocket is ready + VoiceReady bool // NOTE: Deprecated. + + // Whether the UDP Connection is ready + UDPReady bool // NOTE: Deprecated + + // Stores a mapping of guild id's to VoiceConnections + VoiceConnections map[string]*VoiceConnection + + // Managed state object, updated internally with events when + // StateEnabled is true. + State *State + + handlersMu sync.RWMutex + // This is a mapping of event struct to a reflected value + // for event handlers. + // We store the reflected value instead of the function + // reference as it is more performant, instead of re-reflecting + // the function each event. + handlers map[interface{}][]reflect.Value + + // The websocket connection. + wsConn *websocket.Conn + + // When nil, the session is not listening. + listening chan interface{} + + // used to deal with rate limits + // may switch to slices later + // TODO: performance test map vs slices + rateLimit rateLimitMutex + + // sequence tracks the current gateway api websocket sequence number + sequence int + + // stores sessions current Discord Gateway + gateway string + + // stores session ID of current Gateway connection + sessionID string + + // used to make sure gateway websocket writes do not happen concurrently + wsMutex sync.Mutex +} + +type rateLimitMutex struct { + sync.Mutex + url map[string]*sync.Mutex + // bucket map[string]*sync.Mutex // TODO :) +} + +// A Resumed struct holds the data received in a RESUMED event +type Resumed struct { + HeartbeatInterval time.Duration `json:"heartbeat_interval"` + Trace []string `json:"_trace"` +} + +// A VoiceRegion stores data for a specific voice region server. +type VoiceRegion struct { + ID string `json:"id"` + Name string `json:"name"` + Hostname string `json:"sample_hostname"` + Port int `json:"sample_port"` +} + +// A VoiceICE stores data for voice ICE servers. +type VoiceICE struct { + TTL string `json:"ttl"` + Servers []*ICEServer `json:"servers"` +} + +// A ICEServer stores data for a specific voice ICE server. +type ICEServer struct { + URL string `json:"url"` + Username string `json:"username"` + Credential string `json:"credential"` +} + +// A Invite stores all data related to a specific Discord Guild or Channel invite. +type Invite struct { + Guild *Guild `json:"guild"` + Channel *Channel `json:"channel"` + Inviter *User `json:"inviter"` + Code string `json:"code"` + CreatedAt string `json:"created_at"` // TODO make timestamp + MaxAge int `json:"max_age"` + Uses int `json:"uses"` + MaxUses int `json:"max_uses"` + XkcdPass string `json:"xkcdpass"` + Revoked bool `json:"revoked"` + Temporary bool `json:"temporary"` +} + +// A Channel holds all data related to an individual Discord channel. +type Channel struct { + ID string `json:"id"` + GuildID string `json:"guild_id"` + Name string `json:"name"` + Topic string `json:"topic"` + Type string `json:"type"` + LastMessageID string `json:"last_message_id"` + Position int `json:"position"` + Bitrate int `json:"bitrate"` + IsPrivate bool `json:"is_private"` + Recipient *User `json:"recipient"` + Messages []*Message `json:"-"` + PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"` +} + +// A PermissionOverwrite holds permission overwrite data for a Channel +type PermissionOverwrite struct { + ID string `json:"id"` + Type string `json:"type"` + Deny int `json:"deny"` + Allow int `json:"allow"` +} + +// Emoji struct holds data related to Emoji's +type Emoji struct { + ID string `json:"id"` + Name string `json:"name"` + Roles []string `json:"roles"` + Managed bool `json:"managed"` + RequireColons bool `json:"require_colons"` +} + +// VerificationLevel type defination +type VerificationLevel int + +// Constants for VerificationLevel levels from 0 to 3 inclusive +const ( + VerificationLevelNone VerificationLevel = iota + VerificationLevelLow + VerificationLevelMedium + VerificationLevelHigh +) + +// A Guild holds all data related to a specific Discord Guild. Guilds are also +// sometimes referred to as Servers in the Discord client. +type Guild struct { + ID string `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + Region string `json:"region"` + AfkChannelID string `json:"afk_channel_id"` + EmbedChannelID string `json:"embed_channel_id"` + OwnerID string `json:"owner_id"` + JoinedAt string `json:"joined_at"` // make this a timestamp + Splash string `json:"splash"` + AfkTimeout int `json:"afk_timeout"` + VerificationLevel VerificationLevel `json:"verification_level"` + EmbedEnabled bool `json:"embed_enabled"` + Large bool `json:"large"` // ?? + DefaultMessageNotifications int `json:"default_message_notifications"` + Roles []*Role `json:"roles"` + Emojis []*Emoji `json:"emojis"` + Members []*Member `json:"members"` + Presences []*Presence `json:"presences"` + Channels []*Channel `json:"channels"` + VoiceStates []*VoiceState `json:"voice_states"` + Unavailable *bool `json:"unavailable"` +} + +// A GuildParams stores all the data needed to update discord guild settings +type GuildParams struct { + Name string `json:"name"` + Region string `json:"region"` + VerificationLevel *VerificationLevel `json:"verification_level"` +} + +// A Role stores information about Discord guild member roles. +type Role struct { + ID string `json:"id"` + Name string `json:"name"` + Managed bool `json:"managed"` + Hoist bool `json:"hoist"` + Color int `json:"color"` + Position int `json:"position"` + Permissions int `json:"permissions"` +} + +// A VoiceState stores the voice states of Guilds +type VoiceState struct { + UserID string `json:"user_id"` + SessionID string `json:"session_id"` + ChannelID string `json:"channel_id"` + GuildID string `json:"guild_id"` + Suppress bool `json:"suppress"` + SelfMute bool `json:"self_mute"` + SelfDeaf bool `json:"self_deaf"` + Mute bool `json:"mute"` + Deaf bool `json:"deaf"` +} + +// A Presence stores the online, offline, or idle and game status of Guild members. +type Presence struct { + User *User `json:"user"` + Status string `json:"status"` + Game *Game `json:"game"` +} + +// A Game struct holds the name of the "playing .." game for a user +type Game struct { + Name string `json:"name"` + Type int `json:"type"` + URL string `json:"url"` +} + +// A Member stores user information for Guild members. +type Member struct { + GuildID string `json:"guild_id"` + JoinedAt string `json:"joined_at"` + Nick string `json:"nick"` + Deaf bool `json:"deaf"` + Mute bool `json:"mute"` + User *User `json:"user"` + Roles []string `json:"roles"` +} + +// A User stores all data for an individual Discord user. +type User struct { + ID string `json:"id"` + Email string `json:"email"` + Username string `json:"username"` + Avatar string `json:"Avatar"` + Discriminator string `json:"discriminator"` + Token string `json:"token"` + Verified bool `json:"verified"` + MFAEnabled bool `json:"mfa_enabled"` + Bot bool `json:"bot"` +} + +// A Settings stores data for a specific users Discord client settings. +type Settings struct { + RenderEmbeds bool `json:"render_embeds"` + InlineEmbedMedia bool `json:"inline_embed_media"` + InlineAttachmentMedia bool `json:"inline_attachment_media"` + EnableTtsCommand bool `json:"enable_tts_command"` + MessageDisplayCompact bool `json:"message_display_compact"` + ShowCurrentGame bool `json:"show_current_game"` + AllowEmailFriendRequest bool `json:"allow_email_friend_request"` + ConvertEmoticons bool `json:"convert_emoticons"` + Locale string `json:"locale"` + Theme string `json:"theme"` + GuildPositions []string `json:"guild_positions"` + RestrictedGuilds []string `json:"restricted_guilds"` + FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"` +} + +// FriendSourceFlags stores ... TODO :) +type FriendSourceFlags struct { + All bool `json:"all"` + MutualGuilds bool `json:"mutual_guilds"` + MutualFriends bool `json:"mutual_friends"` +} + +// An Event provides a basic initial struct for all websocket event. +type Event struct { + Operation int `json:"op"` + Sequence int `json:"s"` + Type string `json:"t"` + RawData json.RawMessage `json:"d"` + Struct interface{} `json:"-"` +} + +// A Ready stores all data for the websocket READY event. +type Ready struct { + Version int `json:"v"` + SessionID string `json:"session_id"` + HeartbeatInterval time.Duration `json:"heartbeat_interval"` + User *User `json:"user"` + ReadState []*ReadState `json:"read_state"` + PrivateChannels []*Channel `json:"private_channels"` + Guilds []*Guild `json:"guilds"` + + // Undocumented fields + Settings *Settings `json:"user_settings"` + UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"` + Relationships []*Relationship `json:"relationships"` + Presences []*Presence `json:"presences"` +} + +// A Relationship between the logged in user and Relationship.User +type Relationship struct { + User *User `json:"user"` + Type int `json:"type"` // 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req + ID string `json:"id"` +} + +// A TooManyRequests struct holds information received from Discord +// when receiving a HTTP 429 response. +type TooManyRequests struct { + Bucket string `json:"bucket"` + Message string `json:"message"` + RetryAfter time.Duration `json:"retry_after"` +} + +// A ReadState stores data on the read state of channels. +type ReadState struct { + MentionCount int `json:"mention_count"` + LastMessageID string `json:"last_message_id"` + ID string `json:"id"` +} + +// A TypingStart stores data for the typing start websocket event. +type TypingStart struct { + UserID string `json:"user_id"` + ChannelID string `json:"channel_id"` + Timestamp int `json:"timestamp"` +} + +// A PresenceUpdate stores data for the presence update websocket event. +type PresenceUpdate struct { + Presence + GuildID string `json:"guild_id"` + Roles []string `json:"roles"` +} + +// A MessageAck stores data for the message ack websocket event. +type MessageAck struct { + MessageID string `json:"message_id"` + ChannelID string `json:"channel_id"` +} + +// A GuildIntegrationsUpdate stores data for the guild integrations update +// websocket event. +type GuildIntegrationsUpdate struct { + GuildID string `json:"guild_id"` +} + +// A GuildRole stores data for guild role websocket events. +type GuildRole struct { + Role *Role `json:"role"` + GuildID string `json:"guild_id"` +} + +// A GuildRoleDelete stores data for the guild role delete websocket event. +type GuildRoleDelete struct { + RoleID string `json:"role_id"` + GuildID string `json:"guild_id"` +} + +// A GuildBan stores data for a guild ban. +type GuildBan struct { + User *User `json:"user"` + GuildID string `json:"guild_id"` +} + +// A GuildEmojisUpdate stores data for a guild emoji update event. +type GuildEmojisUpdate struct { + GuildID string `json:"guild_id"` + Emojis []*Emoji `json:"emojis"` +} + +// A GuildIntegration stores data for a guild integration. +type GuildIntegration struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Enabled bool `json:"enabled"` + Syncing bool `json:"syncing"` + RoleID string `json:"role_id"` + ExpireBehavior int `json:"expire_behavior"` + ExpireGracePeriod int `json:"expire_grace_period"` + User *User `json:"user"` + Account *GuildIntegrationAccount `json:"account"` + SyncedAt int `json:"synced_at"` +} + +// A GuildIntegrationAccount stores data for a guild integration account. +type GuildIntegrationAccount struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// A GuildEmbed stores data for a guild embed. +type GuildEmbed struct { + Enabled bool `json:"enabled"` + ChannelID string `json:"channel_id"` +} + +// A UserGuildSettingsChannelOverride stores data for a channel override for a users guild settings. +type UserGuildSettingsChannelOverride struct { + Muted bool `json:"muted"` + MessageNotifications int `json:"message_notifications"` + ChannelID string `json:"channel_id"` +} + +// A UserGuildSettings stores data for a users guild settings. +type UserGuildSettings struct { + SupressEveryone bool `json:"suppress_everyone"` + Muted bool `json:"muted"` + MobilePush bool `json:"mobile_push"` + MessageNotifications int `json:"message_notifications"` + GuildID string `json:"guild_id"` + ChannelOverrides []*UserGuildSettingsChannelOverride `json:"channel_overrides"` +} + +// A UserGuildSettingsEdit stores data for editing UserGuildSettings +type UserGuildSettingsEdit struct { + SupressEveryone bool `json:"suppress_everyone"` + Muted bool `json:"muted"` + MobilePush bool `json:"mobile_push"` + MessageNotifications int `json:"message_notifications"` + ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"` +} + +// Constants for the different bit offsets of text channel permissions +const ( + PermissionReadMessages = 1 << (iota + 10) + PermissionSendMessages + PermissionSendTTSMessages + PermissionManageMessages + PermissionEmbedLinks + PermissionAttachFiles + PermissionReadMessageHistory + PermissionMentionEveryone +) + +// Constants for the different bit offsets of voice permissions +const ( + PermissionVoiceConnect = 1 << (iota + 20) + PermissionVoiceSpeak + PermissionVoiceMuteMembers + PermissionVoiceDeafenMembers + PermissionVoiceMoveMembers + PermissionVoiceUseVAD +) + +// Constants for the different bit offsets of general permissions +const ( + PermissionCreateInstantInvite = 1 << iota + PermissionKickMembers + PermissionBanMembers + PermissionManageRoles + PermissionManageChannels + PermissionManageServer + + PermissionAllText = PermissionReadMessages | + PermissionSendMessages | + PermissionSendTTSMessages | + PermissionManageMessages | + PermissionEmbedLinks | + PermissionAttachFiles | + PermissionReadMessageHistory | + PermissionMentionEveryone + PermissionAllVoice = PermissionVoiceConnect | + PermissionVoiceSpeak | + PermissionVoiceMuteMembers | + PermissionVoiceDeafenMembers | + PermissionVoiceMoveMembers | + PermissionVoiceUseVAD + PermissionAllChannel = PermissionAllText | + PermissionAllVoice | + PermissionCreateInstantInvite | + PermissionManageRoles | + PermissionManageChannels + PermissionAll = PermissionAllChannel | + PermissionKickMembers | + PermissionBanMembers | + PermissionManageServer +) diff --git a/vendor/github.com/bwmarrin/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go new file mode 100644 index 00000000..094aa59e --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/voice.go @@ -0,0 +1,853 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains code related to Discord voice suppport + +package discordgo + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "log" + "net" + "runtime" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" + "golang.org/x/crypto/nacl/secretbox" +) + +// ------------------------------------------------------------------------------------------------ +// Code related to both VoiceConnection Websocket and UDP connections. +// ------------------------------------------------------------------------------------------------ + +// A VoiceConnection struct holds all the data and functions related to a Discord Voice Connection. +type VoiceConnection struct { + sync.RWMutex + + Debug bool // If true, print extra logging -- DEPRECATED + LogLevel int + Ready bool // If true, voice is ready to send/receive audio + UserID string + GuildID string + ChannelID string + deaf bool + mute bool + speaking bool + reconnecting bool // If true, voice connection is trying to reconnect + + OpusSend chan []byte // Chan for sending opus audio + OpusRecv chan *Packet // Chan for receiving opus audio + + wsConn *websocket.Conn + wsMutex sync.Mutex + udpConn *net.UDPConn + session *Session + + sessionID string + token string + endpoint string + + // Used to send a close signal to goroutines + close chan struct{} + + // Used to allow blocking until connected + connected chan bool + + // Used to pass the sessionid from onVoiceStateUpdate + // sessionRecv chan string UNUSED ATM + + op4 voiceOP4 + op2 voiceOP2 + + voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler +} + +// VoiceSpeakingUpdateHandler type provides a function defination for the +// VoiceSpeakingUpdate event +type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate) + +// Speaking sends a speaking notification to Discord over the voice websocket. +// This must be sent as true prior to sending audio and should be set to false +// once finished sending audio. +// b : Send true if speaking, false if not. +func (v *VoiceConnection) Speaking(b bool) (err error) { + + v.log(LogDebug, "called (%t)", b) + + type voiceSpeakingData struct { + Speaking bool `json:"speaking"` + Delay int `json:"delay"` + } + + type voiceSpeakingOp struct { + Op int `json:"op"` // Always 5 + Data voiceSpeakingData `json:"d"` + } + + if v.wsConn == nil { + return fmt.Errorf("No VoiceConnection websocket.") + } + + data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}} + v.wsMutex.Lock() + err = v.wsConn.WriteJSON(data) + v.wsMutex.Unlock() + if err != nil { + v.speaking = false + log.Println("Speaking() write json error:", err) + return + } + v.speaking = b + + return +} + +// ChangeChannel sends Discord a request to change channels within a Guild +// !!! NOTE !!! This function may be removed in favour of just using ChannelVoiceJoin +func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err error) { + + v.log(LogInformational, "called") + + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, mute, deaf}} + v.wsMutex.Lock() + err = v.session.wsConn.WriteJSON(data) + v.wsMutex.Unlock() + if err != nil { + return + } + v.ChannelID = channelID + v.deaf = deaf + v.mute = mute + v.speaking = false + + return +} + +// Disconnect disconnects from this voice channel and closes the websocket +// and udp connections to Discord. +// !!! NOTE !!! this function may be removed in favour of ChannelVoiceLeave +func (v *VoiceConnection) Disconnect() (err error) { + + // Send a OP4 with a nil channel to disconnect + if v.sessionID != "" { + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} + v.wsMutex.Lock() + err = v.session.wsConn.WriteJSON(data) + v.wsMutex.Unlock() + v.sessionID = "" + } + + // Close websocket and udp connections + v.Close() + + v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID) + delete(v.session.VoiceConnections, v.GuildID) + + return +} + +// Close closes the voice ws and udp connections +func (v *VoiceConnection) Close() { + + v.log(LogInformational, "called") + + v.Lock() + defer v.Unlock() + + v.Ready = false + v.speaking = false + + if v.close != nil { + v.log(LogInformational, "closing v.close") + close(v.close) + v.close = nil + } + + if v.udpConn != nil { + v.log(LogInformational, "closing udp") + err := v.udpConn.Close() + if err != nil { + log.Println("error closing udp connection: ", err) + } + v.udpConn = nil + } + + if v.wsConn != nil { + v.log(LogInformational, "sending close frame") + + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + v.log(LogError, "error closing websocket, %s", err) + } + + // TODO: Wait for Discord to actually close the connection. + time.Sleep(1 * time.Second) + + v.log(LogInformational, "closing websocket") + err = v.wsConn.Close() + if err != nil { + v.log(LogError, "error closing websocket, %s", err) + } + + v.wsConn = nil + } +} + +// AddHandler adds a Handler for VoiceSpeakingUpdate events. +func (v *VoiceConnection) AddHandler(h VoiceSpeakingUpdateHandler) { + v.Lock() + defer v.Unlock() + + v.voiceSpeakingUpdateHandlers = append(v.voiceSpeakingUpdateHandlers, h) +} + +// VoiceSpeakingUpdate is a struct for a VoiceSpeakingUpdate event. +type VoiceSpeakingUpdate struct { + UserID string `json:"user_id"` + SSRC int `json:"ssrc"` + Speaking bool `json:"speaking"` +} + +// ------------------------------------------------------------------------------------------------ +// Unexported Internal Functions Below. +// ------------------------------------------------------------------------------------------------ + +// A voiceOP4 stores the data for the voice operation 4 websocket event +// which provides us with the NaCl SecretBox encryption key +type voiceOP4 struct { + SecretKey [32]byte `json:"secret_key"` + Mode string `json:"mode"` +} + +// A voiceOP2 stores the data for the voice operation 2 websocket event +// which is sort of like the voice READY packet +type voiceOP2 struct { + SSRC uint32 `json:"ssrc"` + Port int `json:"port"` + Modes []string `json:"modes"` + HeartbeatInterval time.Duration `json:"heartbeat_interval"` +} + +// WaitUntilConnected waits for the Voice Connection to +// become ready, if it does not become ready it retuns an err +func (v *VoiceConnection) waitUntilConnected() error { + + v.log(LogInformational, "called") + + i := 0 + for { + if v.Ready { + return nil + } + + if i > 10 { + return fmt.Errorf("Timeout waiting for voice.") + } + + time.Sleep(1 * time.Second) + i++ + } +} + +// Open opens a voice connection. This should be called +// after VoiceChannelJoin is used and the data VOICE websocket events +// are captured. +func (v *VoiceConnection) open() (err error) { + + v.log(LogInformational, "called") + + v.Lock() + defer v.Unlock() + + // Don't open a websocket if one is already open + if v.wsConn != nil { + v.log(LogWarning, "refusing to overwrite non-nil websocket") + return + } + + // TODO temp? loop to wait for the SessionID + i := 0 + for { + if v.sessionID != "" { + break + } + if i > 20 { // only loop for up to 1 second total + return fmt.Errorf("Did not receive voice Session ID in time.") + } + time.Sleep(50 * time.Millisecond) + i++ + } + + // Connect to VoiceConnection Websocket + vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80")) + v.log(LogInformational, "connecting to voice endpoint %s", vg) + v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil) + if err != nil { + v.log(LogWarning, "error connecting to voice endpoint %s, %s", vg, err) + v.log(LogDebug, "voice struct: %#v\n", v) + return + } + + type voiceHandshakeData struct { + ServerID string `json:"server_id"` + UserID string `json:"user_id"` + SessionID string `json:"session_id"` + Token string `json:"token"` + } + type voiceHandshakeOp struct { + Op int `json:"op"` // Always 0 + Data voiceHandshakeData `json:"d"` + } + data := voiceHandshakeOp{0, voiceHandshakeData{v.GuildID, v.UserID, v.sessionID, v.token}} + + err = v.wsConn.WriteJSON(data) + if err != nil { + v.log(LogWarning, "error sending init packet, %s", err) + return + } + + v.close = make(chan struct{}) + go v.wsListen(v.wsConn, v.close) + + // add loop/check for Ready bool here? + // then return false if not ready? + // but then wsListen will also err. + + return +} + +// wsListen listens on the voice websocket for messages and passes them +// to the voice event handler. This is automatically called by the Open func +func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}) { + + v.log(LogInformational, "called") + + for { + _, message, err := v.wsConn.ReadMessage() + if err != nil { + // Detect if we have been closed manually. If a Close() has already + // happened, the websocket we are listening on will be different to the + // current session. + v.RLock() + sameConnection := v.wsConn == wsConn + v.RUnlock() + if sameConnection { + + v.log(LogError, "voice endpoint %s websocket closed unexpectantly, %s", v.endpoint, err) + + // Start reconnect goroutine then exit. + go v.reconnect() + } + return + } + + // Pass received message to voice event handler + select { + case <-close: + return + default: + go v.onEvent(message) + } + } +} + +// wsEvent handles any voice websocket events. This is only called by the +// wsListen() function. +func (v *VoiceConnection) onEvent(message []byte) { + + v.log(LogDebug, "received: %s", string(message)) + + var e Event + if err := json.Unmarshal(message, &e); err != nil { + v.log(LogError, "unmarshall error, %s", err) + return + } + + switch e.Operation { + + case 2: // READY + + if err := json.Unmarshal(e.RawData, &v.op2); err != nil { + v.log(LogError, "OP2 unmarshall error, %s, %s", err, string(e.RawData)) + return + } + + // Start the voice websocket heartbeat to keep the connection alive + go v.wsHeartbeat(v.wsConn, v.close, v.op2.HeartbeatInterval) + // TODO monitor a chan/bool to verify this was successful + + // Start the UDP connection + err := v.udpOpen() + if err != nil { + v.log(LogError, "error opening udp connection, %s", err) + return + } + + // Start the opusSender. + // TODO: Should we allow 48000/960 values to be user defined? + if v.OpusSend == nil { + v.OpusSend = make(chan []byte, 2) + } + go v.opusSender(v.udpConn, v.close, v.OpusSend, 48000, 960) + + // Start the opusReceiver + if !v.deaf { + if v.OpusRecv == nil { + v.OpusRecv = make(chan *Packet, 2) + } + + go v.opusReceiver(v.udpConn, v.close, v.OpusRecv) + } + + // Send the ready event + v.connected <- true + return + + case 3: // HEARTBEAT response + // add code to use this to track latency? + return + + case 4: // udp encryption secret key + v.op4 = voiceOP4{} + if err := json.Unmarshal(e.RawData, &v.op4); err != nil { + v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData)) + return + } + return + + case 5: + if len(v.voiceSpeakingUpdateHandlers) == 0 { + return + } + + voiceSpeakingUpdate := &VoiceSpeakingUpdate{} + if err := json.Unmarshal(e.RawData, voiceSpeakingUpdate); err != nil { + v.log(LogError, "OP5 unmarshall error, %s, %s", err, string(e.RawData)) + return + } + + for _, h := range v.voiceSpeakingUpdateHandlers { + h(v, voiceSpeakingUpdate) + } + + default: + v.log(LogError, "unknown voice operation, %d, %s", e.Operation, string(e.RawData)) + } + + return +} + +type voiceHeartbeatOp struct { + Op int `json:"op"` // Always 3 + Data int `json:"d"` +} + +// NOTE :: When a guild voice server changes how do we shut this down +// properly, so a new connection can be setup without fuss? +// +// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client +// is still connected. If you do not send these heartbeats Discord will +// disconnect the websocket connection after a few seconds. +func (v *VoiceConnection) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i time.Duration) { + + if close == nil || wsConn == nil { + return + } + + var err error + ticker := time.NewTicker(i * time.Millisecond) + for { + v.log(LogDebug, "sending heartbeat packet") + v.wsMutex.Lock() + err = wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())}) + v.wsMutex.Unlock() + if err != nil { + v.log(LogError, "error sending heartbeat to voice endpoint %s, %s", v.endpoint, err) + return + } + + select { + case <-ticker.C: + // continue loop and send heartbeat + case <-close: + return + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Code related to the VoiceConnection UDP connection +// ------------------------------------------------------------------------------------------------ + +type voiceUDPData struct { + Address string `json:"address"` // Public IP of machine running this code + Port uint16 `json:"port"` // UDP Port of machine running this code + Mode string `json:"mode"` // always "xsalsa20_poly1305" +} + +type voiceUDPD struct { + Protocol string `json:"protocol"` // Always "udp" ? + Data voiceUDPData `json:"data"` +} + +type voiceUDPOp struct { + Op int `json:"op"` // Always 1 + Data voiceUDPD `json:"d"` +} + +// udpOpen opens a UDP connection to the voice server and completes the +// initial required handshake. This connection is left open in the session +// and can be used to send or receive audio. This should only be called +// from voice.wsEvent OP2 +func (v *VoiceConnection) udpOpen() (err error) { + + v.Lock() + defer v.Unlock() + + if v.wsConn == nil { + return fmt.Errorf("nil voice websocket") + } + + if v.udpConn != nil { + return fmt.Errorf("udp connection already open") + } + + if v.close == nil { + return fmt.Errorf("nil close channel") + } + + if v.endpoint == "" { + return fmt.Errorf("empty endpoint") + } + + host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.op2.Port) + addr, err := net.ResolveUDPAddr("udp", host) + if err != nil { + v.log(LogWarning, "error resolving udp host %s, %s", host, err) + return + } + + v.log(LogInformational, "connecting to udp addr %s", addr.String()) + v.udpConn, err = net.DialUDP("udp", nil, addr) + if err != nil { + v.log(LogWarning, "error connecting to udp addr %s, %s", addr.String(), err) + return + } + + // Create a 70 byte array and put the SSRC code from the Op 2 VoiceConnection event + // into it. Then send that over the UDP connection to Discord + sb := make([]byte, 70) + binary.BigEndian.PutUint32(sb, v.op2.SSRC) + _, err = v.udpConn.Write(sb) + if err != nil { + v.log(LogWarning, "udp write error to %s, %s", addr.String(), err) + return + } + + // Create a 70 byte array and listen for the initial handshake response + // from Discord. Once we get it parse the IP and PORT information out + // of the response. This should be our public IP and PORT as Discord + // saw us. + rb := make([]byte, 70) + rlen, _, err := v.udpConn.ReadFromUDP(rb) + if err != nil { + v.log(LogWarning, "udp read error, %s, %s", addr.String(), err) + return + } + + if rlen < 70 { + v.log(LogWarning, "received udp packet too small") + return fmt.Errorf("received udp packet too small") + } + + // Loop over position 4 though 20 to grab the IP address + // Should never be beyond position 20. + var ip string + for i := 4; i < 20; i++ { + if rb[i] == 0 { + break + } + ip += string(rb[i]) + } + + // Grab port from position 68 and 69 + port := binary.LittleEndian.Uint16(rb[68:70]) + + // Take the data from above and send it back to Discord to finalize + // the UDP connection handshake. + data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "xsalsa20_poly1305"}}} + + v.wsMutex.Lock() + err = v.wsConn.WriteJSON(data) + v.wsMutex.Unlock() + if err != nil { + v.log(LogWarning, "udp write error, %#v, %s", data, err) + return + } + + // start udpKeepAlive + go v.udpKeepAlive(v.udpConn, v.close, 5*time.Second) + // TODO: find a way to check that it fired off okay + + return +} + +// udpKeepAlive sends a udp packet to keep the udp connection open +// This is still a bit of a "proof of concept" +func (v *VoiceConnection) udpKeepAlive(udpConn *net.UDPConn, close <-chan struct{}, i time.Duration) { + + if udpConn == nil || close == nil { + return + } + + var err error + var sequence uint64 + + packet := make([]byte, 8) + + ticker := time.NewTicker(i) + for { + + binary.LittleEndian.PutUint64(packet, sequence) + sequence++ + + _, err = udpConn.Write(packet) + if err != nil { + v.log(LogError, "write error, %s", err) + return + } + + select { + case <-ticker.C: + // continue loop and send keepalive + case <-close: + return + } + } +} + +// opusSender will listen on the given channel and send any +// pre-encoded opus audio to Discord. Supposedly. +func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}, opus <-chan []byte, rate, size int) { + + if udpConn == nil || close == nil { + return + } + + runtime.LockOSThread() + + // VoiceConnection is now ready to receive audio packets + // TODO: this needs reviewed as I think there must be a better way. + v.Ready = true + defer func() { v.Ready = false }() + + var sequence uint16 + var timestamp uint32 + var recvbuf []byte + var ok bool + udpHeader := make([]byte, 12) + var nonce [24]byte + + // build the parts that don't change in the udpHeader + udpHeader[0] = 0x80 + udpHeader[1] = 0x78 + binary.BigEndian.PutUint32(udpHeader[8:], v.op2.SSRC) + + // start a send loop that loops until buf chan is closed + ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000))) + for { + + // Get data from chan. If chan is closed, return. + select { + case <-close: + return + case recvbuf, ok = <-opus: + if !ok { + return + } + // else, continue loop + } + + if !v.speaking { + err := v.Speaking(true) + if err != nil { + v.log(LogError, "error sending speaking packet, %s", err) + } + } + + // Add sequence and timestamp to udpPacket + binary.BigEndian.PutUint16(udpHeader[2:], sequence) + binary.BigEndian.PutUint32(udpHeader[4:], timestamp) + + // encrypt the opus data + copy(nonce[:], udpHeader) + sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey) + + // block here until we're exactly at the right time :) + // Then send rtp audio packet to Discord over UDP + select { + case <-close: + return + case <-ticker.C: + // continue + } + _, err := udpConn.Write(sendbuf) + + if err != nil { + v.log(LogError, "udp write error, %s", err) + v.log(LogDebug, "voice struct: %#v\n", v) + return + } + + if (sequence) == 0xFFFF { + sequence = 0 + } else { + sequence++ + } + + if (timestamp + uint32(size)) >= 0xFFFFFFFF { + timestamp = 0 + } else { + timestamp += uint32(size) + } + } +} + +// A Packet contains the headers and content of a received voice packet. +type Packet struct { + SSRC uint32 + Sequence uint16 + Timestamp uint32 + Type []byte + Opus []byte + PCM []int16 +} + +// opusReceiver listens on the UDP socket for incoming packets +// and sends them across the given channel +// NOTE :: This function may change names later. +func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct{}, c chan *Packet) { + + if udpConn == nil || close == nil { + return + } + + p := Packet{} + recvbuf := make([]byte, 1024) + var nonce [24]byte + + for { + rlen, err := udpConn.Read(recvbuf) + if err != nil { + // Detect if we have been closed manually. If a Close() has already + // happened, the udp connection we are listening on will be different + // to the current session. + v.RLock() + sameConnection := v.udpConn == udpConn + v.RUnlock() + if sameConnection { + + v.log(LogError, "udp read error, %s, %s", v.endpoint, err) + v.log(LogDebug, "voice struct: %#v\n", v) + + go v.reconnect() + } + return + } + + select { + case <-close: + return + default: + // continue loop + } + + // For now, skip anything except audio. + if rlen < 12 || recvbuf[0] != 0x80 { + continue + } + + // build a audio packet struct + p.Type = recvbuf[0:2] + p.Sequence = binary.BigEndian.Uint16(recvbuf[2:4]) + p.Timestamp = binary.BigEndian.Uint32(recvbuf[4:8]) + p.SSRC = binary.BigEndian.Uint32(recvbuf[8:12]) + // decrypt opus data + copy(nonce[:], recvbuf[0:12]) + p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey) + + if c != nil { + c <- &p + } + } +} + +// Reconnect will close down a voice connection then immediately try to +// reconnect to that session. +// NOTE : This func is messy and a WIP while I find what works. +// It will be cleaned up once a proven stable option is flushed out. +// aka: this is ugly shit code, please don't judge too harshly. +func (v *VoiceConnection) reconnect() { + + v.log(LogInformational, "called") + + v.Lock() + if v.reconnecting { + v.log(LogInformational, "already reconnecting to channel %s, exiting", v.ChannelID) + v.Unlock() + return + } + v.reconnecting = true + v.Unlock() + + defer func() { v.reconnecting = false }() + + // Close any currently open connections + v.Close() + + wait := time.Duration(1) + for { + + <-time.After(wait * time.Second) + wait *= 2 + if wait > 600 { + wait = 600 + } + + if v.session.DataReady == false || v.session.wsConn == nil { + v.log(LogInformational, "cannot reconenct to channel %s with unready session", v.ChannelID) + continue + } + + v.log(LogInformational, "trying to reconnect to channel %s", v.ChannelID) + + _, err := v.session.ChannelVoiceJoin(v.GuildID, v.ChannelID, v.mute, v.deaf) + if err == nil { + v.log(LogInformational, "successfully reconnected to channel %s", v.ChannelID) + return + } + + // if the reconnect above didn't work lets just send a disconnect + // packet to reset things. + // Send a OP4 with a nil channel to disconnect + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} + v.session.wsMutex.Lock() + err = v.session.wsConn.WriteJSON(data) + v.session.wsMutex.Unlock() + if err != nil { + v.log(LogError, "error sending disconnect packet, %s", err) + } + + v.log(LogInformational, "error reconnecting to channel %s, %s", v.ChannelID, err) + } +} diff --git a/vendor/github.com/bwmarrin/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go new file mode 100644 index 00000000..a19c3842 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/wsapi.go @@ -0,0 +1,679 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains low level functions for interacting with the Discord +// data websocket interface. + +package discordgo + +import ( + "bytes" + "compress/zlib" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net/http" + "reflect" + "runtime" + "time" + + "github.com/gorilla/websocket" +) + +type resumePacket struct { + Op int `json:"op"` + Data struct { + Token string `json:"token"` + SessionID string `json:"session_id"` + Sequence int `json:"seq"` + } `json:"d"` +} + +// Open opens a websocket connection to Discord. +func (s *Session) Open() (err error) { + + s.log(LogInformational, "called") + + s.Lock() + defer func() { + if err != nil { + s.Unlock() + } + }() + + if s.wsConn != nil { + err = errors.New("Web socket already opened.") + return + } + + if s.VoiceConnections == nil { + s.log(LogInformational, "creating new VoiceConnections map") + s.VoiceConnections = make(map[string]*VoiceConnection) + } + + // Get the gateway to use for the Websocket connection + if s.gateway == "" { + s.gateway, err = s.Gateway() + if err != nil { + return + } + + // Add the version and encoding to the URL + s.gateway = fmt.Sprintf("%s?v=4&encoding=json", s.gateway) + } + + header := http.Header{} + header.Add("accept-encoding", "zlib") + + s.log(LogInformational, "connecting to gateway %s", s.gateway) + s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header) + if err != nil { + s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err) + s.gateway = "" // clear cached gateway + // TODO: should we add a retry block here? + return + } + + if s.sessionID != "" && s.sequence > 0 { + + p := resumePacket{} + p.Op = 6 + p.Data.Token = s.Token + p.Data.SessionID = s.sessionID + p.Data.Sequence = s.sequence + + s.log(LogInformational, "sending resume packet to gateway") + err = s.wsConn.WriteJSON(p) + if err != nil { + s.log(LogWarning, "error sending gateway resume packet, %s, %s", s.gateway, err) + return + } + + } else { + + err = s.identify() + if err != nil { + s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err) + return + } + } + + // Create listening outside of listen, as it needs to happen inside the mutex + // lock. + s.listening = make(chan interface{}) + go s.listen(s.wsConn, s.listening) + + s.Unlock() + + s.initialize() + s.log(LogInformational, "emit connect event") + s.handle(&Connect{}) + + s.log(LogInformational, "exiting") + return +} + +// listen polls the websocket connection for events, it will stop when the +// listening channel is closed, or an error occurs. +func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) { + + s.log(LogInformational, "called") + + for { + + messageType, message, err := wsConn.ReadMessage() + + if err != nil { + + // Detect if we have been closed manually. If a Close() has already + // happened, the websocket we are listening on will be different to + // the current session. + s.RLock() + sameConnection := s.wsConn == wsConn + s.RUnlock() + + if sameConnection { + + s.log(LogWarning, "error reading from gateway %s websocket, %s", s.gateway, err) + // There has been an error reading, close the websocket so that + // OnDisconnect event is emitted. + err := s.Close() + if err != nil { + s.log(LogWarning, "error closing session connection, %s", err) + } + + s.log(LogInformational, "calling reconnect() now") + s.reconnect() + } + + return + } + + select { + + case <-listening: + return + + default: + s.onEvent(messageType, message) + + } + } +} + +type heartbeatOp struct { + Op int `json:"op"` + Data int `json:"d"` +} + +// heartbeat sends regular heartbeats to Discord so it knows the client +// is still connected. If you do not send these heartbeats Discord will +// disconnect the websocket connection after a few seconds. +func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, i time.Duration) { + + s.log(LogInformational, "called") + + if listening == nil || wsConn == nil { + return + } + + var err error + ticker := time.NewTicker(i * time.Millisecond) + + for { + + s.log(LogInformational, "sending gateway websocket heartbeat seq %d", s.sequence) + s.wsMutex.Lock() + err = wsConn.WriteJSON(heartbeatOp{1, s.sequence}) + s.wsMutex.Unlock() + if err != nil { + s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err) + s.Lock() + s.DataReady = false + s.Unlock() + return + } + s.Lock() + s.DataReady = true + s.Unlock() + + select { + case <-ticker.C: + // continue loop and send heartbeat + case <-listening: + return + } + } +} + +type updateStatusData struct { + IdleSince *int `json:"idle_since"` + Game *Game `json:"game"` +} + +type updateStatusOp struct { + Op int `json:"op"` + Data updateStatusData `json:"d"` +} + +// UpdateStreamingStatus is used to update the user's streaming status. +// If idle>0 then set status to idle. +// If game!="" then set game. +// If game!="" and url!="" then set the status type to streaming with the URL set. +// if otherwise, set status to active, and no game. +func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err error) { + + s.log(LogInformational, "called") + + s.RLock() + defer s.RUnlock() + if s.wsConn == nil { + return errors.New("no websocket connection exists") + } + + var usd updateStatusData + if idle > 0 { + usd.IdleSince = &idle + } + + if game != "" { + gameType := 0 + if url != "" { + gameType = 1 + } + usd.Game = &Game{ + Name: game, + Type: gameType, + URL: url, + } + } + + s.wsMutex.Lock() + err = s.wsConn.WriteJSON(updateStatusOp{3, usd}) + s.wsMutex.Unlock() + + return +} + +// UpdateStatus is used to update the user's status. +// If idle>0 then set status to idle. +// If game!="" then set game. +// if otherwise, set status to active, and no game. +func (s *Session) UpdateStatus(idle int, game string) (err error) { + return s.UpdateStreamingStatus(idle, game, "") +} + +// onEvent is the "event handler" for all messages received on the +// Discord Gateway API websocket connection. +// +// If you use the AddHandler() function to register a handler for a +// specific event this function will pass the event along to that handler. +// +// If you use the AddHandler() function to register a handler for the +// "OnEvent" event then all events will be passed to that handler. +// +// TODO: You may also register a custom event handler entirely using... +func (s *Session) onEvent(messageType int, message []byte) { + + var err error + var reader io.Reader + reader = bytes.NewBuffer(message) + + // If this is a compressed message, uncompress it. + if messageType == websocket.BinaryMessage { + + z, err2 := zlib.NewReader(reader) + if err2 != nil { + s.log(LogError, "error uncompressing websocket message, %s", err) + return + } + + defer func() { + err3 := z.Close() + if err3 != nil { + s.log(LogWarning, "error closing zlib, %s", err) + } + }() + + reader = z + } + + // Decode the event into an Event struct. + var e *Event + decoder := json.NewDecoder(reader) + if err = decoder.Decode(&e); err != nil { + s.log(LogError, "error decoding websocket message, %s", err) + return + } + + s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData)) + + // Ping request. + // Must respond with a heartbeat packet within 5 seconds + if e.Operation == 1 { + s.log(LogInformational, "sending heartbeat in response to Op1") + s.wsMutex.Lock() + err = s.wsConn.WriteJSON(heartbeatOp{1, s.sequence}) + s.wsMutex.Unlock() + if err != nil { + s.log(LogError, "error sending heartbeat in response to Op1") + return + } + + return + } + + // Reconnect + // Must immediately disconnect from gateway and reconnect to new gateway. + if e.Operation == 7 { + // TODO + } + + // Invalid Session + // Must respond with a Identify packet. + if e.Operation == 9 { + + s.log(LogInformational, "sending identify packet to gateway in response to Op9") + + err = s.identify() + if err != nil { + s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err) + return + } + + return + } + + // Do not try to Dispatch a non-Dispatch Message + if e.Operation != 0 { + // But we probably should be doing something with them. + // TEMP + s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message)) + return + } + + // Store the message sequence + s.sequence = e.Sequence + + // Map event to registered event handlers and pass it along + // to any registered functions + i := eventToInterface[e.Type] + if i != nil { + + // Create a new instance of the event type. + i = reflect.New(reflect.TypeOf(i)).Interface() + + // Attempt to unmarshal our event. + if err = json.Unmarshal(e.RawData, i); err != nil { + s.log(LogError, "error unmarshalling %s event, %s", e.Type, err) + } + + // Send event to any registered event handlers for it's type. + // Because the above doesn't cancel this, in case of an error + // the struct could be partially populated or at default values. + // However, most errors are due to a single field and I feel + // it's better to pass along what we received than nothing at all. + // TODO: Think about that decision :) + // Either way, READY events must fire, even with errors. + go s.handle(i) + + } else { + s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData)) + } + + // Emit event to the OnEvent handler + e.Struct = i + go s.handle(e) +} + +// ------------------------------------------------------------------------------------------------ +// Code related to voice connections that initiate over the data websocket +// ------------------------------------------------------------------------------------------------ + +// A VoiceServerUpdate stores the data received during the Voice Server Update +// data websocket event. This data is used during the initial Voice Channel +// join handshaking. +type VoiceServerUpdate struct { + Token string `json:"token"` + GuildID string `json:"guild_id"` + Endpoint string `json:"endpoint"` +} + +type voiceChannelJoinData struct { + GuildID *string `json:"guild_id"` + ChannelID *string `json:"channel_id"` + SelfMute bool `json:"self_mute"` + SelfDeaf bool `json:"self_deaf"` +} + +type voiceChannelJoinOp struct { + Op int `json:"op"` + Data voiceChannelJoinData `json:"d"` +} + +// ChannelVoiceJoin joins the session user to a voice channel. +// +// gID : Guild ID of the channel to join. +// cID : Channel ID of the channel to join. +// mute : If true, you will be set to muted upon joining. +// deaf : If true, you will be set to deafened upon joining. +func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *VoiceConnection, err error) { + + s.log(LogInformational, "called") + + voice, _ = s.VoiceConnections[gID] + + if voice == nil { + voice = &VoiceConnection{} + s.VoiceConnections[gID] = voice + } + + voice.GuildID = gID + voice.ChannelID = cID + voice.deaf = deaf + voice.mute = mute + voice.session = s + + // Send the request to Discord that we want to join the voice channel + data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}} + s.wsMutex.Lock() + err = s.wsConn.WriteJSON(data) + s.wsMutex.Unlock() + if err != nil { + return + } + + // doesn't exactly work perfect yet.. TODO + err = voice.waitUntilConnected() + if err != nil { + s.log(LogWarning, "error waiting for voice to connect, %s", err) + voice.Close() + return + } + + return +} + +// onVoiceStateUpdate handles Voice State Update events on the data websocket. +func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) { + + // If we don't have a connection for the channel, don't bother + if st.ChannelID == "" { + return + } + + // Check if we have a voice connection to update + voice, exists := s.VoiceConnections[st.GuildID] + if !exists { + return + } + + // Need to have this happen at login and store it in the Session + // TODO : This should be done upon connecting to Discord, or + // be moved to a small helper function + self, err := s.User("@me") // TODO: move to Login/New + if err != nil { + log.Println(err) + return + } + + // We only care about events that are about us + if st.UserID != self.ID { + return + } + + // Store the SessionID for later use. + voice.UserID = self.ID // TODO: Review + voice.sessionID = st.SessionID +} + +// onVoiceServerUpdate handles the Voice Server Update data websocket event. +// +// This is also fired if the Guild's voice region changes while connected +// to a voice channel. In that case, need to re-establish connection to +// the new region endpoint. +func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) { + + s.log(LogInformational, "called") + + voice, exists := s.VoiceConnections[st.GuildID] + + // If no VoiceConnection exists, just skip this + if !exists { + return + } + + // If currently connected to voice ws/udp, then disconnect. + // Has no effect if not connected. + voice.Close() + + // Store values for later use + voice.token = st.Token + voice.endpoint = st.Endpoint + voice.GuildID = st.GuildID + + // Open a conenction to the voice server + err := voice.open() + if err != nil { + s.log(LogError, "onVoiceServerUpdate voice.open, %s", err) + } +} + +type identifyProperties struct { + OS string `json:"$os"` + Browser string `json:"$browser"` + Device string `json:"$device"` + Referer string `json:"$referer"` + ReferringDomain string `json:"$referring_domain"` +} + +type identifyData struct { + Token string `json:"token"` + Properties identifyProperties `json:"properties"` + LargeThreshold int `json:"large_threshold"` + Compress bool `json:"compress"` + Shard *[2]int `json:"shard,omitempty"` +} + +type identifyOp struct { + Op int `json:"op"` + Data identifyData `json:"d"` +} + +// identify sends the identify packet to the gateway +func (s *Session) identify() error { + + properties := identifyProperties{runtime.GOOS, + "Discordgo v" + VERSION, + "", + "", + "", + } + + data := identifyData{s.Token, + properties, + 250, + s.Compress, + nil, + } + + if s.ShardCount > 1 { + + if s.ShardID >= s.ShardCount { + return errors.New("ShardID must be less than ShardCount") + } + + data.Shard = &[2]int{s.ShardID, s.ShardCount} + } + + op := identifyOp{2, data} + + s.wsMutex.Lock() + err := s.wsConn.WriteJSON(op) + s.wsMutex.Unlock() + if err != nil { + return err + } + + return nil +} + +func (s *Session) reconnect() { + + s.log(LogInformational, "called") + + var err error + + if s.ShouldReconnectOnError { + + wait := time.Duration(1) + + for { + s.log(LogInformational, "trying to reconnect to gateway") + + err = s.Open() + if err == nil { + s.log(LogInformational, "successfully reconnected to gateway") + + // I'm not sure if this is actually needed. + // if the gw reconnect works properly, voice should stay alive + // However, there seems to be cases where something "weird" + // happens. So we're doing this for now just to improve + // stability in those edge cases. + for _, v := range s.VoiceConnections { + + s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID) + go v.reconnect() + + // This is here just to prevent violently spamming the + // voice reconnects + time.Sleep(1 * time.Second) + + } + return + } + + s.log(LogError, "error reconnecting to gateway, %s", err) + + <-time.After(wait * time.Second) + wait *= 2 + if wait > 600 { + wait = 600 + } + } + } +} + +// Close closes a websocket and stops all listening/heartbeat goroutines. +// TODO: Add support for Voice WS/UDP connections +func (s *Session) Close() (err error) { + + s.log(LogInformational, "called") + s.Lock() + + s.DataReady = false + + if s.listening != nil { + s.log(LogInformational, "closing listening channel") + close(s.listening) + s.listening = nil + } + + // TODO: Close all active Voice Connections too + // this should force stop any reconnecting voice channels too + + if s.wsConn != nil { + + s.log(LogInformational, "sending close frame") + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + s.log(LogError, "error closing websocket, %s", err) + } + + // TODO: Wait for Discord to actually close the connection. + time.Sleep(1 * time.Second) + + s.log(LogInformational, "closing gateway websocket") + err = s.wsConn.Close() + if err != nil { + s.log(LogError, "error closing websocket, %s", err) + } + + s.wsConn = nil + } + + s.Unlock() + + s.log(LogInformational, "emit disconnect event") + s.handle(&Disconnect{}) + + return +} diff --git a/vendor/golang.org/x/crypto/poly1305/LICENSE b/vendor/golang.org/x/crypto/poly1305/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/crypto/poly1305/const_amd64.s b/vendor/golang.org/x/crypto/poly1305/const_amd64.s new file mode 100644 index 00000000..8e861f33 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/const_amd64.s @@ -0,0 +1,45 @@ +// Copyright 2012 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. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +DATA ·SCALE(SB)/8, $0x37F4000000000000 +GLOBL ·SCALE(SB), 8, $8 +DATA ·TWO32(SB)/8, $0x41F0000000000000 +GLOBL ·TWO32(SB), 8, $8 +DATA ·TWO64(SB)/8, $0x43F0000000000000 +GLOBL ·TWO64(SB), 8, $8 +DATA ·TWO96(SB)/8, $0x45F0000000000000 +GLOBL ·TWO96(SB), 8, $8 +DATA ·ALPHA32(SB)/8, $0x45E8000000000000 +GLOBL ·ALPHA32(SB), 8, $8 +DATA ·ALPHA64(SB)/8, $0x47E8000000000000 +GLOBL ·ALPHA64(SB), 8, $8 +DATA ·ALPHA96(SB)/8, $0x49E8000000000000 +GLOBL ·ALPHA96(SB), 8, $8 +DATA ·ALPHA130(SB)/8, $0x4C08000000000000 +GLOBL ·ALPHA130(SB), 8, $8 +DATA ·DOFFSET0(SB)/8, $0x4330000000000000 +GLOBL ·DOFFSET0(SB), 8, $8 +DATA ·DOFFSET1(SB)/8, $0x4530000000000000 +GLOBL ·DOFFSET1(SB), 8, $8 +DATA ·DOFFSET2(SB)/8, $0x4730000000000000 +GLOBL ·DOFFSET2(SB), 8, $8 +DATA ·DOFFSET3(SB)/8, $0x4930000000000000 +GLOBL ·DOFFSET3(SB), 8, $8 +DATA ·DOFFSET3MINUSTWO128(SB)/8, $0x492FFFFE00000000 +GLOBL ·DOFFSET3MINUSTWO128(SB), 8, $8 +DATA ·HOFFSET0(SB)/8, $0x43300001FFFFFFFB +GLOBL ·HOFFSET0(SB), 8, $8 +DATA ·HOFFSET1(SB)/8, $0x45300001FFFFFFFE +GLOBL ·HOFFSET1(SB), 8, $8 +DATA ·HOFFSET2(SB)/8, $0x47300001FFFFFFFE +GLOBL ·HOFFSET2(SB), 8, $8 +DATA ·HOFFSET3(SB)/8, $0x49300003FFFFFFFE +GLOBL ·HOFFSET3(SB), 8, $8 +DATA ·ROUNDING(SB)/2, $0x137f +GLOBL ·ROUNDING(SB), 8, $2 diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305.go b/vendor/golang.org/x/crypto/poly1305/poly1305.go new file mode 100644 index 00000000..4a5f826f --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/poly1305.go @@ -0,0 +1,32 @@ +// Copyright 2012 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 poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf. + +Poly1305 is a fast, one-time authentication function. It is infeasible for an +attacker to generate an authenticator for a message without the key. However, a +key must only be used for a single message. Authenticating two different +messages with the same key allows an attacker to forge authenticators for other +messages with the same key. + +Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was +used with a fixed key in order to generate one-time keys from an nonce. +However, in this package AES isn't used and the one-time key is specified +directly. +*/ +package poly1305 // import "golang.org/x/crypto/poly1305" + +import "crypto/subtle" + +// TagSize is the size, in bytes, of a poly1305 authenticator. +const TagSize = 16 + +// Verify returns true if mac is a valid authenticator for m with the given +// key. +func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { + var tmp [16]byte + Sum(&tmp, m, key) + return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 +} diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s b/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s new file mode 100644 index 00000000..f8d4ee92 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s @@ -0,0 +1,497 @@ +// Copyright 2012 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. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key) +TEXT ·poly1305(SB),0,$224-32 + MOVQ out+0(FP),DI + MOVQ m+8(FP),SI + MOVQ mlen+16(FP),DX + MOVQ key+24(FP),CX + + MOVQ SP,R11 + MOVQ $31,R9 + NOTQ R9 + ANDQ R9,SP + ADDQ $32,SP + + MOVQ R11,32(SP) + MOVQ R12,40(SP) + MOVQ R13,48(SP) + MOVQ R14,56(SP) + MOVQ R15,64(SP) + MOVQ BX,72(SP) + MOVQ BP,80(SP) + FLDCW ·ROUNDING(SB) + MOVL 0(CX),R8 + MOVL 4(CX),R9 + MOVL 8(CX),AX + MOVL 12(CX),R10 + MOVQ DI,88(SP) + MOVQ CX,96(SP) + MOVL $0X43300000,108(SP) + MOVL $0X45300000,116(SP) + MOVL $0X47300000,124(SP) + MOVL $0X49300000,132(SP) + ANDL $0X0FFFFFFF,R8 + ANDL $0X0FFFFFFC,R9 + ANDL $0X0FFFFFFC,AX + ANDL $0X0FFFFFFC,R10 + MOVL R8,104(SP) + MOVL R9,112(SP) + MOVL AX,120(SP) + MOVL R10,128(SP) + FMOVD 104(SP), F0 + FSUBD ·DOFFSET0(SB), F0 + FMOVD 112(SP), F0 + FSUBD ·DOFFSET1(SB), F0 + FMOVD 120(SP), F0 + FSUBD ·DOFFSET2(SB), F0 + FMOVD 128(SP), F0 + FSUBD ·DOFFSET3(SB), F0 + FXCHD F0, F3 + FMOVDP F0, 136(SP) + FXCHD F0, F1 + FMOVD F0, 144(SP) + FMULD ·SCALE(SB), F0 + FMOVDP F0, 152(SP) + FMOVD F0, 160(SP) + FMULD ·SCALE(SB), F0 + FMOVDP F0, 168(SP) + FMOVD F0, 176(SP) + FMULD ·SCALE(SB), F0 + FMOVDP F0, 184(SP) + FLDZ + FLDZ + FLDZ + FLDZ + CMPQ DX,$16 + JB ADDATMOST15BYTES + INITIALATLEAST16BYTES: + MOVL 12(SI),DI + MOVL 8(SI),CX + MOVL 4(SI),R8 + MOVL 0(SI),R9 + MOVL DI,128(SP) + MOVL CX,120(SP) + MOVL R8,112(SP) + MOVL R9,104(SP) + ADDQ $16,SI + SUBQ $16,DX + FXCHD F0, F3 + FADDD 128(SP), F0 + FSUBD ·DOFFSET3MINUSTWO128(SB), F0 + FXCHD F0, F1 + FADDD 112(SP), F0 + FSUBD ·DOFFSET1(SB), F0 + FXCHD F0, F2 + FADDD 120(SP), F0 + FSUBD ·DOFFSET2(SB), F0 + FXCHD F0, F3 + FADDD 104(SP), F0 + FSUBD ·DOFFSET0(SB), F0 + CMPQ DX,$16 + JB MULTIPLYADDATMOST15BYTES + MULTIPLYADDATLEAST16BYTES: + MOVL 12(SI),DI + MOVL 8(SI),CX + MOVL 4(SI),R8 + MOVL 0(SI),R9 + MOVL DI,128(SP) + MOVL CX,120(SP) + MOVL R8,112(SP) + MOVL R9,104(SP) + ADDQ $16,SI + SUBQ $16,DX + FMOVD ·ALPHA130(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA130(SB), F0 + FSUBD F0,F2 + FMULD ·SCALE(SB), F0 + FMOVD ·ALPHA32(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA32(SB), F0 + FSUBD F0,F2 + FXCHD F0, F2 + FADDDP F0,F1 + FMOVD ·ALPHA64(SB), F0 + FADDD F4,F0 + FSUBD ·ALPHA64(SB), F0 + FSUBD F0,F4 + FMOVD ·ALPHA96(SB), F0 + FADDD F6,F0 + FSUBD ·ALPHA96(SB), F0 + FSUBD F0,F6 + FXCHD F0, F6 + FADDDP F0,F1 + FXCHD F0, F3 + FADDDP F0,F5 + FXCHD F0, F3 + FADDDP F0,F1 + FMOVD 176(SP), F0 + FMULD F3,F0 + FMOVD 160(SP), F0 + FMULD F4,F0 + FMOVD 144(SP), F0 + FMULD F5,F0 + FMOVD 136(SP), F0 + FMULDP F0,F6 + FMOVD 160(SP), F0 + FMULD F4,F0 + FADDDP F0,F3 + FMOVD 144(SP), F0 + FMULD F4,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F4,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULDP F0,F4 + FXCHD F0, F3 + FADDDP F0,F5 + FMOVD 144(SP), F0 + FMULD F4,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F4,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULD F4,F0 + FADDDP F0,F3 + FMOVD 168(SP), F0 + FMULDP F0,F4 + FXCHD F0, F3 + FADDDP F0,F4 + FMOVD 136(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FXCHD F0, F3 + FMOVD 184(SP), F0 + FMULD F5,F0 + FADDDP F0,F3 + FXCHD F0, F1 + FMOVD 168(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FMOVD 152(SP), F0 + FMULDP F0,F5 + FXCHD F0, F4 + FADDDP F0,F1 + CMPQ DX,$16 + FXCHD F0, F2 + FMOVD 128(SP), F0 + FSUBD ·DOFFSET3MINUSTWO128(SB), F0 + FADDDP F0,F1 + FXCHD F0, F1 + FMOVD 120(SP), F0 + FSUBD ·DOFFSET2(SB), F0 + FADDDP F0,F1 + FXCHD F0, F3 + FMOVD 112(SP), F0 + FSUBD ·DOFFSET1(SB), F0 + FADDDP F0,F1 + FXCHD F0, F2 + FMOVD 104(SP), F0 + FSUBD ·DOFFSET0(SB), F0 + FADDDP F0,F1 + JAE MULTIPLYADDATLEAST16BYTES + MULTIPLYADDATMOST15BYTES: + FMOVD ·ALPHA130(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA130(SB), F0 + FSUBD F0,F2 + FMULD ·SCALE(SB), F0 + FMOVD ·ALPHA32(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA32(SB), F0 + FSUBD F0,F2 + FMOVD ·ALPHA64(SB), F0 + FADDD F5,F0 + FSUBD ·ALPHA64(SB), F0 + FSUBD F0,F5 + FMOVD ·ALPHA96(SB), F0 + FADDD F7,F0 + FSUBD ·ALPHA96(SB), F0 + FSUBD F0,F7 + FXCHD F0, F7 + FADDDP F0,F1 + FXCHD F0, F5 + FADDDP F0,F1 + FXCHD F0, F3 + FADDDP F0,F5 + FADDDP F0,F1 + FMOVD 176(SP), F0 + FMULD F1,F0 + FMOVD 160(SP), F0 + FMULD F2,F0 + FMOVD 144(SP), F0 + FMULD F3,F0 + FMOVD 136(SP), F0 + FMULDP F0,F4 + FMOVD 160(SP), F0 + FMULD F5,F0 + FADDDP F0,F3 + FMOVD 144(SP), F0 + FMULD F5,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULDP F0,F5 + FXCHD F0, F4 + FADDDP F0,F3 + FMOVD 144(SP), F0 + FMULD F5,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULD F5,F0 + FADDDP F0,F4 + FMOVD 168(SP), F0 + FMULDP F0,F5 + FXCHD F0, F4 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULD F5,F0 + FADDDP F0,F4 + FMOVD 168(SP), F0 + FMULD F5,F0 + FADDDP F0,F3 + FMOVD 152(SP), F0 + FMULDP F0,F5 + FXCHD F0, F4 + FADDDP F0,F1 + ADDATMOST15BYTES: + CMPQ DX,$0 + JE NOMOREBYTES + MOVL $0,0(SP) + MOVL $0, 4 (SP) + MOVL $0, 8 (SP) + MOVL $0, 12 (SP) + LEAQ 0(SP),DI + MOVQ DX,CX + REP; MOVSB + MOVB $1,0(DI) + MOVL 12 (SP),DI + MOVL 8 (SP),SI + MOVL 4 (SP),DX + MOVL 0(SP),CX + MOVL DI,128(SP) + MOVL SI,120(SP) + MOVL DX,112(SP) + MOVL CX,104(SP) + FXCHD F0, F3 + FADDD 128(SP), F0 + FSUBD ·DOFFSET3(SB), F0 + FXCHD F0, F2 + FADDD 120(SP), F0 + FSUBD ·DOFFSET2(SB), F0 + FXCHD F0, F1 + FADDD 112(SP), F0 + FSUBD ·DOFFSET1(SB), F0 + FXCHD F0, F3 + FADDD 104(SP), F0 + FSUBD ·DOFFSET0(SB), F0 + FMOVD ·ALPHA130(SB), F0 + FADDD F3,F0 + FSUBD ·ALPHA130(SB), F0 + FSUBD F0,F3 + FMULD ·SCALE(SB), F0 + FMOVD ·ALPHA32(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA32(SB), F0 + FSUBD F0,F2 + FMOVD ·ALPHA64(SB), F0 + FADDD F6,F0 + FSUBD ·ALPHA64(SB), F0 + FSUBD F0,F6 + FMOVD ·ALPHA96(SB), F0 + FADDD F5,F0 + FSUBD ·ALPHA96(SB), F0 + FSUBD F0,F5 + FXCHD F0, F4 + FADDDP F0,F3 + FXCHD F0, F6 + FADDDP F0,F1 + FXCHD F0, F3 + FADDDP F0,F5 + FXCHD F0, F3 + FADDDP F0,F1 + FMOVD 176(SP), F0 + FMULD F3,F0 + FMOVD 160(SP), F0 + FMULD F4,F0 + FMOVD 144(SP), F0 + FMULD F5,F0 + FMOVD 136(SP), F0 + FMULDP F0,F6 + FMOVD 160(SP), F0 + FMULD F5,F0 + FADDDP F0,F3 + FMOVD 144(SP), F0 + FMULD F5,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F5,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULDP F0,F5 + FXCHD F0, F4 + FADDDP F0,F5 + FMOVD 144(SP), F0 + FMULD F6,F0 + FADDDP F0,F2 + FMOVD 136(SP), F0 + FMULD F6,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULD F6,F0 + FADDDP F0,F4 + FMOVD 168(SP), F0 + FMULDP F0,F6 + FXCHD F0, F5 + FADDDP F0,F4 + FMOVD 136(SP), F0 + FMULD F2,F0 + FADDDP F0,F1 + FMOVD 184(SP), F0 + FMULD F2,F0 + FADDDP F0,F5 + FMOVD 168(SP), F0 + FMULD F2,F0 + FADDDP F0,F3 + FMOVD 152(SP), F0 + FMULDP F0,F2 + FXCHD F0, F1 + FADDDP F0,F3 + FXCHD F0, F3 + FXCHD F0, F2 + NOMOREBYTES: + MOVL $0,R10 + FMOVD ·ALPHA130(SB), F0 + FADDD F4,F0 + FSUBD ·ALPHA130(SB), F0 + FSUBD F0,F4 + FMULD ·SCALE(SB), F0 + FMOVD ·ALPHA32(SB), F0 + FADDD F2,F0 + FSUBD ·ALPHA32(SB), F0 + FSUBD F0,F2 + FMOVD ·ALPHA64(SB), F0 + FADDD F4,F0 + FSUBD ·ALPHA64(SB), F0 + FSUBD F0,F4 + FMOVD ·ALPHA96(SB), F0 + FADDD F6,F0 + FSUBD ·ALPHA96(SB), F0 + FXCHD F0, F6 + FSUBD F6,F0 + FXCHD F0, F4 + FADDDP F0,F3 + FXCHD F0, F4 + FADDDP F0,F1 + FXCHD F0, F2 + FADDDP F0,F3 + FXCHD F0, F4 + FADDDP F0,F3 + FXCHD F0, F3 + FADDD ·HOFFSET0(SB), F0 + FXCHD F0, F3 + FADDD ·HOFFSET1(SB), F0 + FXCHD F0, F1 + FADDD ·HOFFSET2(SB), F0 + FXCHD F0, F2 + FADDD ·HOFFSET3(SB), F0 + FXCHD F0, F3 + FMOVDP F0, 104(SP) + FMOVDP F0, 112(SP) + FMOVDP F0, 120(SP) + FMOVDP F0, 128(SP) + MOVL 108(SP),DI + ANDL $63,DI + MOVL 116(SP),SI + ANDL $63,SI + MOVL 124(SP),DX + ANDL $63,DX + MOVL 132(SP),CX + ANDL $63,CX + MOVL 112(SP),R8 + ADDL DI,R8 + MOVQ R8,112(SP) + MOVL 120(SP),DI + ADCL SI,DI + MOVQ DI,120(SP) + MOVL 128(SP),DI + ADCL DX,DI + MOVQ DI,128(SP) + MOVL R10,DI + ADCL CX,DI + MOVQ DI,136(SP) + MOVQ $5,DI + MOVL 104(SP),SI + ADDL SI,DI + MOVQ DI,104(SP) + MOVL R10,DI + MOVQ 112(SP),DX + ADCL DX,DI + MOVQ DI,112(SP) + MOVL R10,DI + MOVQ 120(SP),CX + ADCL CX,DI + MOVQ DI,120(SP) + MOVL R10,DI + MOVQ 128(SP),R8 + ADCL R8,DI + MOVQ DI,128(SP) + MOVQ $0XFFFFFFFC,DI + MOVQ 136(SP),R9 + ADCL R9,DI + SARL $16,DI + MOVQ DI,R9 + XORL $0XFFFFFFFF,R9 + ANDQ DI,SI + MOVQ 104(SP),AX + ANDQ R9,AX + ORQ AX,SI + ANDQ DI,DX + MOVQ 112(SP),AX + ANDQ R9,AX + ORQ AX,DX + ANDQ DI,CX + MOVQ 120(SP),AX + ANDQ R9,AX + ORQ AX,CX + ANDQ DI,R8 + MOVQ 128(SP),DI + ANDQ R9,DI + ORQ DI,R8 + MOVQ 88(SP),DI + MOVQ 96(SP),R9 + ADDL 16(R9),SI + ADCL 20(R9),DX + ADCL 24(R9),CX + ADCL 28(R9),R8 + MOVL SI,0(DI) + MOVL DX,4(DI) + MOVL CX,8(DI) + MOVL R8,12(DI) + MOVQ 32(SP),R11 + MOVQ 40(SP),R12 + MOVQ 48(SP),R13 + MOVQ 56(SP),R14 + MOVQ 64(SP),R15 + MOVQ 72(SP),BX + MOVQ 80(SP),BP + MOVQ R11,SP + RET diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s b/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s new file mode 100644 index 00000000..c1538674 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s @@ -0,0 +1,379 @@ +// Copyright 2015 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. + +// This code was translated into a form compatible with 5a from the public +// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305. + +// +build arm,!gccgo,!appengine + +DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff +DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03 +DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff +DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff +DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff +GLOBL poly1305_init_constants_armv6<>(SB), 8, $20 + +// Warning: the linker may use R11 to synthesize certain instructions. Please +// take care and verify that no synthetic instructions use it. + +TEXT poly1305_init_ext_armv6<>(SB),4,$-4 + MOVM.DB.W [R4-R11], (R13) + MOVM.IA.W (R1), [R2-R5] + MOVW $poly1305_init_constants_armv6<>(SB), R7 + MOVW R2, R8 + MOVW R2>>26, R9 + MOVW R3>>20, g + MOVW R4>>14, R11 + MOVW R5>>8, R12 + ORR R3<<6, R9, R9 + ORR R4<<12, g, g + ORR R5<<18, R11, R11 + MOVM.IA (R7), [R2-R6] + AND R8, R2, R2 + AND R9, R3, R3 + AND g, R4, R4 + AND R11, R5, R5 + AND R12, R6, R6 + MOVM.IA.W [R2-R6], (R0) + EOR R2, R2, R2 + EOR R3, R3, R3 + EOR R4, R4, R4 + EOR R5, R5, R5 + EOR R6, R6, R6 + MOVM.IA.W [R2-R6], (R0) + MOVM.IA.W (R1), [R2-R5] + MOVM.IA [R2-R6], (R0) + MOVM.IA.W (R13), [R4-R11] + RET + +#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \ + MOVBU (offset+0)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+0)(Rdst); \ + MOVBU (offset+1)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+1)(Rdst); \ + MOVBU (offset+2)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+2)(Rdst); \ + MOVBU (offset+3)(Rsrc), Rtmp; \ + MOVBU Rtmp, (offset+3)(Rdst) + +TEXT poly1305_blocks_armv6<>(SB),4,$-4 + MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13) + SUB $128, R13 + MOVW R0, 36(R13) + MOVW R1, 40(R13) + MOVW R2, 44(R13) + MOVW R1, R14 + MOVW R2, R12 + MOVW 56(R0), R8 + WORD $0xe1180008 // TST R8, R8 not working see issue 5921 + EOR R6, R6, R6 + MOVW.EQ $(1<<24), R6 + MOVW R6, 32(R13) + ADD $64, R13, g + MOVM.IA (R0), [R0-R9] + MOVM.IA [R0-R4], (g) + CMP $16, R12 + BLO poly1305_blocks_armv6_done +poly1305_blocks_armv6_mainloop: + WORD $0xe31e0003 // TST R14, #3 not working see issue 5921 + BEQ poly1305_blocks_armv6_mainloop_aligned + ADD $48, R13, g + MOVW_UNALIGNED(R14, g, R0, 0) + MOVW_UNALIGNED(R14, g, R0, 4) + MOVW_UNALIGNED(R14, g, R0, 8) + MOVW_UNALIGNED(R14, g, R0, 12) + MOVM.IA (g), [R0-R3] + ADD $16, R14 + B poly1305_blocks_armv6_mainloop_loaded +poly1305_blocks_armv6_mainloop_aligned: + MOVM.IA.W (R14), [R0-R3] +poly1305_blocks_armv6_mainloop_loaded: + MOVW R0>>26, g + MOVW R1>>20, R11 + MOVW R2>>14, R12 + MOVW R14, 40(R13) + MOVW R3>>8, R4 + ORR R1<<6, g, g + ORR R2<<12, R11, R11 + ORR R3<<18, R12, R12 + BIC $0xfc000000, R0, R0 + BIC $0xfc000000, g, g + MOVW 32(R13), R3 + BIC $0xfc000000, R11, R11 + BIC $0xfc000000, R12, R12 + ADD R0, R5, R5 + ADD g, R6, R6 + ORR R3, R4, R4 + ADD R11, R7, R7 + ADD $64, R13, R14 + ADD R12, R8, R8 + ADD R4, R9, R9 + MOVM.IA (R14), [R0-R4] + MULLU R4, R5, (R11, g) + MULLU R3, R5, (R14, R12) + MULALU R3, R6, (R11, g) + MULALU R2, R6, (R14, R12) + MULALU R2, R7, (R11, g) + MULALU R1, R7, (R14, R12) + ADD R4<<2, R4, R4 + ADD R3<<2, R3, R3 + MULALU R1, R8, (R11, g) + MULALU R0, R8, (R14, R12) + MULALU R0, R9, (R11, g) + MULALU R4, R9, (R14, R12) + MOVW g, 24(R13) + MOVW R11, 28(R13) + MOVW R12, 16(R13) + MOVW R14, 20(R13) + MULLU R2, R5, (R11, g) + MULLU R1, R5, (R14, R12) + MULALU R1, R6, (R11, g) + MULALU R0, R6, (R14, R12) + MULALU R0, R7, (R11, g) + MULALU R4, R7, (R14, R12) + ADD R2<<2, R2, R2 + ADD R1<<2, R1, R1 + MULALU R4, R8, (R11, g) + MULALU R3, R8, (R14, R12) + MULALU R3, R9, (R11, g) + MULALU R2, R9, (R14, R12) + MOVW g, 8(R13) + MOVW R11, 12(R13) + MOVW R12, 0(R13) + MOVW R14, w+4(SP) + MULLU R0, R5, (R11, g) + MULALU R4, R6, (R11, g) + MULALU R3, R7, (R11, g) + MULALU R2, R8, (R11, g) + MULALU R1, R9, (R11, g) + MOVM.IA (R13), [R0-R7] + MOVW g>>26, R12 + MOVW R4>>26, R14 + ORR R11<<6, R12, R12 + ORR R5<<6, R14, R14 + BIC $0xfc000000, g, g + BIC $0xfc000000, R4, R4 + ADD.S R12, R0, R0 + ADC $0, R1, R1 + ADD.S R14, R6, R6 + ADC $0, R7, R7 + MOVW R0>>26, R12 + MOVW R6>>26, R14 + ORR R1<<6, R12, R12 + ORR R7<<6, R14, R14 + BIC $0xfc000000, R0, R0 + BIC $0xfc000000, R6, R6 + ADD R14<<2, R14, R14 + ADD.S R12, R2, R2 + ADC $0, R3, R3 + ADD R14, g, g + MOVW R2>>26, R12 + MOVW g>>26, R14 + ORR R3<<6, R12, R12 + BIC $0xfc000000, g, R5 + BIC $0xfc000000, R2, R7 + ADD R12, R4, R4 + ADD R14, R0, R0 + MOVW R4>>26, R12 + BIC $0xfc000000, R4, R8 + ADD R12, R6, R9 + MOVW w+44(SP), R12 + MOVW w+40(SP), R14 + MOVW R0, R6 + CMP $32, R12 + SUB $16, R12, R12 + MOVW R12, 44(R13) + BHS poly1305_blocks_armv6_mainloop +poly1305_blocks_armv6_done: + MOVW 36(R13), R12 + MOVW R5, 20(R12) + MOVW R6, 24(R12) + MOVW R7, 28(R12) + MOVW R8, 32(R12) + MOVW R9, 36(R12) + ADD $128, R13, R13 + MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14] + RET + +#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \ + MOVBU.P 1(Rsrc), Rtmp; \ + MOVBU.P Rtmp, 1(Rdst); \ + MOVBU.P 1(Rsrc), Rtmp; \ + MOVBU.P Rtmp, 1(Rdst) + +#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \ + MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \ + MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) + +TEXT poly1305_finish_ext_armv6<>(SB),4,$-4 + MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13) + SUB $16, R13, R13 + MOVW R0, R5 + MOVW R1, R6 + MOVW R2, R7 + MOVW R3, R8 + AND.S R2, R2, R2 + BEQ poly1305_finish_ext_armv6_noremaining + EOR R0, R0 + MOVW R13, R9 + MOVW R0, 0(R13) + MOVW R0, 4(R13) + MOVW R0, 8(R13) + MOVW R0, 12(R13) + WORD $0xe3110003 // TST R1, #3 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_aligned + WORD $0xe3120008 // TST R2, #8 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip8 + MOVWP_UNALIGNED(R1, R9, g) + MOVWP_UNALIGNED(R1, R9, g) +poly1305_finish_ext_armv6_skip8: + WORD $0xe3120004 // TST $4, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip4 + MOVWP_UNALIGNED(R1, R9, g) +poly1305_finish_ext_armv6_skip4: + WORD $0xe3120002 // TST $2, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip2 + MOVHUP_UNALIGNED(R1, R9, g) + B poly1305_finish_ext_armv6_skip2 +poly1305_finish_ext_armv6_aligned: + WORD $0xe3120008 // TST R2, #8 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip8_aligned + MOVM.IA.W (R1), [g-R11] + MOVM.IA.W [g-R11], (R9) +poly1305_finish_ext_armv6_skip8_aligned: + WORD $0xe3120004 // TST $4, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip4_aligned + MOVW.P 4(R1), g + MOVW.P g, 4(R9) +poly1305_finish_ext_armv6_skip4_aligned: + WORD $0xe3120002 // TST $2, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip2 + MOVHU.P 2(R1), g + MOVH.P g, 2(R9) +poly1305_finish_ext_armv6_skip2: + WORD $0xe3120001 // TST $1, R2 not working see issue 5921 + BEQ poly1305_finish_ext_armv6_skip1 + MOVBU.P 1(R1), g + MOVBU.P g, 1(R9) +poly1305_finish_ext_armv6_skip1: + MOVW $1, R11 + MOVBU R11, 0(R9) + MOVW R11, 56(R5) + MOVW R5, R0 + MOVW R13, R1 + MOVW $16, R2 + BL poly1305_blocks_armv6<>(SB) +poly1305_finish_ext_armv6_noremaining: + MOVW 20(R5), R0 + MOVW 24(R5), R1 + MOVW 28(R5), R2 + MOVW 32(R5), R3 + MOVW 36(R5), R4 + MOVW R4>>26, R12 + BIC $0xfc000000, R4, R4 + ADD R12<<2, R12, R12 + ADD R12, R0, R0 + MOVW R0>>26, R12 + BIC $0xfc000000, R0, R0 + ADD R12, R1, R1 + MOVW R1>>26, R12 + BIC $0xfc000000, R1, R1 + ADD R12, R2, R2 + MOVW R2>>26, R12 + BIC $0xfc000000, R2, R2 + ADD R12, R3, R3 + MOVW R3>>26, R12 + BIC $0xfc000000, R3, R3 + ADD R12, R4, R4 + ADD $5, R0, R6 + MOVW R6>>26, R12 + BIC $0xfc000000, R6, R6 + ADD R12, R1, R7 + MOVW R7>>26, R12 + BIC $0xfc000000, R7, R7 + ADD R12, R2, g + MOVW g>>26, R12 + BIC $0xfc000000, g, g + ADD R12, R3, R11 + MOVW $-(1<<26), R12 + ADD R11>>26, R12, R12 + BIC $0xfc000000, R11, R11 + ADD R12, R4, R14 + MOVW R14>>31, R12 + SUB $1, R12 + AND R12, R6, R6 + AND R12, R7, R7 + AND R12, g, g + AND R12, R11, R11 + AND R12, R14, R14 + MVN R12, R12 + AND R12, R0, R0 + AND R12, R1, R1 + AND R12, R2, R2 + AND R12, R3, R3 + AND R12, R4, R4 + ORR R6, R0, R0 + ORR R7, R1, R1 + ORR g, R2, R2 + ORR R11, R3, R3 + ORR R14, R4, R4 + ORR R1<<26, R0, R0 + MOVW R1>>6, R1 + ORR R2<<20, R1, R1 + MOVW R2>>12, R2 + ORR R3<<14, R2, R2 + MOVW R3>>18, R3 + ORR R4<<8, R3, R3 + MOVW 40(R5), R6 + MOVW 44(R5), R7 + MOVW 48(R5), g + MOVW 52(R5), R11 + ADD.S R6, R0, R0 + ADC.S R7, R1, R1 + ADC.S g, R2, R2 + ADC.S R11, R3, R3 + MOVM.IA [R0-R3], (R8) + MOVW R5, R12 + EOR R0, R0, R0 + EOR R1, R1, R1 + EOR R2, R2, R2 + EOR R3, R3, R3 + EOR R4, R4, R4 + EOR R5, R5, R5 + EOR R6, R6, R6 + EOR R7, R7, R7 + MOVM.IA.W [R0-R7], (R12) + MOVM.IA [R0-R7], (R12) + ADD $16, R13, R13 + MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14] + RET + +// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key) +TEXT ·poly1305_auth_armv6(SB),0,$280-16 + MOVW out+0(FP), R4 + MOVW m+4(FP), R5 + MOVW mlen+8(FP), R6 + MOVW key+12(FP), R7 + + MOVW R13, R8 + BIC $63, R13 + SUB $64, R13, R13 + MOVW R13, R0 + MOVW R7, R1 + BL poly1305_init_ext_armv6<>(SB) + BIC.S $15, R6, R2 + BEQ poly1305_auth_armv6_noblocks + MOVW R13, R0 + MOVW R5, R1 + ADD R2, R5, R5 + SUB R2, R6, R6 + BL poly1305_blocks_armv6<>(SB) +poly1305_auth_armv6_noblocks: + MOVW R13, R0 + MOVW R5, R1 + MOVW R6, R2 + MOVW R4, R3 + BL poly1305_finish_ext_armv6<>(SB) + MOVW R8, R13 + RET diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go new file mode 100644 index 00000000..6775c703 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go @@ -0,0 +1,24 @@ +// Copyright 2012 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. + +// +build amd64,!gccgo,!appengine + +package poly1305 + +// This function is implemented in poly1305_amd64.s + +//go:noescape + +func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305(out, mPtr, uint64(len(m)), key) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_arm.go b/vendor/golang.org/x/crypto/poly1305/sum_arm.go new file mode 100644 index 00000000..50b979c2 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_arm.go @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +// +build arm,!gccgo,!appengine + +package poly1305 + +// This function is implemented in poly1305_arm.s + +//go:noescape + +func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305_auth_armv6(out, mPtr, uint32(len(m)), key) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ref.go b/vendor/golang.org/x/crypto/poly1305/sum_ref.go new file mode 100644 index 00000000..0b24fc78 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_ref.go @@ -0,0 +1,1531 @@ +// Copyright 2012 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. + +// +build !amd64,!arm gccgo appengine + +package poly1305 + +// Based on original, public domain implementation from NaCl by D. J. +// Bernstein. + +import "math" + +const ( + alpham80 = 0.00000000558793544769287109375 + alpham48 = 24.0 + alpham16 = 103079215104.0 + alpha0 = 6755399441055744.0 + alpha18 = 1770887431076116955136.0 + alpha32 = 29014219670751100192948224.0 + alpha50 = 7605903601369376408980219232256.0 + alpha64 = 124615124604835863084731911901282304.0 + alpha82 = 32667107224410092492483962313449748299776.0 + alpha96 = 535217884764734955396857238543560676143529984.0 + alpha112 = 35076039295941670036888435985190792471742381031424.0 + alpha130 = 9194973245195333150150082162901855101712434733101613056.0 + scale = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125 + offset0 = 6755408030990331.0 + offset1 = 29014256564239239022116864.0 + offset2 = 124615283061160854719918951570079744.0 + offset3 = 535219245894202480694386063513315216128475136.0 +) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + r := key + s := key[16:] + var ( + y7 float64 + y6 float64 + y1 float64 + y0 float64 + y5 float64 + y4 float64 + x7 float64 + x6 float64 + x1 float64 + x0 float64 + y3 float64 + y2 float64 + x5 float64 + r3lowx0 float64 + x4 float64 + r0lowx6 float64 + x3 float64 + r3highx0 float64 + x2 float64 + r0highx6 float64 + r0lowx0 float64 + sr1lowx6 float64 + r0highx0 float64 + sr1highx6 float64 + sr3low float64 + r1lowx0 float64 + sr2lowx6 float64 + r1highx0 float64 + sr2highx6 float64 + r2lowx0 float64 + sr3lowx6 float64 + r2highx0 float64 + sr3highx6 float64 + r1highx4 float64 + r1lowx4 float64 + r0highx4 float64 + r0lowx4 float64 + sr3highx4 float64 + sr3lowx4 float64 + sr2highx4 float64 + sr2lowx4 float64 + r0lowx2 float64 + r0highx2 float64 + r1lowx2 float64 + r1highx2 float64 + r2lowx2 float64 + r2highx2 float64 + sr3lowx2 float64 + sr3highx2 float64 + z0 float64 + z1 float64 + z2 float64 + z3 float64 + m0 int64 + m1 int64 + m2 int64 + m3 int64 + m00 uint32 + m01 uint32 + m02 uint32 + m03 uint32 + m10 uint32 + m11 uint32 + m12 uint32 + m13 uint32 + m20 uint32 + m21 uint32 + m22 uint32 + m23 uint32 + m30 uint32 + m31 uint32 + m32 uint32 + m33 uint64 + lbelow2 int32 + lbelow3 int32 + lbelow4 int32 + lbelow5 int32 + lbelow6 int32 + lbelow7 int32 + lbelow8 int32 + lbelow9 int32 + lbelow10 int32 + lbelow11 int32 + lbelow12 int32 + lbelow13 int32 + lbelow14 int32 + lbelow15 int32 + s00 uint32 + s01 uint32 + s02 uint32 + s03 uint32 + s10 uint32 + s11 uint32 + s12 uint32 + s13 uint32 + s20 uint32 + s21 uint32 + s22 uint32 + s23 uint32 + s30 uint32 + s31 uint32 + s32 uint32 + s33 uint32 + bits32 uint64 + f uint64 + f0 uint64 + f1 uint64 + f2 uint64 + f3 uint64 + f4 uint64 + g uint64 + g0 uint64 + g1 uint64 + g2 uint64 + g3 uint64 + g4 uint64 + ) + + var p int32 + + l := int32(len(m)) + + r00 := uint32(r[0]) + + r01 := uint32(r[1]) + + r02 := uint32(r[2]) + r0 := int64(2151) + + r03 := uint32(r[3]) + r03 &= 15 + r0 <<= 51 + + r10 := uint32(r[4]) + r10 &= 252 + r01 <<= 8 + r0 += int64(r00) + + r11 := uint32(r[5]) + r02 <<= 16 + r0 += int64(r01) + + r12 := uint32(r[6]) + r03 <<= 24 + r0 += int64(r02) + + r13 := uint32(r[7]) + r13 &= 15 + r1 := int64(2215) + r0 += int64(r03) + + d0 := r0 + r1 <<= 51 + r2 := int64(2279) + + r20 := uint32(r[8]) + r20 &= 252 + r11 <<= 8 + r1 += int64(r10) + + r21 := uint32(r[9]) + r12 <<= 16 + r1 += int64(r11) + + r22 := uint32(r[10]) + r13 <<= 24 + r1 += int64(r12) + + r23 := uint32(r[11]) + r23 &= 15 + r2 <<= 51 + r1 += int64(r13) + + d1 := r1 + r21 <<= 8 + r2 += int64(r20) + + r30 := uint32(r[12]) + r30 &= 252 + r22 <<= 16 + r2 += int64(r21) + + r31 := uint32(r[13]) + r23 <<= 24 + r2 += int64(r22) + + r32 := uint32(r[14]) + r2 += int64(r23) + r3 := int64(2343) + + d2 := r2 + r3 <<= 51 + + r33 := uint32(r[15]) + r33 &= 15 + r31 <<= 8 + r3 += int64(r30) + + r32 <<= 16 + r3 += int64(r31) + + r33 <<= 24 + r3 += int64(r32) + + r3 += int64(r33) + h0 := alpha32 - alpha32 + + d3 := r3 + h1 := alpha32 - alpha32 + + h2 := alpha32 - alpha32 + + h3 := alpha32 - alpha32 + + h4 := alpha32 - alpha32 + + r0low := math.Float64frombits(uint64(d0)) + h5 := alpha32 - alpha32 + + r1low := math.Float64frombits(uint64(d1)) + h6 := alpha32 - alpha32 + + r2low := math.Float64frombits(uint64(d2)) + h7 := alpha32 - alpha32 + + r0low -= alpha0 + + r1low -= alpha32 + + r2low -= alpha64 + + r0high := r0low + alpha18 + + r3low := math.Float64frombits(uint64(d3)) + + r1high := r1low + alpha50 + sr1low := scale * r1low + + r2high := r2low + alpha82 + sr2low := scale * r2low + + r0high -= alpha18 + r0high_stack := r0high + + r3low -= alpha96 + + r1high -= alpha50 + r1high_stack := r1high + + sr1high := sr1low + alpham80 + + r0low -= r0high + + r2high -= alpha82 + sr3low = scale * r3low + + sr2high := sr2low + alpham48 + + r1low -= r1high + r1low_stack := r1low + + sr1high -= alpham80 + sr1high_stack := sr1high + + r2low -= r2high + r2low_stack := r2low + + sr2high -= alpham48 + sr2high_stack := sr2high + + r3high := r3low + alpha112 + r0low_stack := r0low + + sr1low -= sr1high + sr1low_stack := sr1low + + sr3high := sr3low + alpham16 + r2high_stack := r2high + + sr2low -= sr2high + sr2low_stack := sr2low + + r3high -= alpha112 + r3high_stack := r3high + + sr3high -= alpham16 + sr3high_stack := sr3high + + r3low -= r3high + r3low_stack := r3low + + sr3low -= sr3high + sr3low_stack := sr3low + + if l < 16 { + goto addatmost15bytes + } + + m00 = uint32(m[p+0]) + m0 = 2151 + + m0 <<= 51 + m1 = 2215 + m01 = uint32(m[p+1]) + + m1 <<= 51 + m2 = 2279 + m02 = uint32(m[p+2]) + + m2 <<= 51 + m3 = 2343 + m03 = uint32(m[p+3]) + + m10 = uint32(m[p+4]) + m01 <<= 8 + m0 += int64(m00) + + m11 = uint32(m[p+5]) + m02 <<= 16 + m0 += int64(m01) + + m12 = uint32(m[p+6]) + m03 <<= 24 + m0 += int64(m02) + + m13 = uint32(m[p+7]) + m3 <<= 51 + m0 += int64(m03) + + m20 = uint32(m[p+8]) + m11 <<= 8 + m1 += int64(m10) + + m21 = uint32(m[p+9]) + m12 <<= 16 + m1 += int64(m11) + + m22 = uint32(m[p+10]) + m13 <<= 24 + m1 += int64(m12) + + m23 = uint32(m[p+11]) + m1 += int64(m13) + + m30 = uint32(m[p+12]) + m21 <<= 8 + m2 += int64(m20) + + m31 = uint32(m[p+13]) + m22 <<= 16 + m2 += int64(m21) + + m32 = uint32(m[p+14]) + m23 <<= 24 + m2 += int64(m22) + + m33 = uint64(m[p+15]) + m2 += int64(m23) + + d0 = m0 + m31 <<= 8 + m3 += int64(m30) + + d1 = m1 + m32 <<= 16 + m3 += int64(m31) + + d2 = m2 + m33 += 256 + + m33 <<= 24 + m3 += int64(m32) + + m3 += int64(m33) + d3 = m3 + + p += 16 + l -= 16 + + z0 = math.Float64frombits(uint64(d0)) + + z1 = math.Float64frombits(uint64(d1)) + + z2 = math.Float64frombits(uint64(d2)) + + z3 = math.Float64frombits(uint64(d3)) + + z0 -= alpha0 + + z1 -= alpha32 + + z2 -= alpha64 + + z3 -= alpha96 + + h0 += z0 + + h1 += z1 + + h3 += z2 + + h5 += z3 + + if l < 16 { + goto multiplyaddatmost15bytes + } + +multiplyaddatleast16bytes: + + m2 = 2279 + m20 = uint32(m[p+8]) + y7 = h7 + alpha130 + + m2 <<= 51 + m3 = 2343 + m21 = uint32(m[p+9]) + y6 = h6 + alpha130 + + m3 <<= 51 + m0 = 2151 + m22 = uint32(m[p+10]) + y1 = h1 + alpha32 + + m0 <<= 51 + m1 = 2215 + m23 = uint32(m[p+11]) + y0 = h0 + alpha32 + + m1 <<= 51 + m30 = uint32(m[p+12]) + y7 -= alpha130 + + m21 <<= 8 + m2 += int64(m20) + m31 = uint32(m[p+13]) + y6 -= alpha130 + + m22 <<= 16 + m2 += int64(m21) + m32 = uint32(m[p+14]) + y1 -= alpha32 + + m23 <<= 24 + m2 += int64(m22) + m33 = uint64(m[p+15]) + y0 -= alpha32 + + m2 += int64(m23) + m00 = uint32(m[p+0]) + y5 = h5 + alpha96 + + m31 <<= 8 + m3 += int64(m30) + m01 = uint32(m[p+1]) + y4 = h4 + alpha96 + + m32 <<= 16 + m02 = uint32(m[p+2]) + x7 = h7 - y7 + y7 *= scale + + m33 += 256 + m03 = uint32(m[p+3]) + x6 = h6 - y6 + y6 *= scale + + m33 <<= 24 + m3 += int64(m31) + m10 = uint32(m[p+4]) + x1 = h1 - y1 + + m01 <<= 8 + m3 += int64(m32) + m11 = uint32(m[p+5]) + x0 = h0 - y0 + + m3 += int64(m33) + m0 += int64(m00) + m12 = uint32(m[p+6]) + y5 -= alpha96 + + m02 <<= 16 + m0 += int64(m01) + m13 = uint32(m[p+7]) + y4 -= alpha96 + + m03 <<= 24 + m0 += int64(m02) + d2 = m2 + x1 += y7 + + m0 += int64(m03) + d3 = m3 + x0 += y6 + + m11 <<= 8 + m1 += int64(m10) + d0 = m0 + x7 += y5 + + m12 <<= 16 + m1 += int64(m11) + x6 += y4 + + m13 <<= 24 + m1 += int64(m12) + y3 = h3 + alpha64 + + m1 += int64(m13) + d1 = m1 + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + z2 = math.Float64frombits(uint64(d2)) + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + z3 = math.Float64frombits(uint64(d3)) + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + z2 -= alpha64 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + z3 -= alpha96 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + p += 16 + l -= 16 + h6 += r2lowx2 + + h7 += r2highx2 + + z1 = math.Float64frombits(uint64(d1)) + h0 += sr3lowx2 + + z0 = math.Float64frombits(uint64(d0)) + h1 += sr3highx2 + + z1 -= alpha32 + + z0 -= alpha0 + + h5 += z3 + + h3 += z2 + + h1 += z1 + + h0 += z0 + + if l >= 16 { + goto multiplyaddatleast16bytes + } + +multiplyaddatmost15bytes: + + y7 = h7 + alpha130 + + y6 = h6 + alpha130 + + y1 = h1 + alpha32 + + y0 = h0 + alpha32 + + y7 -= alpha130 + + y6 -= alpha130 + + y1 -= alpha32 + + y0 -= alpha32 + + y5 = h5 + alpha96 + + y4 = h4 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + x6 = h6 - y6 + y6 *= scale + + x1 = h1 - y1 + + x0 = h0 - y0 + + y5 -= alpha96 + + y4 -= alpha96 + + x1 += y7 + + x0 += y6 + + x7 += y5 + + x6 += y4 + + y3 = h3 + alpha64 + + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + h6 += r2lowx2 + + h7 += r2highx2 + + h0 += sr3lowx2 + + h1 += sr3highx2 + +addatmost15bytes: + + if l == 0 { + goto nomorebytes + } + + lbelow2 = l - 2 + + lbelow3 = l - 3 + + lbelow2 >>= 31 + lbelow4 = l - 4 + + m00 = uint32(m[p+0]) + lbelow3 >>= 31 + p += lbelow2 + + m01 = uint32(m[p+1]) + lbelow4 >>= 31 + p += lbelow3 + + m02 = uint32(m[p+2]) + p += lbelow4 + m0 = 2151 + + m03 = uint32(m[p+3]) + m0 <<= 51 + m1 = 2215 + + m0 += int64(m00) + m01 &^= uint32(lbelow2) + + m02 &^= uint32(lbelow3) + m01 -= uint32(lbelow2) + + m01 <<= 8 + m03 &^= uint32(lbelow4) + + m0 += int64(m01) + lbelow2 -= lbelow3 + + m02 += uint32(lbelow2) + lbelow3 -= lbelow4 + + m02 <<= 16 + m03 += uint32(lbelow3) + + m03 <<= 24 + m0 += int64(m02) + + m0 += int64(m03) + lbelow5 = l - 5 + + lbelow6 = l - 6 + lbelow7 = l - 7 + + lbelow5 >>= 31 + lbelow8 = l - 8 + + lbelow6 >>= 31 + p += lbelow5 + + m10 = uint32(m[p+4]) + lbelow7 >>= 31 + p += lbelow6 + + m11 = uint32(m[p+5]) + lbelow8 >>= 31 + p += lbelow7 + + m12 = uint32(m[p+6]) + m1 <<= 51 + p += lbelow8 + + m13 = uint32(m[p+7]) + m10 &^= uint32(lbelow5) + lbelow4 -= lbelow5 + + m10 += uint32(lbelow4) + lbelow5 -= lbelow6 + + m11 &^= uint32(lbelow6) + m11 += uint32(lbelow5) + + m11 <<= 8 + m1 += int64(m10) + + m1 += int64(m11) + m12 &^= uint32(lbelow7) + + lbelow6 -= lbelow7 + m13 &^= uint32(lbelow8) + + m12 += uint32(lbelow6) + lbelow7 -= lbelow8 + + m12 <<= 16 + m13 += uint32(lbelow7) + + m13 <<= 24 + m1 += int64(m12) + + m1 += int64(m13) + m2 = 2279 + + lbelow9 = l - 9 + m3 = 2343 + + lbelow10 = l - 10 + lbelow11 = l - 11 + + lbelow9 >>= 31 + lbelow12 = l - 12 + + lbelow10 >>= 31 + p += lbelow9 + + m20 = uint32(m[p+8]) + lbelow11 >>= 31 + p += lbelow10 + + m21 = uint32(m[p+9]) + lbelow12 >>= 31 + p += lbelow11 + + m22 = uint32(m[p+10]) + m2 <<= 51 + p += lbelow12 + + m23 = uint32(m[p+11]) + m20 &^= uint32(lbelow9) + lbelow8 -= lbelow9 + + m20 += uint32(lbelow8) + lbelow9 -= lbelow10 + + m21 &^= uint32(lbelow10) + m21 += uint32(lbelow9) + + m21 <<= 8 + m2 += int64(m20) + + m2 += int64(m21) + m22 &^= uint32(lbelow11) + + lbelow10 -= lbelow11 + m23 &^= uint32(lbelow12) + + m22 += uint32(lbelow10) + lbelow11 -= lbelow12 + + m22 <<= 16 + m23 += uint32(lbelow11) + + m23 <<= 24 + m2 += int64(m22) + + m3 <<= 51 + lbelow13 = l - 13 + + lbelow13 >>= 31 + lbelow14 = l - 14 + + lbelow14 >>= 31 + p += lbelow13 + lbelow15 = l - 15 + + m30 = uint32(m[p+12]) + lbelow15 >>= 31 + p += lbelow14 + + m31 = uint32(m[p+13]) + p += lbelow15 + m2 += int64(m23) + + m32 = uint32(m[p+14]) + m30 &^= uint32(lbelow13) + lbelow12 -= lbelow13 + + m30 += uint32(lbelow12) + lbelow13 -= lbelow14 + + m3 += int64(m30) + m31 &^= uint32(lbelow14) + + m31 += uint32(lbelow13) + m32 &^= uint32(lbelow15) + + m31 <<= 8 + lbelow14 -= lbelow15 + + m3 += int64(m31) + m32 += uint32(lbelow14) + d0 = m0 + + m32 <<= 16 + m33 = uint64(lbelow15 + 1) + d1 = m1 + + m33 <<= 24 + m3 += int64(m32) + d2 = m2 + + m3 += int64(m33) + d3 = m3 + + z3 = math.Float64frombits(uint64(d3)) + + z2 = math.Float64frombits(uint64(d2)) + + z1 = math.Float64frombits(uint64(d1)) + + z0 = math.Float64frombits(uint64(d0)) + + z3 -= alpha96 + + z2 -= alpha64 + + z1 -= alpha32 + + z0 -= alpha0 + + h5 += z3 + + h3 += z2 + + h1 += z1 + + h0 += z0 + + y7 = h7 + alpha130 + + y6 = h6 + alpha130 + + y1 = h1 + alpha32 + + y0 = h0 + alpha32 + + y7 -= alpha130 + + y6 -= alpha130 + + y1 -= alpha32 + + y0 -= alpha32 + + y5 = h5 + alpha96 + + y4 = h4 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + x6 = h6 - y6 + y6 *= scale + + x1 = h1 - y1 + + x0 = h0 - y0 + + y5 -= alpha96 + + y4 -= alpha96 + + x1 += y7 + + x0 += y6 + + x7 += y5 + + x6 += y4 + + y3 = h3 + alpha64 + + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + h6 += r2lowx2 + + h7 += r2highx2 + + h0 += sr3lowx2 + + h1 += sr3highx2 + +nomorebytes: + + y7 = h7 + alpha130 + + y0 = h0 + alpha32 + + y1 = h1 + alpha32 + + y2 = h2 + alpha64 + + y7 -= alpha130 + + y3 = h3 + alpha64 + + y4 = h4 + alpha96 + + y5 = h5 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + y0 -= alpha32 + + y1 -= alpha32 + + y2 -= alpha64 + + h6 += x7 + + y3 -= alpha64 + + y4 -= alpha96 + + y5 -= alpha96 + + y6 = h6 + alpha130 + + x0 = h0 - y0 + + x1 = h1 - y1 + + x2 = h2 - y2 + + y6 -= alpha130 + + x0 += y7 + + x3 = h3 - y3 + + x4 = h4 - y4 + + x5 = h5 - y5 + + x6 = h6 - y6 + + y6 *= scale + + x2 += y0 + + x3 += y1 + + x4 += y2 + + x0 += y6 + + x5 += y3 + + x6 += y4 + + x2 += x3 + + x0 += x1 + + x4 += x5 + + x6 += y5 + + x2 += offset1 + d1 = int64(math.Float64bits(x2)) + + x0 += offset0 + d0 = int64(math.Float64bits(x0)) + + x4 += offset2 + d2 = int64(math.Float64bits(x4)) + + x6 += offset3 + d3 = int64(math.Float64bits(x6)) + + f0 = uint64(d0) + + f1 = uint64(d1) + bits32 = math.MaxUint64 + + f2 = uint64(d2) + bits32 >>= 32 + + f3 = uint64(d3) + f = f0 >> 32 + + f0 &= bits32 + f &= 255 + + f1 += f + g0 = f0 + 5 + + g = g0 >> 32 + g0 &= bits32 + + f = f1 >> 32 + f1 &= bits32 + + f &= 255 + g1 = f1 + g + + g = g1 >> 32 + f2 += f + + f = f2 >> 32 + g1 &= bits32 + + f2 &= bits32 + f &= 255 + + f3 += f + g2 = f2 + g + + g = g2 >> 32 + g2 &= bits32 + + f4 = f3 >> 32 + f3 &= bits32 + + f4 &= 255 + g3 = f3 + g + + g = g3 >> 32 + g3 &= bits32 + + g4 = f4 + g + + g4 = g4 - 4 + s00 = uint32(s[0]) + + f = uint64(int64(g4) >> 63) + s01 = uint32(s[1]) + + f0 &= f + g0 &^= f + s02 = uint32(s[2]) + + f1 &= f + f0 |= g0 + s03 = uint32(s[3]) + + g1 &^= f + f2 &= f + s10 = uint32(s[4]) + + f3 &= f + g2 &^= f + s11 = uint32(s[5]) + + g3 &^= f + f1 |= g1 + s12 = uint32(s[6]) + + f2 |= g2 + f3 |= g3 + s13 = uint32(s[7]) + + s01 <<= 8 + f0 += uint64(s00) + s20 = uint32(s[8]) + + s02 <<= 16 + f0 += uint64(s01) + s21 = uint32(s[9]) + + s03 <<= 24 + f0 += uint64(s02) + s22 = uint32(s[10]) + + s11 <<= 8 + f1 += uint64(s10) + s23 = uint32(s[11]) + + s12 <<= 16 + f1 += uint64(s11) + s30 = uint32(s[12]) + + s13 <<= 24 + f1 += uint64(s12) + s31 = uint32(s[13]) + + f0 += uint64(s03) + f1 += uint64(s13) + s32 = uint32(s[14]) + + s21 <<= 8 + f2 += uint64(s20) + s33 = uint32(s[15]) + + s22 <<= 16 + f2 += uint64(s21) + + s23 <<= 24 + f2 += uint64(s22) + + s31 <<= 8 + f3 += uint64(s30) + + s32 <<= 16 + f3 += uint64(s31) + + s33 <<= 24 + f3 += uint64(s32) + + f2 += uint64(s23) + f3 += uint64(s33) + + out[0] = byte(f0) + f0 >>= 8 + out[1] = byte(f0) + f0 >>= 8 + out[2] = byte(f0) + f0 >>= 8 + out[3] = byte(f0) + f0 >>= 8 + f1 += f0 + + out[4] = byte(f1) + f1 >>= 8 + out[5] = byte(f1) + f1 >>= 8 + out[6] = byte(f1) + f1 >>= 8 + out[7] = byte(f1) + f1 >>= 8 + f2 += f1 + + out[8] = byte(f2) + f2 >>= 8 + out[9] = byte(f2) + f2 >>= 8 + out[10] = byte(f2) + f2 >>= 8 + out[11] = byte(f2) + f2 >>= 8 + f3 += f2 + + out[12] = byte(f3) + f3 >>= 8 + out[13] = byte(f3) + f3 >>= 8 + out[14] = byte(f3) + f3 >>= 8 + out[15] = byte(f3) +} diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE b/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go new file mode 100644 index 00000000..4c96147c --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go @@ -0,0 +1,144 @@ +// Copyright 2012 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 salsa provides low-level access to functions in the Salsa family. +package salsa // import "golang.org/x/crypto/salsa20/salsa" + +// Sigma is the Salsa20 constant for 256-bit keys. +var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'} + +// HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte +// key k, and 16-byte constant c, and puts the result into the 32-byte array +// out. +func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) { + x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 + x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 + x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 + x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 + x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 + x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 + x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 + x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 + x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 + x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 + x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 + x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 + + for i := 0; i < 20; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x5) + out[5] = byte(x5 >> 8) + out[6] = byte(x5 >> 16) + out[7] = byte(x5 >> 24) + + out[8] = byte(x10) + out[9] = byte(x10 >> 8) + out[10] = byte(x10 >> 16) + out[11] = byte(x10 >> 24) + + out[12] = byte(x15) + out[13] = byte(x15 >> 8) + out[14] = byte(x15 >> 16) + out[15] = byte(x15 >> 24) + + out[16] = byte(x6) + out[17] = byte(x6 >> 8) + out[18] = byte(x6 >> 16) + out[19] = byte(x6 >> 24) + + out[20] = byte(x7) + out[21] = byte(x7 >> 8) + out[22] = byte(x7 >> 16) + out[23] = byte(x7 >> 24) + + out[24] = byte(x8) + out[25] = byte(x8 >> 8) + out[26] = byte(x8 >> 16) + out[27] = byte(x8 >> 24) + + out[28] = byte(x9) + out[29] = byte(x9 >> 8) + out[30] = byte(x9 >> 16) + out[31] = byte(x9 >> 24) +} diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s b/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s new file mode 100644 index 00000000..6e1df963 --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s @@ -0,0 +1,902 @@ +// Copyright 2012 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. + +// +build amd64,!appengine,!gccgo + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) +TEXT ·salsa2020XORKeyStream(SB),0,$512-40 + MOVQ out+0(FP),DI + MOVQ in+8(FP),SI + MOVQ n+16(FP),DX + MOVQ nonce+24(FP),CX + MOVQ key+32(FP),R8 + + MOVQ SP,R11 + MOVQ $31,R9 + NOTQ R9 + ANDQ R9,SP + ADDQ $32,SP + + MOVQ R11,352(SP) + MOVQ R12,360(SP) + MOVQ R13,368(SP) + MOVQ R14,376(SP) + MOVQ R15,384(SP) + MOVQ BX,392(SP) + MOVQ BP,400(SP) + MOVQ DX,R9 + MOVQ CX,DX + MOVQ R8,R10 + CMPQ R9,$0 + JBE DONE + START: + MOVL 20(R10),CX + MOVL 0(R10),R8 + MOVL 0(DX),AX + MOVL 16(R10),R11 + MOVL CX,0(SP) + MOVL R8, 4 (SP) + MOVL AX, 8 (SP) + MOVL R11, 12 (SP) + MOVL 8(DX),CX + MOVL 24(R10),R8 + MOVL 4(R10),AX + MOVL 4(DX),R11 + MOVL CX,16(SP) + MOVL R8, 20 (SP) + MOVL AX, 24 (SP) + MOVL R11, 28 (SP) + MOVL 12(DX),CX + MOVL 12(R10),DX + MOVL 28(R10),R8 + MOVL 8(R10),AX + MOVL DX,32(SP) + MOVL CX, 36 (SP) + MOVL R8, 40 (SP) + MOVL AX, 44 (SP) + MOVQ $1634760805,DX + MOVQ $857760878,CX + MOVQ $2036477234,R8 + MOVQ $1797285236,AX + MOVL DX,48(SP) + MOVL CX, 52 (SP) + MOVL R8, 56 (SP) + MOVL AX, 60 (SP) + CMPQ R9,$256 + JB BYTESBETWEEN1AND255 + MOVOA 48(SP),X0 + PSHUFL $0X55,X0,X1 + PSHUFL $0XAA,X0,X2 + PSHUFL $0XFF,X0,X3 + PSHUFL $0X00,X0,X0 + MOVOA X1,64(SP) + MOVOA X2,80(SP) + MOVOA X3,96(SP) + MOVOA X0,112(SP) + MOVOA 0(SP),X0 + PSHUFL $0XAA,X0,X1 + PSHUFL $0XFF,X0,X2 + PSHUFL $0X00,X0,X3 + PSHUFL $0X55,X0,X0 + MOVOA X1,128(SP) + MOVOA X2,144(SP) + MOVOA X3,160(SP) + MOVOA X0,176(SP) + MOVOA 16(SP),X0 + PSHUFL $0XFF,X0,X1 + PSHUFL $0X55,X0,X2 + PSHUFL $0XAA,X0,X0 + MOVOA X1,192(SP) + MOVOA X2,208(SP) + MOVOA X0,224(SP) + MOVOA 32(SP),X0 + PSHUFL $0X00,X0,X1 + PSHUFL $0XAA,X0,X2 + PSHUFL $0XFF,X0,X0 + MOVOA X1,240(SP) + MOVOA X2,256(SP) + MOVOA X0,272(SP) + BYTESATLEAST256: + MOVL 16(SP),DX + MOVL 36 (SP),CX + MOVL DX,288(SP) + MOVL CX,304(SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 292 (SP) + MOVL CX, 308 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 296 (SP) + MOVL CX, 312 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX, 300 (SP) + MOVL CX, 316 (SP) + ADDQ $1,DX + SHLQ $32,CX + ADDQ CX,DX + MOVQ DX,CX + SHRQ $32,CX + MOVL DX,16(SP) + MOVL CX, 36 (SP) + MOVQ R9,408(SP) + MOVQ $20,DX + MOVOA 64(SP),X0 + MOVOA 80(SP),X1 + MOVOA 96(SP),X2 + MOVOA 256(SP),X3 + MOVOA 272(SP),X4 + MOVOA 128(SP),X5 + MOVOA 144(SP),X6 + MOVOA 176(SP),X7 + MOVOA 192(SP),X8 + MOVOA 208(SP),X9 + MOVOA 224(SP),X10 + MOVOA 304(SP),X11 + MOVOA 112(SP),X12 + MOVOA 160(SP),X13 + MOVOA 240(SP),X14 + MOVOA 288(SP),X15 + MAINLOOP1: + MOVOA X1,320(SP) + MOVOA X2,336(SP) + MOVOA X13,X1 + PADDL X12,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X14 + PSRLL $25,X2 + PXOR X2,X14 + MOVOA X7,X1 + PADDL X0,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X11 + PSRLL $25,X2 + PXOR X2,X11 + MOVOA X12,X1 + PADDL X14,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X15 + PSRLL $23,X2 + PXOR X2,X15 + MOVOA X0,X1 + PADDL X11,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X9 + PSRLL $23,X2 + PXOR X2,X9 + MOVOA X14,X1 + PADDL X15,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X13 + PSRLL $19,X2 + PXOR X2,X13 + MOVOA X11,X1 + PADDL X9,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X7 + PSRLL $19,X2 + PXOR X2,X7 + MOVOA X15,X1 + PADDL X13,X1 + MOVOA X1,X2 + PSLLL $18,X1 + PXOR X1,X12 + PSRLL $14,X2 + PXOR X2,X12 + MOVOA 320(SP),X1 + MOVOA X12,320(SP) + MOVOA X9,X2 + PADDL X7,X2 + MOVOA X2,X12 + PSLLL $18,X2 + PXOR X2,X0 + PSRLL $14,X12 + PXOR X12,X0 + MOVOA X5,X2 + PADDL X1,X2 + MOVOA X2,X12 + PSLLL $7,X2 + PXOR X2,X3 + PSRLL $25,X12 + PXOR X12,X3 + MOVOA 336(SP),X2 + MOVOA X0,336(SP) + MOVOA X6,X0 + PADDL X2,X0 + MOVOA X0,X12 + PSLLL $7,X0 + PXOR X0,X4 + PSRLL $25,X12 + PXOR X12,X4 + MOVOA X1,X0 + PADDL X3,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X10 + PSRLL $23,X12 + PXOR X12,X10 + MOVOA X2,X0 + PADDL X4,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X8 + PSRLL $23,X12 + PXOR X12,X8 + MOVOA X3,X0 + PADDL X10,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X5 + PSRLL $19,X12 + PXOR X12,X5 + MOVOA X4,X0 + PADDL X8,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X6 + PSRLL $19,X12 + PXOR X12,X6 + MOVOA X10,X0 + PADDL X5,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X1 + PSRLL $14,X12 + PXOR X12,X1 + MOVOA 320(SP),X0 + MOVOA X1,320(SP) + MOVOA X4,X1 + PADDL X0,X1 + MOVOA X1,X12 + PSLLL $7,X1 + PXOR X1,X7 + PSRLL $25,X12 + PXOR X12,X7 + MOVOA X8,X1 + PADDL X6,X1 + MOVOA X1,X12 + PSLLL $18,X1 + PXOR X1,X2 + PSRLL $14,X12 + PXOR X12,X2 + MOVOA 336(SP),X12 + MOVOA X2,336(SP) + MOVOA X14,X1 + PADDL X12,X1 + MOVOA X1,X2 + PSLLL $7,X1 + PXOR X1,X5 + PSRLL $25,X2 + PXOR X2,X5 + MOVOA X0,X1 + PADDL X7,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X10 + PSRLL $23,X2 + PXOR X2,X10 + MOVOA X12,X1 + PADDL X5,X1 + MOVOA X1,X2 + PSLLL $9,X1 + PXOR X1,X8 + PSRLL $23,X2 + PXOR X2,X8 + MOVOA X7,X1 + PADDL X10,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X4 + PSRLL $19,X2 + PXOR X2,X4 + MOVOA X5,X1 + PADDL X8,X1 + MOVOA X1,X2 + PSLLL $13,X1 + PXOR X1,X14 + PSRLL $19,X2 + PXOR X2,X14 + MOVOA X10,X1 + PADDL X4,X1 + MOVOA X1,X2 + PSLLL $18,X1 + PXOR X1,X0 + PSRLL $14,X2 + PXOR X2,X0 + MOVOA 320(SP),X1 + MOVOA X0,320(SP) + MOVOA X8,X0 + PADDL X14,X0 + MOVOA X0,X2 + PSLLL $18,X0 + PXOR X0,X12 + PSRLL $14,X2 + PXOR X2,X12 + MOVOA X11,X0 + PADDL X1,X0 + MOVOA X0,X2 + PSLLL $7,X0 + PXOR X0,X6 + PSRLL $25,X2 + PXOR X2,X6 + MOVOA 336(SP),X2 + MOVOA X12,336(SP) + MOVOA X3,X0 + PADDL X2,X0 + MOVOA X0,X12 + PSLLL $7,X0 + PXOR X0,X13 + PSRLL $25,X12 + PXOR X12,X13 + MOVOA X1,X0 + PADDL X6,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X15 + PSRLL $23,X12 + PXOR X12,X15 + MOVOA X2,X0 + PADDL X13,X0 + MOVOA X0,X12 + PSLLL $9,X0 + PXOR X0,X9 + PSRLL $23,X12 + PXOR X12,X9 + MOVOA X6,X0 + PADDL X15,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X11 + PSRLL $19,X12 + PXOR X12,X11 + MOVOA X13,X0 + PADDL X9,X0 + MOVOA X0,X12 + PSLLL $13,X0 + PXOR X0,X3 + PSRLL $19,X12 + PXOR X12,X3 + MOVOA X15,X0 + PADDL X11,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X1 + PSRLL $14,X12 + PXOR X12,X1 + MOVOA X9,X0 + PADDL X3,X0 + MOVOA X0,X12 + PSLLL $18,X0 + PXOR X0,X2 + PSRLL $14,X12 + PXOR X12,X2 + MOVOA 320(SP),X12 + MOVOA 336(SP),X0 + SUBQ $2,DX + JA MAINLOOP1 + PADDL 112(SP),X12 + PADDL 176(SP),X7 + PADDL 224(SP),X10 + PADDL 272(SP),X4 + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 0(SI),DX + XORL 4(SI),CX + XORL 8(SI),R8 + XORL 12(SI),R9 + MOVL DX,0(DI) + MOVL CX,4(DI) + MOVL R8,8(DI) + MOVL R9,12(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 64(SI),DX + XORL 68(SI),CX + XORL 72(SI),R8 + XORL 76(SI),R9 + MOVL DX,64(DI) + MOVL CX,68(DI) + MOVL R8,72(DI) + MOVL R9,76(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + PSHUFL $0X39,X12,X12 + PSHUFL $0X39,X7,X7 + PSHUFL $0X39,X10,X10 + PSHUFL $0X39,X4,X4 + XORL 128(SI),DX + XORL 132(SI),CX + XORL 136(SI),R8 + XORL 140(SI),R9 + MOVL DX,128(DI) + MOVL CX,132(DI) + MOVL R8,136(DI) + MOVL R9,140(DI) + MOVD X12,DX + MOVD X7,CX + MOVD X10,R8 + MOVD X4,R9 + XORL 192(SI),DX + XORL 196(SI),CX + XORL 200(SI),R8 + XORL 204(SI),R9 + MOVL DX,192(DI) + MOVL CX,196(DI) + MOVL R8,200(DI) + MOVL R9,204(DI) + PADDL 240(SP),X14 + PADDL 64(SP),X0 + PADDL 128(SP),X5 + PADDL 192(SP),X8 + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 16(SI),DX + XORL 20(SI),CX + XORL 24(SI),R8 + XORL 28(SI),R9 + MOVL DX,16(DI) + MOVL CX,20(DI) + MOVL R8,24(DI) + MOVL R9,28(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 80(SI),DX + XORL 84(SI),CX + XORL 88(SI),R8 + XORL 92(SI),R9 + MOVL DX,80(DI) + MOVL CX,84(DI) + MOVL R8,88(DI) + MOVL R9,92(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + PSHUFL $0X39,X14,X14 + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X5,X5 + PSHUFL $0X39,X8,X8 + XORL 144(SI),DX + XORL 148(SI),CX + XORL 152(SI),R8 + XORL 156(SI),R9 + MOVL DX,144(DI) + MOVL CX,148(DI) + MOVL R8,152(DI) + MOVL R9,156(DI) + MOVD X14,DX + MOVD X0,CX + MOVD X5,R8 + MOVD X8,R9 + XORL 208(SI),DX + XORL 212(SI),CX + XORL 216(SI),R8 + XORL 220(SI),R9 + MOVL DX,208(DI) + MOVL CX,212(DI) + MOVL R8,216(DI) + MOVL R9,220(DI) + PADDL 288(SP),X15 + PADDL 304(SP),X11 + PADDL 80(SP),X1 + PADDL 144(SP),X6 + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 32(SI),DX + XORL 36(SI),CX + XORL 40(SI),R8 + XORL 44(SI),R9 + MOVL DX,32(DI) + MOVL CX,36(DI) + MOVL R8,40(DI) + MOVL R9,44(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 96(SI),DX + XORL 100(SI),CX + XORL 104(SI),R8 + XORL 108(SI),R9 + MOVL DX,96(DI) + MOVL CX,100(DI) + MOVL R8,104(DI) + MOVL R9,108(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + PSHUFL $0X39,X15,X15 + PSHUFL $0X39,X11,X11 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X6,X6 + XORL 160(SI),DX + XORL 164(SI),CX + XORL 168(SI),R8 + XORL 172(SI),R9 + MOVL DX,160(DI) + MOVL CX,164(DI) + MOVL R8,168(DI) + MOVL R9,172(DI) + MOVD X15,DX + MOVD X11,CX + MOVD X1,R8 + MOVD X6,R9 + XORL 224(SI),DX + XORL 228(SI),CX + XORL 232(SI),R8 + XORL 236(SI),R9 + MOVL DX,224(DI) + MOVL CX,228(DI) + MOVL R8,232(DI) + MOVL R9,236(DI) + PADDL 160(SP),X13 + PADDL 208(SP),X9 + PADDL 256(SP),X3 + PADDL 96(SP),X2 + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 48(SI),DX + XORL 52(SI),CX + XORL 56(SI),R8 + XORL 60(SI),R9 + MOVL DX,48(DI) + MOVL CX,52(DI) + MOVL R8,56(DI) + MOVL R9,60(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 112(SI),DX + XORL 116(SI),CX + XORL 120(SI),R8 + XORL 124(SI),R9 + MOVL DX,112(DI) + MOVL CX,116(DI) + MOVL R8,120(DI) + MOVL R9,124(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + PSHUFL $0X39,X13,X13 + PSHUFL $0X39,X9,X9 + PSHUFL $0X39,X3,X3 + PSHUFL $0X39,X2,X2 + XORL 176(SI),DX + XORL 180(SI),CX + XORL 184(SI),R8 + XORL 188(SI),R9 + MOVL DX,176(DI) + MOVL CX,180(DI) + MOVL R8,184(DI) + MOVL R9,188(DI) + MOVD X13,DX + MOVD X9,CX + MOVD X3,R8 + MOVD X2,R9 + XORL 240(SI),DX + XORL 244(SI),CX + XORL 248(SI),R8 + XORL 252(SI),R9 + MOVL DX,240(DI) + MOVL CX,244(DI) + MOVL R8,248(DI) + MOVL R9,252(DI) + MOVQ 408(SP),R9 + SUBQ $256,R9 + ADDQ $256,SI + ADDQ $256,DI + CMPQ R9,$256 + JAE BYTESATLEAST256 + CMPQ R9,$0 + JBE DONE + BYTESBETWEEN1AND255: + CMPQ R9,$64 + JAE NOCOPY + MOVQ DI,DX + LEAQ 416(SP),DI + MOVQ R9,CX + REP; MOVSB + LEAQ 416(SP),DI + LEAQ 416(SP),SI + NOCOPY: + MOVQ R9,408(SP) + MOVOA 48(SP),X0 + MOVOA 0(SP),X1 + MOVOA 16(SP),X2 + MOVOA 32(SP),X3 + MOVOA X1,X4 + MOVQ $20,CX + MAINLOOP2: + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X3 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X3,X3 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X1 + PSHUFL $0X4E,X2,X2 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X1,X1 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X1 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X1,X1 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X3 + PSHUFL $0X4E,X2,X2 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X3,X3 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X3 + PXOR X6,X3 + PADDL X3,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X3,X3 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X1 + PSHUFL $0X4E,X2,X2 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X3,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X1,X1 + PXOR X6,X0 + PADDL X0,X4 + MOVOA X0,X5 + MOVOA X4,X6 + PSLLL $7,X4 + PSRLL $25,X6 + PXOR X4,X1 + PXOR X6,X1 + PADDL X1,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $9,X5 + PSRLL $23,X6 + PXOR X5,X2 + PSHUFL $0X93,X1,X1 + PXOR X6,X2 + PADDL X2,X4 + MOVOA X2,X5 + MOVOA X4,X6 + PSLLL $13,X4 + PSRLL $19,X6 + PXOR X4,X3 + PSHUFL $0X4E,X2,X2 + PXOR X6,X3 + SUBQ $4,CX + PADDL X3,X5 + MOVOA X1,X4 + MOVOA X5,X6 + PSLLL $18,X5 + PXOR X7,X7 + PSRLL $14,X6 + PXOR X5,X0 + PSHUFL $0X39,X3,X3 + PXOR X6,X0 + JA MAINLOOP2 + PADDL 48(SP),X0 + PADDL 0(SP),X1 + PADDL 16(SP),X2 + PADDL 32(SP),X3 + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 0(SI),CX + XORL 48(SI),R8 + XORL 32(SI),R9 + XORL 16(SI),AX + MOVL CX,0(DI) + MOVL R8,48(DI) + MOVL R9,32(DI) + MOVL AX,16(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 20(SI),CX + XORL 4(SI),R8 + XORL 52(SI),R9 + XORL 36(SI),AX + MOVL CX,20(DI) + MOVL R8,4(DI) + MOVL R9,52(DI) + MOVL AX,36(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + PSHUFL $0X39,X0,X0 + PSHUFL $0X39,X1,X1 + PSHUFL $0X39,X2,X2 + PSHUFL $0X39,X3,X3 + XORL 40(SI),CX + XORL 24(SI),R8 + XORL 8(SI),R9 + XORL 56(SI),AX + MOVL CX,40(DI) + MOVL R8,24(DI) + MOVL R9,8(DI) + MOVL AX,56(DI) + MOVD X0,CX + MOVD X1,R8 + MOVD X2,R9 + MOVD X3,AX + XORL 60(SI),CX + XORL 44(SI),R8 + XORL 28(SI),R9 + XORL 12(SI),AX + MOVL CX,60(DI) + MOVL R8,44(DI) + MOVL R9,28(DI) + MOVL AX,12(DI) + MOVQ 408(SP),R9 + MOVL 16(SP),CX + MOVL 36 (SP),R8 + ADDQ $1,CX + SHLQ $32,R8 + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $32,R8 + MOVL CX,16(SP) + MOVL R8, 36 (SP) + CMPQ R9,$64 + JA BYTESATLEAST65 + JAE BYTESATLEAST64 + MOVQ DI,SI + MOVQ DX,DI + MOVQ R9,CX + REP; MOVSB + BYTESATLEAST64: + DONE: + MOVQ 352(SP),R11 + MOVQ 360(SP),R12 + MOVQ 368(SP),R13 + MOVQ 376(SP),R14 + MOVQ 384(SP),R15 + MOVQ 392(SP),BX + MOVQ 400(SP),BP + MOVQ R11,SP + RET + BYTESATLEAST65: + SUBQ $64,R9 + ADDQ $64,DI + ADDQ $64,SI + JMP BYTESBETWEEN1AND255 diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go new file mode 100644 index 00000000..9bfc0927 --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go @@ -0,0 +1,199 @@ +// Copyright 2012 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 salsa + +// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts +// the result into the 64-byte array out. The input and output may be the same array. +func Core208(out *[64]byte, in *[64]byte) { + j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24 + j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24 + j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24 + j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24 + j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24 + j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24 + j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24 + j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24 + j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24 + j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24 + j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24 + j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24 + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 + x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 + + for i := 0; i < 8; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += j0 + x1 += j1 + x2 += j2 + x3 += j3 + x4 += j4 + x5 += j5 + x6 += j6 + x7 += j7 + x8 += j8 + x9 += j9 + x10 += j10 + x11 += j11 + x12 += j12 + x13 += j13 + x14 += j14 + x15 += j15 + + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x1) + out[5] = byte(x1 >> 8) + out[6] = byte(x1 >> 16) + out[7] = byte(x1 >> 24) + + out[8] = byte(x2) + out[9] = byte(x2 >> 8) + out[10] = byte(x2 >> 16) + out[11] = byte(x2 >> 24) + + out[12] = byte(x3) + out[13] = byte(x3 >> 8) + out[14] = byte(x3 >> 16) + out[15] = byte(x3 >> 24) + + out[16] = byte(x4) + out[17] = byte(x4 >> 8) + out[18] = byte(x4 >> 16) + out[19] = byte(x4 >> 24) + + out[20] = byte(x5) + out[21] = byte(x5 >> 8) + out[22] = byte(x5 >> 16) + out[23] = byte(x5 >> 24) + + out[24] = byte(x6) + out[25] = byte(x6 >> 8) + out[26] = byte(x6 >> 16) + out[27] = byte(x6 >> 24) + + out[28] = byte(x7) + out[29] = byte(x7 >> 8) + out[30] = byte(x7 >> 16) + out[31] = byte(x7 >> 24) + + out[32] = byte(x8) + out[33] = byte(x8 >> 8) + out[34] = byte(x8 >> 16) + out[35] = byte(x8 >> 24) + + out[36] = byte(x9) + out[37] = byte(x9 >> 8) + out[38] = byte(x9 >> 16) + out[39] = byte(x9 >> 24) + + out[40] = byte(x10) + out[41] = byte(x10 >> 8) + out[42] = byte(x10 >> 16) + out[43] = byte(x10 >> 24) + + out[44] = byte(x11) + out[45] = byte(x11 >> 8) + out[46] = byte(x11 >> 16) + out[47] = byte(x11 >> 24) + + out[48] = byte(x12) + out[49] = byte(x12 >> 8) + out[50] = byte(x12 >> 16) + out[51] = byte(x12 >> 24) + + out[52] = byte(x13) + out[53] = byte(x13 >> 8) + out[54] = byte(x13 >> 16) + out[55] = byte(x13 >> 24) + + out[56] = byte(x14) + out[57] = byte(x14 >> 8) + out[58] = byte(x14 >> 16) + out[59] = byte(x14 >> 24) + + out[60] = byte(x15) + out[61] = byte(x15 >> 8) + out[62] = byte(x15 >> 16) + out[63] = byte(x15 >> 24) +} diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go new file mode 100644 index 00000000..903c7858 --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go @@ -0,0 +1,23 @@ +// Copyright 2012 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. + +// +build amd64,!appengine,!gccgo + +package salsa + +// This function is implemented in salsa2020_amd64.s. + +//go:noescape + +func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) + +// XORKeyStream crypts bytes from in to out using the given key and counters. +// In and out may be the same slice but otherwise should not overlap. Counter +// contains the raw salsa20 counter bytes (both nonce and block counter). +func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { + if len(in) == 0 { + return + } + salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0]) +} diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go new file mode 100644 index 00000000..95f8ca5b --- /dev/null +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go @@ -0,0 +1,234 @@ +// Copyright 2012 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. + +// +build !amd64 appengine gccgo + +package salsa + +const rounds = 20 + +// core applies the Salsa20 core function to 16-byte input in, 32-byte key k, +// and 16-byte constant c, and puts the result into 64-byte array out. +func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) { + j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 + j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 + j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 + j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 + j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 + j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 + j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 + j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 + j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 + j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 + j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 + j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 + j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 + j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 + j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 + j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 + x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 + + for i := 0; i < rounds; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += j0 + x1 += j1 + x2 += j2 + x3 += j3 + x4 += j4 + x5 += j5 + x6 += j6 + x7 += j7 + x8 += j8 + x9 += j9 + x10 += j10 + x11 += j11 + x12 += j12 + x13 += j13 + x14 += j14 + x15 += j15 + + out[0] = byte(x0) + out[1] = byte(x0 >> 8) + out[2] = byte(x0 >> 16) + out[3] = byte(x0 >> 24) + + out[4] = byte(x1) + out[5] = byte(x1 >> 8) + out[6] = byte(x1 >> 16) + out[7] = byte(x1 >> 24) + + out[8] = byte(x2) + out[9] = byte(x2 >> 8) + out[10] = byte(x2 >> 16) + out[11] = byte(x2 >> 24) + + out[12] = byte(x3) + out[13] = byte(x3 >> 8) + out[14] = byte(x3 >> 16) + out[15] = byte(x3 >> 24) + + out[16] = byte(x4) + out[17] = byte(x4 >> 8) + out[18] = byte(x4 >> 16) + out[19] = byte(x4 >> 24) + + out[20] = byte(x5) + out[21] = byte(x5 >> 8) + out[22] = byte(x5 >> 16) + out[23] = byte(x5 >> 24) + + out[24] = byte(x6) + out[25] = byte(x6 >> 8) + out[26] = byte(x6 >> 16) + out[27] = byte(x6 >> 24) + + out[28] = byte(x7) + out[29] = byte(x7 >> 8) + out[30] = byte(x7 >> 16) + out[31] = byte(x7 >> 24) + + out[32] = byte(x8) + out[33] = byte(x8 >> 8) + out[34] = byte(x8 >> 16) + out[35] = byte(x8 >> 24) + + out[36] = byte(x9) + out[37] = byte(x9 >> 8) + out[38] = byte(x9 >> 16) + out[39] = byte(x9 >> 24) + + out[40] = byte(x10) + out[41] = byte(x10 >> 8) + out[42] = byte(x10 >> 16) + out[43] = byte(x10 >> 24) + + out[44] = byte(x11) + out[45] = byte(x11 >> 8) + out[46] = byte(x11 >> 16) + out[47] = byte(x11 >> 24) + + out[48] = byte(x12) + out[49] = byte(x12 >> 8) + out[50] = byte(x12 >> 16) + out[51] = byte(x12 >> 24) + + out[52] = byte(x13) + out[53] = byte(x13 >> 8) + out[54] = byte(x13 >> 16) + out[55] = byte(x13 >> 24) + + out[56] = byte(x14) + out[57] = byte(x14 >> 8) + out[58] = byte(x14 >> 16) + out[59] = byte(x14 >> 24) + + out[60] = byte(x15) + out[61] = byte(x15 >> 8) + out[62] = byte(x15 >> 16) + out[63] = byte(x15 >> 24) +} + +// XORKeyStream crypts bytes from in to out using the given key and counters. +// In and out may be the same slice but otherwise should not overlap. Counter +// contains the raw salsa20 counter bytes (both nonce and block counter). +func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { + var block [64]byte + var counterCopy [16]byte + copy(counterCopy[:], counter[:]) + + for len(in) >= 64 { + core(&block, &counterCopy, key, &Sigma) + for i, x := range block { + out[i] = in[i] ^ x + } + u := uint32(1) + for i := 8; i < 16; i++ { + u += uint32(counterCopy[i]) + counterCopy[i] = byte(u) + u >>= 8 + } + in = in[64:] + out = out[64:] + } + + if len(in) > 0 { + core(&block, &counterCopy, key, &Sigma) + for i, v := range in { + out[i] = v ^ block[i] + } + } +} diff --git a/vendor/manifest b/vendor/manifest index ef9cc7d9..705c463d 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -44,6 +44,14 @@ "notests": true }, { + "importpath": "github.com/bwmarrin/discordgo", + "repository": "https://github.com/bwmarrin/discordgo", + "vcs": "git", + "revision": "11fa9dc906c531a85cd969b7b6b77c2f452a61b3", + "branch": "master", + "notests": true + }, + { "importpath": "github.com/gorilla/schema", "repository": "https://github.com/gorilla/schema", "vcs": "", @@ -185,6 +193,33 @@ "notests": true }, { + "importpath": "golang.org/x/crypto/nacl/secretbox", + "repository": "https://go.googlesource.com/crypto", + "vcs": "git", + "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726", + "branch": "master", + "path": "/nacl/secretbox", + "notests": true + }, + { + "importpath": "golang.org/x/crypto/poly1305", + "repository": "https://go.googlesource.com/crypto", + "vcs": "git", + "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726", + "branch": "master", + "path": "/poly1305", + "notests": true + }, + { + "importpath": "golang.org/x/crypto/salsa20/salsa", + "repository": "https://go.googlesource.com/crypto", + "vcs": "git", + "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726", + "branch": "master", + "path": "/salsa20/salsa", + "notests": true + }, + { "importpath": "golang.org/x/net/http2/hpack", "repository": "https://go.googlesource.com/net", "vcs": "", |