Add Discord support

pull/54/head
Wim 8 years ago
parent 0816e96831
commit a0b84beb9b

@ -3,9 +3,9 @@
:warning: Look at [README-0.6.md] (https://github.com/42wim/matterbridge/blob/master/README-0.6.md) for the documentation of the current stable.
The information below is about the develop version.
Simple bridge between mattermost, IRC, XMPP, Gitter and Slack
Simple bridge between mattermost, IRC, XMPP, Gitter, Slack and Discord
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter and Slack. Pick and mix.
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack and Discord. Pick and mix.
* Supports multiple channels.
* Matterbridge can also work with private groups on your mattermost.
* Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts.
@ -24,6 +24,7 @@ Accounts to one of the supported bridges
* [XMPP] (https://jabber.org)
* [Gitter] (https://gitter.im)
* [Slack] (https://slack.com)
* [Discord] (https://discordapp.com)
## Docker
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```

@ -2,6 +2,7 @@ package bridge
import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/discord"
"github.com/42wim/matterbridge/bridge/gitter"
"github.com/42wim/matterbridge/bridge/irc"
"github.com/42wim/matterbridge/bridge/mattermost"
@ -35,6 +36,8 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) Bridg
return bslack.New(cfg.Slack[name], name, c)
case "xmpp":
return bxmpp.New(cfg.Xmpp[name], name, c)
case "discord":
return bdiscord.New(cfg.Discord[name], name, c)
}
return nil
}

@ -16,6 +16,7 @@ type Message struct {
type Protocol struct {
BindAddress string // mattermost, slack
Guild string // discord
IconURL string // mattermost, slack
IgnoreNicks string // all protocols
Jid string // xmpp
@ -32,11 +33,11 @@ type Protocol struct {
PrefixMessagesWithNick bool // mattemost, slack
Protocol string //all protocols
RemoteNickFormat string // all protocols
Server string // IRC,mattermost,XMPP
Server string // IRC,mattermost,XMPP,discord
ShowJoinPart bool // all protocols
SkipTLSVerify bool // IRC, mattermost
Team string // mattermost
Token string // gitter, slack
Token string // gitter, slack, discord
URL string // mattermost, slack
UseAPI bool // mattermost, slack
UseSASL bool // IRC
@ -61,6 +62,7 @@ type Config struct {
Slack map[string]Protocol
Gitter map[string]Protocol
Xmpp map[string]Protocol
Discord map[string]Protocol
Gateway []Gateway
}

@ -6,6 +6,7 @@ See matterbridge.toml.sample for an example
## New features
* Allow for bridging the same type of bridge, which means you can eg bridge between multiple mattermosts.
* The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways).
* Discord support added. See matterbridge.toml.sample for more information
# v0.6.1
## New features

@ -131,5 +131,7 @@ func (gw *Gateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
setNickFormat(msg, gw.Config.Mattermost[dest.Origin()].RemoteNickFormat)
case "slack":
setNickFormat(msg, gw.Config.Slack[dest.Origin()].RemoteNickFormat)
case "discord":
setNickFormat(msg, gw.Config.Discord[dest.Origin()].RemoteNickFormat)
}
}

@ -262,6 +262,35 @@ NicksPerRow=4
#OPTIONAL
IgnoreNicks="mmbot spammer2"
###################################################################
#discord section
###################################################################
[discord]
#You can configure multiple servers "[discord.name]" or "[discord.name2]"
#In this example we use [discord.game]
#REQUIRED
[discord.game]
#Token to connect with Discord API
#You can get your token by following the instructions on
#https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token
#REQUIRED
Token="Yourtokenhere"
#REQUIRED
Guild="yourguildname"
#Nicks you want to ignore. Messages of those users will not be bridged.
#OPTIONAL
IgnoreNicks="spammer1 spammer2"
#RemoteNickFormat defines how remote users appear on this bridge
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
#OPTIONAL (default {BRIDGE}-{NICK})
RemoteNickFormat="[{BRIDGE}] <{NICK}>
###################################################################
#Gateway configuration

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

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

@ -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" }
)

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

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

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

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

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

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

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

@ -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!")
}
}

@ -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()))
}
*/

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

@ -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])
}

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

35
vendor/manifest vendored

@ -43,6 +43,14 @@
"branch": "master",
"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",
@ -184,6 +192,33 @@
"path": "/blowfish",
"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",

Loading…
Cancel
Save