mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-27 08:49:24 +00:00
Compare commits
15 Commits
v0.10.2-de
...
v0.11.0-be
Author | SHA1 | Date | |
---|---|---|---|
33dffd5ea8 | |||
57176dadd4 | |||
dd449a8705 | |||
587ad9f41d | |||
a16ad8bf3b | |||
1e0490bd36 | |||
8afc641f0c | |||
2e4d58cb92 | |||
02d7e2db65 | |||
f935c573e9 | |||
4a25e66c00 | |||
95f4e3448e | |||
eacb1c1771 | |||
07fd825349 | |||
be15cc8a36 |
11
README.md
11
README.md
@ -42,7 +42,7 @@ Accounts to one of the supported bridges
|
|||||||
# Installing
|
# Installing
|
||||||
## Binaries
|
## Binaries
|
||||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
|
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
|
||||||
* Latest release [v0.10.1](https://github.com/42wim/matterbridge/releases/latest)
|
* Latest release [v0.10.3](https://github.com/42wim/matterbridge/releases/latest)
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
|
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
|
||||||
@ -122,9 +122,11 @@ RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
|
|||||||
```
|
```
|
||||||
|
|
||||||
# Running
|
# Running
|
||||||
1) Copy the matterbridge.toml.sample to matterbridge.toml in the same directory as the matterbridge binary.
|
1) Copy the matterbridge.toml.sample to matterbridge.toml
|
||||||
2) Edit matterbridge.toml with the settings for your environment.
|
2) Edit matterbridge.toml with the settings for your environment.
|
||||||
3) Now you can run matterbridge. (```./matterbridge```)
|
3) Now you can run matterbridge. (```./matterbridge```)
|
||||||
|
|
||||||
|
(Matterbridge will only look for the config file in your current directory, if it isn't there specify -conf "/path/toyour/matterbridge.toml")
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage of ./matterbridge:
|
Usage of ./matterbridge:
|
||||||
@ -132,6 +134,8 @@ Usage of ./matterbridge:
|
|||||||
config file (default "matterbridge.toml")
|
config file (default "matterbridge.toml")
|
||||||
-debug
|
-debug
|
||||||
enable debug
|
enable debug
|
||||||
|
-gops
|
||||||
|
enable gops agent
|
||||||
-version
|
-version
|
||||||
show version
|
show version
|
||||||
```
|
```
|
||||||
@ -165,6 +169,7 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
* discord - https://github.com/bwmarrin/discordgo
|
* discord - https://github.com/bwmarrin/discordgo
|
||||||
* echo - https://github.com/labstack/echo
|
* echo - https://github.com/labstack/echo
|
||||||
* gitter - https://github.com/sromku/go-gitter
|
* gitter - https://github.com/sromku/go-gitter
|
||||||
|
* gops - https://github.com/google/gops
|
||||||
* irc - https://github.com/thoj/go-ircevent
|
* irc - https://github.com/thoj/go-ircevent
|
||||||
* mattermost - https://github.com/mattermost/platform
|
* mattermost - https://github.com/mattermost/platform
|
||||||
* matrix - https://github.com/matrix-org/gomatrix
|
* matrix - https://github.com/matrix-org/gomatrix
|
||||||
|
@ -27,23 +27,23 @@ type Bridger interface {
|
|||||||
type Bridge struct {
|
type Bridge struct {
|
||||||
Config config.Protocol
|
Config config.Protocol
|
||||||
Bridger
|
Bridger
|
||||||
Name string
|
Name string
|
||||||
Account string
|
Account string
|
||||||
Protocol string
|
Protocol string
|
||||||
ChannelsIn map[string]config.ChannelOptions
|
Channels map[string]config.ChannelInfo
|
||||||
ChannelsOut map[string]config.ChannelOptions
|
Joined map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge {
|
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge {
|
||||||
b := new(Bridge)
|
b := new(Bridge)
|
||||||
b.ChannelsIn = make(map[string]config.ChannelOptions)
|
b.Channels = make(map[string]config.ChannelInfo)
|
||||||
b.ChannelsOut = make(map[string]config.ChannelOptions)
|
|
||||||
accInfo := strings.Split(bridge.Account, ".")
|
accInfo := strings.Split(bridge.Account, ".")
|
||||||
protocol := accInfo[0]
|
protocol := accInfo[0]
|
||||||
name := accInfo[1]
|
name := accInfo[1]
|
||||||
b.Name = name
|
b.Name = name
|
||||||
b.Protocol = protocol
|
b.Protocol = protocol
|
||||||
b.Account = bridge.Account
|
b.Account = bridge.Account
|
||||||
|
b.Joined = make(map[string]bool)
|
||||||
|
|
||||||
// override config from environment
|
// override config from environment
|
||||||
config.OverrideCfgFromEnv(cfg, protocol, name)
|
config.OverrideCfgFromEnv(cfg, protocol, name)
|
||||||
@ -83,33 +83,28 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bridge) JoinChannels() error {
|
func (b *Bridge) JoinChannels() error {
|
||||||
exists := make(map[string]bool)
|
err := b.joinChannels(b.Channels, b.Joined)
|
||||||
err := b.joinChannels(b.ChannelsIn, exists)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = b.joinChannels(b.ChannelsOut, exists)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bridge) joinChannels(cMap map[string]config.ChannelOptions, exists map[string]bool) error {
|
func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error {
|
||||||
mychannel := ""
|
mychannel := ""
|
||||||
for channel, info := range cMap {
|
for ID, channel := range channels {
|
||||||
if !exists[channel] {
|
if !exists[ID] {
|
||||||
mychannel = channel
|
mychannel = channel.Name
|
||||||
log.Infof("%s: joining %s", b.Account, channel)
|
log.Infof("%s: joining %s (%s)", b.Account, channel.Name, ID)
|
||||||
if b.Protocol == "irc" && info.Key != "" {
|
if b.Protocol == "irc" && channel.Options.Key != "" {
|
||||||
log.Debugf("using key %s for channel %s", info.Key, channel)
|
log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
|
||||||
mychannel = mychannel + " " + info.Key
|
mychannel = mychannel + " " + channel.Options.Key
|
||||||
}
|
}
|
||||||
err := b.JoinChannel(mychannel)
|
err := b.JoinChannel(channel.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
exists[channel] = true
|
exists[ID] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -25,6 +25,16 @@ type Message struct {
|
|||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelInfo struct {
|
||||||
|
Name string
|
||||||
|
Account string
|
||||||
|
Direction string
|
||||||
|
ID string
|
||||||
|
GID map[string]bool
|
||||||
|
SameChannel map[string]bool
|
||||||
|
Options ChannelOptions
|
||||||
|
}
|
||||||
|
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
BindAddress string // mattermost, slack
|
BindAddress string // mattermost, slack
|
||||||
Buffer int // api
|
Buffer int // api
|
||||||
@ -63,9 +73,10 @@ type ChannelOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Bridge struct {
|
type Bridge struct {
|
||||||
Account string
|
Account string
|
||||||
Channel string
|
Channel string
|
||||||
Options ChannelOptions
|
Options ChannelOptions
|
||||||
|
SameChannel bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Gateway struct {
|
type Gateway struct {
|
||||||
|
@ -92,7 +92,7 @@ func (b *Birc) Connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Birc) Disconnect() error {
|
func (b *Birc) Disconnect() error {
|
||||||
b.i.Disconnect()
|
//b.i.Disconnect()
|
||||||
close(b.Local)
|
close(b.Local)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -96,12 +96,7 @@ func (b *Bmattermost) Send(msg config.Message) error {
|
|||||||
channel := msg.Channel
|
channel := msg.Channel
|
||||||
|
|
||||||
if b.Config.PrefixMessagesWithNick {
|
if b.Config.PrefixMessagesWithNick {
|
||||||
/*if IsMarkup(message) {
|
message = nick + message
|
||||||
message = nick + "\n\n" + message
|
|
||||||
} else {
|
|
||||||
*/
|
|
||||||
message = nick + " " + message
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
if !b.Config.UseAPI {
|
if !b.Config.UseAPI {
|
||||||
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
|
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
|
||||||
|
@ -73,6 +73,10 @@ func (b *Bslack) Disconnect() error {
|
|||||||
func (b *Bslack) JoinChannel(channel string) error {
|
func (b *Bslack) JoinChannel(channel string) error {
|
||||||
// we can only join channels using the API
|
// we can only join channels using the API
|
||||||
if b.Config.UseAPI {
|
if b.Config.UseAPI {
|
||||||
|
if strings.HasPrefix(b.Config.Token, "xoxb") {
|
||||||
|
// TODO check if bot has already joined channel
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err := b.sc.JoinChannel(channel)
|
_, err := b.sc.JoinChannel(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -80,28 +80,30 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
|||||||
text := ""
|
text := ""
|
||||||
channel := ""
|
channel := ""
|
||||||
for update := range updates {
|
for update := range updates {
|
||||||
|
var message *tgbotapi.Message
|
||||||
// handle channels
|
// handle channels
|
||||||
if update.ChannelPost != nil {
|
if update.ChannelPost != nil {
|
||||||
if update.ChannelPost.From != nil {
|
message = update.ChannelPost
|
||||||
username = update.ChannelPost.From.FirstName
|
}
|
||||||
if username == "" {
|
if update.EditedChannelPost != nil {
|
||||||
username = update.ChannelPost.From.UserName
|
message = update.EditedChannelPost
|
||||||
}
|
|
||||||
}
|
|
||||||
text = update.ChannelPost.Text
|
|
||||||
channel = strconv.FormatInt(update.ChannelPost.Chat.ID, 10)
|
|
||||||
}
|
}
|
||||||
// handle groups
|
// handle groups
|
||||||
if update.Message != nil {
|
if update.Message != nil {
|
||||||
if update.Message.From != nil {
|
message = update.Message
|
||||||
username = update.Message.From.FirstName
|
|
||||||
if username == "" {
|
|
||||||
username = update.Message.From.UserName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text = update.Message.Text
|
|
||||||
channel = strconv.FormatInt(update.Message.Chat.ID, 10)
|
|
||||||
}
|
}
|
||||||
|
if update.EditedMessage != nil {
|
||||||
|
message = update.EditedMessage
|
||||||
|
}
|
||||||
|
if message.From != nil {
|
||||||
|
username = message.From.FirstName
|
||||||
|
if username == "" {
|
||||||
|
username = message.From.UserName
|
||||||
|
}
|
||||||
|
text = message.Text
|
||||||
|
channel = strconv.FormatInt(message.Chat.ID, 10)
|
||||||
|
}
|
||||||
|
|
||||||
if username == "" {
|
if username == "" {
|
||||||
username = "unknown"
|
username = "unknown"
|
||||||
}
|
}
|
||||||
|
21
changelog.md
21
changelog.md
@ -1,3 +1,24 @@
|
|||||||
|
# v0.11.0-dev
|
||||||
|
## New features
|
||||||
|
* general: reusing the same account on multiple gateways now also reuses the connection.
|
||||||
|
This is particuarly useful for irc. See #87
|
||||||
|
* general: the Name is now REQUIRED and needs to be UNIQUE for each gateway configuration
|
||||||
|
|
||||||
|
# v0.10.3
|
||||||
|
## Bugfix
|
||||||
|
* slack: Allow bot tokens for now without warning (slack). Closes #140 (fixes user_is_bot message on channel join)
|
||||||
|
|
||||||
|
# v0.10.2
|
||||||
|
## New features
|
||||||
|
* general: gops agent added. Allows for more debugging. See #134
|
||||||
|
* general: toml inline table support added for config file
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* all: vendored libs updated
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
* general: add more informative messages on startup
|
||||||
|
|
||||||
# v0.10.1
|
# v0.10.1
|
||||||
## Bugfix
|
## Bugfix
|
||||||
* gitter: Fix sending messages on new channel join.
|
* gitter: Fix sending messages on new channel join.
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -14,21 +13,21 @@ type Gateway struct {
|
|||||||
*config.Config
|
*config.Config
|
||||||
MyConfig *config.Gateway
|
MyConfig *config.Gateway
|
||||||
Bridges map[string]*bridge.Bridge
|
Bridges map[string]*bridge.Bridge
|
||||||
ChannelsOut map[string][]string
|
Channels map[string]*config.ChannelInfo
|
||||||
ChannelsIn map[string][]string
|
|
||||||
ChannelOptions map[string]config.ChannelOptions
|
ChannelOptions map[string]config.ChannelOptions
|
||||||
|
Names map[string]bool
|
||||||
Name string
|
Name string
|
||||||
Message chan config.Message
|
Message chan config.Message
|
||||||
DestChannelFunc func(msg *config.Message, dest string) []string
|
DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, gateway *config.Gateway) *Gateway {
|
func New(cfg *config.Config) *Gateway {
|
||||||
gw := &Gateway{}
|
gw := &Gateway{}
|
||||||
gw.Name = gateway.Name
|
|
||||||
gw.Config = cfg
|
gw.Config = cfg
|
||||||
gw.MyConfig = gateway
|
gw.Channels = make(map[string]*config.ChannelInfo)
|
||||||
gw.Message = make(chan config.Message)
|
gw.Message = make(chan config.Message)
|
||||||
gw.Bridges = make(map[string]*bridge.Bridge)
|
gw.Bridges = make(map[string]*bridge.Bridge)
|
||||||
|
gw.Names = make(map[string]bool)
|
||||||
gw.DestChannelFunc = gw.getDestChannel
|
gw.DestChannelFunc = gw.getDestChannel
|
||||||
return gw
|
return gw
|
||||||
}
|
}
|
||||||
@ -36,13 +35,17 @@ func New(cfg *config.Config, gateway *config.Gateway) *Gateway {
|
|||||||
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
||||||
for _, br := range gw.Bridges {
|
for _, br := range gw.Bridges {
|
||||||
if br.Account == cfg.Account {
|
if br.Account == cfg.Account {
|
||||||
|
gw.mapChannelsToBridge(br)
|
||||||
|
err := br.JoinChannels()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Infof("Starting bridge: %s ", cfg.Account)
|
log.Infof("Starting bridge: %s ", cfg.Account)
|
||||||
br := bridge.New(gw.Config, cfg, gw.Message)
|
br := bridge.New(gw.Config, cfg, gw.Message)
|
||||||
gw.mapChannelsToBridge(br, gw.ChannelsOut)
|
gw.mapChannelsToBridge(br)
|
||||||
gw.mapChannelsToBridge(br, gw.ChannelsIn)
|
|
||||||
gw.Bridges[cfg.Account] = br
|
gw.Bridges[cfg.Account] = br
|
||||||
err := br.Connect()
|
err := br.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,17 +58,17 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge, cMap map[string][]string) {
|
func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
|
||||||
for _, channel := range cMap[br.Account] {
|
if gw.Names[cfg.Name] {
|
||||||
if _, ok := gw.ChannelOptions[br.Account+channel]; ok {
|
return fmt.Errorf("Gateway with name %s already exists", cfg.Name)
|
||||||
br.ChannelsOut[channel] = gw.ChannelOptions[br.Account+channel]
|
|
||||||
} else {
|
|
||||||
br.ChannelsOut[channel] = config.ChannelOptions{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if cfg.Name == "" {
|
||||||
|
return fmt.Errorf("%s", "Gateway without name found")
|
||||||
func (gw *Gateway) Start() error {
|
}
|
||||||
|
log.Infof("Starting gateway: %s", cfg.Name)
|
||||||
|
gw.Names[cfg.Name] = true
|
||||||
|
gw.Name = cfg.Name
|
||||||
|
gw.MyConfig = cfg
|
||||||
gw.mapChannels()
|
gw.mapChannels()
|
||||||
for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
|
for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
|
||||||
err := gw.AddBridge(&br)
|
err := gw.AddBridge(&br)
|
||||||
@ -73,6 +76,18 @@ func (gw *Gateway) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
|
||||||
|
for ID, channel := range gw.Channels {
|
||||||
|
if br.Account == channel.Account {
|
||||||
|
br.Channels[ID] = *channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) Start() error {
|
||||||
go gw.handleReceive()
|
go gw.handleReceive()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -109,45 +124,52 @@ RECONNECT:
|
|||||||
time.Sleep(time.Second * 60)
|
time.Sleep(time.Second * 60)
|
||||||
goto RECONNECT
|
goto RECONNECT
|
||||||
}
|
}
|
||||||
|
br.Joined = make(map[string]bool)
|
||||||
br.JoinChannels()
|
br.JoinChannels()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) mapChannels() error {
|
func (gw *Gateway) mapChannels() error {
|
||||||
options := make(map[string]config.ChannelOptions)
|
for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) {
|
||||||
m := make(map[string][]string)
|
ID := br.Channel + br.Account
|
||||||
for _, br := range gw.MyConfig.Out {
|
_, ok := gw.Channels[ID]
|
||||||
m[br.Account] = append(m[br.Account], br.Channel)
|
if !ok {
|
||||||
options[br.Account+br.Channel] = br.Options
|
channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account,
|
||||||
|
GID: make(map[string]bool), SameChannel: make(map[string]bool)}
|
||||||
|
channel.GID[gw.Name] = true
|
||||||
|
channel.SameChannel[gw.Name] = br.SameChannel
|
||||||
|
gw.Channels[channel.ID] = channel
|
||||||
|
}
|
||||||
|
gw.Channels[ID].GID[gw.Name] = true
|
||||||
|
gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
|
||||||
}
|
}
|
||||||
gw.ChannelsOut = m
|
|
||||||
m = nil
|
for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
|
||||||
m = make(map[string][]string)
|
ID := br.Channel + br.Account
|
||||||
for _, br := range gw.MyConfig.In {
|
_, ok := gw.Channels[ID]
|
||||||
m[br.Account] = append(m[br.Account], br.Channel)
|
if !ok {
|
||||||
options[br.Account+br.Channel] = br.Options
|
channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account,
|
||||||
|
GID: make(map[string]bool), SameChannel: make(map[string]bool)}
|
||||||
|
channel.GID[gw.Name] = true
|
||||||
|
channel.SameChannel[gw.Name] = br.SameChannel
|
||||||
|
gw.Channels[channel.ID] = channel
|
||||||
|
}
|
||||||
|
gw.Channels[ID].GID[gw.Name] = true
|
||||||
|
gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
|
||||||
}
|
}
|
||||||
gw.ChannelsIn = m
|
|
||||||
for _, br := range gw.MyConfig.InOut {
|
|
||||||
gw.ChannelsIn[br.Account] = append(gw.ChannelsIn[br.Account], br.Channel)
|
|
||||||
gw.ChannelsOut[br.Account] = append(gw.ChannelsOut[br.Account], br.Channel)
|
|
||||||
options[br.Account+br.Channel] = br.Options
|
|
||||||
}
|
|
||||||
gw.ChannelOptions = options
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
|
func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
|
||||||
channels := gw.ChannelsIn[msg.Account]
|
var channels []config.ChannelInfo
|
||||||
// broadcast to every out channel (irc QUIT)
|
for _, channel := range gw.Channels {
|
||||||
if msg.Event == config.EVENT_JOIN_LEAVE && msg.Channel == "" {
|
if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
|
||||||
return gw.ChannelsOut[dest]
|
continue
|
||||||
}
|
}
|
||||||
for _, channel := range channels {
|
if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(*msg, channel) {
|
||||||
if channel == msg.Channel {
|
channels = append(channels, *channel)
|
||||||
return gw.ChannelsOut[dest]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []string{}
|
return channels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
|
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
|
||||||
@ -155,19 +177,19 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
|
|||||||
if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
|
if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// broadcast to every out channel (irc QUIT)
|
||||||
|
if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
|
||||||
|
log.Debug("empty channel")
|
||||||
|
return
|
||||||
|
}
|
||||||
originchannel := msg.Channel
|
originchannel := msg.Channel
|
||||||
channels := gw.DestChannelFunc(&msg, dest.Account)
|
for _, channel := range gw.DestChannelFunc(&msg, *dest) {
|
||||||
for _, channel := range channels {
|
// do not send to ourself
|
||||||
// do not send the message to the bridge we come from if also the channel is the same
|
if channel.ID == getChannelID(msg) {
|
||||||
if msg.Account == dest.Account && channel == originchannel {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msg.Channel = channel
|
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
|
||||||
if msg.Channel == "" {
|
msg.Channel = channel.Name
|
||||||
log.Debug("empty channel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel)
|
|
||||||
gw.modifyUsername(&msg, dest)
|
gw.modifyUsername(&msg, dest)
|
||||||
// for api we need originchannel as channel
|
// for api we need originchannel as channel
|
||||||
if dest.Protocol == "api" {
|
if dest.Protocol == "api" {
|
||||||
@ -194,21 +216,6 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) modifyMessage(msg *config.Message, dest *bridge.Bridge) {
|
|
||||||
val := reflect.ValueOf(gw.Config).Elem()
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
|
||||||
typeField := val.Type().Field(i)
|
|
||||||
// look for the protocol map (both lowercase)
|
|
||||||
if strings.ToLower(typeField.Name) == dest.Protocol {
|
|
||||||
// get the Protocol struct from the map
|
|
||||||
protoCfg := val.Field(i).MapIndex(reflect.ValueOf(dest.Name))
|
|
||||||
//config.SetNickFormat(msg, protoCfg.Interface().(config.Protocol))
|
|
||||||
val.Field(i).SetMapIndex(reflect.ValueOf(dest.Name), protoCfg)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
|
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
|
||||||
br := gw.Bridges[msg.Account]
|
br := gw.Bridges[msg.Account]
|
||||||
msg.Protocol = br.Protocol
|
msg.Protocol = br.Protocol
|
||||||
@ -221,3 +228,29 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
|
|||||||
nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
|
nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
|
||||||
msg.Username = nick
|
msg.Username = nick
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChannelID(msg config.Message) string {
|
||||||
|
return msg.Channel + msg.Account
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) validGatewayDest(msg config.Message, channel *config.ChannelInfo) bool {
|
||||||
|
GIDmap := gw.Channels[getChannelID(msg)].GID
|
||||||
|
// check if we are running a samechannelgateway.
|
||||||
|
// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel.
|
||||||
|
for k, _ := range GIDmap {
|
||||||
|
if channel.SameChannel[k] == true {
|
||||||
|
if msg.Channel == channel.Name {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if we are in the correct gateway
|
||||||
|
for k, _ := range GIDmap {
|
||||||
|
if channel.GID[k] == true {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -2,48 +2,27 @@ package samechannelgateway
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/gateway"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SameChannelGateway struct {
|
type SameChannelGateway struct {
|
||||||
*config.Config
|
*config.Config
|
||||||
MyConfig *config.SameChannelGateway
|
|
||||||
Channels []string
|
|
||||||
Name string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, gatewayCfg *config.SameChannelGateway) *SameChannelGateway {
|
func New(cfg *config.Config) *SameChannelGateway {
|
||||||
return &SameChannelGateway{
|
return &SameChannelGateway{Config: cfg}
|
||||||
MyConfig: gatewayCfg,
|
|
||||||
Channels: gatewayCfg.Channels,
|
|
||||||
Name: gatewayCfg.Name,
|
|
||||||
Config: cfg}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sgw *SameChannelGateway) Start() error {
|
func (sgw *SameChannelGateway) GetConfig() []config.Gateway {
|
||||||
gw := gateway.New(sgw.Config, &config.Gateway{Name: sgw.Name})
|
var gwconfigs []config.Gateway
|
||||||
gw.DestChannelFunc = sgw.getDestChannel
|
cfg := sgw.Config
|
||||||
for _, account := range sgw.MyConfig.Accounts {
|
for _, gw := range cfg.SameChannelGateway {
|
||||||
for _, channel := range sgw.Channels {
|
gwconfig := config.Gateway{Name: gw.Name, Enable: gw.Enable}
|
||||||
br := config.Bridge{Account: account, Channel: channel}
|
for _, account := range gw.Accounts {
|
||||||
gw.MyConfig.InOut = append(gw.MyConfig.InOut, br)
|
for _, channel := range gw.Channels {
|
||||||
|
gwconfig.InOut = append(gwconfig.InOut, config.Bridge{Account: account, Channel: channel, SameChannel: true})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
gwconfigs = append(gwconfigs, gwconfig)
|
||||||
}
|
}
|
||||||
return gw.Start()
|
return gwconfigs
|
||||||
}
|
|
||||||
|
|
||||||
func (sgw *SameChannelGateway) validChannel(channel string) bool {
|
|
||||||
for _, c := range sgw.Channels {
|
|
||||||
if c == channel {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sgw *SameChannelGateway) getDestChannel(msg *config.Message, dest string) []string {
|
|
||||||
if sgw.validChannel(msg.Channel) {
|
|
||||||
return []string{msg.Channel}
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,11 @@ import (
|
|||||||
"github.com/42wim/matterbridge/gateway/samechannel"
|
"github.com/42wim/matterbridge/gateway/samechannel"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/google/gops/agent"
|
"github.com/google/gops/agent"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "0.10.2-dev"
|
version = "0.11.0-dev"
|
||||||
githash string
|
githash string
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,31 +40,26 @@ func main() {
|
|||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
}
|
}
|
||||||
log.Printf("Running version %s %s", version, githash)
|
log.Printf("Running version %s %s", version, githash)
|
||||||
cfg := config.NewConfig(*flagConfig)
|
if strings.Contains(version, "-dev") {
|
||||||
for _, gw := range cfg.SameChannelGateway {
|
log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
|
||||||
if !gw.Enable {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Printf("Starting samechannel gateway %#v", gw.Name)
|
|
||||||
g := samechannelgateway.New(cfg, &gw)
|
|
||||||
err := g.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Starting gateway failed %#v", err)
|
|
||||||
}
|
|
||||||
log.Printf("Started samechannel gateway %#v", gw.Name)
|
|
||||||
}
|
}
|
||||||
|
cfg := config.NewConfig(*flagConfig)
|
||||||
|
|
||||||
for _, gw := range cfg.Gateway {
|
g := gateway.New(cfg)
|
||||||
|
sgw := samechannelgateway.New(cfg)
|
||||||
|
gwconfigs := sgw.GetConfig()
|
||||||
|
for _, gw := range append(gwconfigs, cfg.Gateway...) {
|
||||||
if !gw.Enable {
|
if !gw.Enable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Printf("Starting gateway %#v", gw.Name)
|
err := g.AddConfig(&gw)
|
||||||
g := gateway.New(cfg, &gw)
|
|
||||||
err := g.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Starting gateway failed %#v", err)
|
log.Fatalf("Starting gateway failed: %s", err)
|
||||||
}
|
}
|
||||||
log.Printf("Started gateway %#v", gw.Name)
|
}
|
||||||
|
err := g.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Starting gateway failed: %s", err)
|
||||||
}
|
}
|
||||||
log.Printf("Gateway(s) started succesfully. Now relaying messages")
|
log.Printf("Gateway(s) started succesfully. Now relaying messages")
|
||||||
select {}
|
select {}
|
||||||
|
@ -587,7 +587,7 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|||||||
#
|
#
|
||||||
|
|
||||||
[[gateway]]
|
[[gateway]]
|
||||||
#OPTIONAL (not used for now)
|
#REQUIRED and UNIQUE
|
||||||
name="gateway1"
|
name="gateway1"
|
||||||
#Enable enables this gateway
|
#Enable enables this gateway
|
||||||
##OPTIONAL (default false)
|
##OPTIONAL (default false)
|
||||||
@ -659,6 +659,7 @@ enable=true
|
|||||||
#channel testing on slack and vice versa. (and for the channel testing2 and testing3)
|
#channel testing on slack and vice versa. (and for the channel testing2 and testing3)
|
||||||
|
|
||||||
[[samechannelgateway]]
|
[[samechannelgateway]]
|
||||||
|
name="samechannel1"
|
||||||
enable = false
|
enable = false
|
||||||
accounts = [ "mattermost.work","slack.hobby" ]
|
accounts = [ "mattermost.work","slack.hobby" ]
|
||||||
channels = [ "testing","testing2","testing3"]
|
channels = [ "testing","testing2","testing3"]
|
||||||
|
@ -30,3 +30,12 @@ enable=true
|
|||||||
[[gateway.out]]
|
[[gateway.out]]
|
||||||
account="mattermost.work"
|
account="mattermost.work"
|
||||||
channel="off-topic"
|
channel="off-topic"
|
||||||
|
|
||||||
|
#simpler config possible since v0.10.2
|
||||||
|
#[[gateway]]
|
||||||
|
#name="gateway2"
|
||||||
|
#enable=true
|
||||||
|
#inout = [
|
||||||
|
# { account="irc.freenode", channel="#testing", options={key="channelkey"}},
|
||||||
|
# { account="mattermost.work", channel="off-topic" },
|
||||||
|
#]
|
||||||
|
@ -159,11 +159,11 @@ func (m *MMClient) Login() error {
|
|||||||
m.Client.SetTeamId(m.Team.Id)
|
m.Client.SetTeamId(m.Team.Id)
|
||||||
|
|
||||||
// setup websocket connection
|
// setup websocket connection
|
||||||
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX + "/users/websocket"
|
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX_V3 + "/users/websocket"
|
||||||
header := http.Header{}
|
header := http.Header{}
|
||||||
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
||||||
|
|
||||||
m.log.Debug("WsClient: making connection")
|
m.log.Debugf("WsClient: making connection: %s", wsurl)
|
||||||
for {
|
for {
|
||||||
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||||
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
||||||
|
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
@ -4,7 +4,7 @@ files via reflection. There is also support for delaying decoding with
|
|||||||
the Primitive type, and querying the set of keys in a TOML document with the
|
the Primitive type, and querying the set of keys in a TOML document with the
|
||||||
MetaData type.
|
MetaData type.
|
||||||
|
|
||||||
The specification implemented: https://github.com/mojombo/toml
|
The specification implemented: https://github.com/toml-lang/toml
|
||||||
|
|
||||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
||||||
whether a file is a valid TOML document. It can also be used to print the
|
whether a file is a valid TOML document. It can also be used to print the
|
||||||
|
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
@ -241,7 +241,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
|||||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||||
panicIfInvalidKey(key)
|
panicIfInvalidKey(key)
|
||||||
if len(key) == 1 {
|
if len(key) == 1 {
|
||||||
// Output an extra new line between top-level tables.
|
// Output an extra newline between top-level tables.
|
||||||
// (The newline isn't written if nothing else has been written though.)
|
// (The newline isn't written if nothing else has been written though.)
|
||||||
enc.newline()
|
enc.newline()
|
||||||
}
|
}
|
||||||
|
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
@ -30,24 +30,28 @@ const (
|
|||||||
itemArrayTableEnd
|
itemArrayTableEnd
|
||||||
itemKeyStart
|
itemKeyStart
|
||||||
itemCommentStart
|
itemCommentStart
|
||||||
|
itemInlineTableStart
|
||||||
|
itemInlineTableEnd
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eof = 0
|
eof = 0
|
||||||
tableStart = '['
|
comma = ','
|
||||||
tableEnd = ']'
|
tableStart = '['
|
||||||
arrayTableStart = '['
|
tableEnd = ']'
|
||||||
arrayTableEnd = ']'
|
arrayTableStart = '['
|
||||||
tableSep = '.'
|
arrayTableEnd = ']'
|
||||||
keySep = '='
|
tableSep = '.'
|
||||||
arrayStart = '['
|
keySep = '='
|
||||||
arrayEnd = ']'
|
arrayStart = '['
|
||||||
arrayValTerm = ','
|
arrayEnd = ']'
|
||||||
commentStart = '#'
|
commentStart = '#'
|
||||||
stringStart = '"'
|
stringStart = '"'
|
||||||
stringEnd = '"'
|
stringEnd = '"'
|
||||||
rawStringStart = '\''
|
rawStringStart = '\''
|
||||||
rawStringEnd = '\''
|
rawStringEnd = '\''
|
||||||
|
inlineTableStart = '{'
|
||||||
|
inlineTableEnd = '}'
|
||||||
)
|
)
|
||||||
|
|
||||||
type stateFn func(lx *lexer) stateFn
|
type stateFn func(lx *lexer) stateFn
|
||||||
@ -56,11 +60,18 @@ type lexer struct {
|
|||||||
input string
|
input string
|
||||||
start int
|
start int
|
||||||
pos int
|
pos int
|
||||||
width int
|
|
||||||
line int
|
line int
|
||||||
state stateFn
|
state stateFn
|
||||||
items chan item
|
items chan item
|
||||||
|
|
||||||
|
// Allow for backing up up to three runes.
|
||||||
|
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
||||||
|
prevWidths [3]int
|
||||||
|
nprev int // how many of prevWidths are in use
|
||||||
|
// If we emit an eof, we can still back up, but it is not OK to call
|
||||||
|
// next again.
|
||||||
|
atEOF bool
|
||||||
|
|
||||||
// A stack of state functions used to maintain context.
|
// A stack of state functions used to maintain context.
|
||||||
// The idea is to reuse parts of the state machine in various places.
|
// The idea is to reuse parts of the state machine in various places.
|
||||||
// For example, values can appear at the top level or within arbitrarily
|
// For example, values can appear at the top level or within arbitrarily
|
||||||
@ -88,7 +99,7 @@ func (lx *lexer) nextItem() item {
|
|||||||
|
|
||||||
func lex(input string) *lexer {
|
func lex(input string) *lexer {
|
||||||
lx := &lexer{
|
lx := &lexer{
|
||||||
input: input + "\n",
|
input: input,
|
||||||
state: lexTop,
|
state: lexTop,
|
||||||
line: 1,
|
line: 1,
|
||||||
items: make(chan item, 10),
|
items: make(chan item, 10),
|
||||||
@ -103,7 +114,7 @@ func (lx *lexer) push(state stateFn) {
|
|||||||
|
|
||||||
func (lx *lexer) pop() stateFn {
|
func (lx *lexer) pop() stateFn {
|
||||||
if len(lx.stack) == 0 {
|
if len(lx.stack) == 0 {
|
||||||
return lx.errorf("BUG in lexer: no states to pop.")
|
return lx.errorf("BUG in lexer: no states to pop")
|
||||||
}
|
}
|
||||||
last := lx.stack[len(lx.stack)-1]
|
last := lx.stack[len(lx.stack)-1]
|
||||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
||||||
@ -125,16 +136,25 @@ func (lx *lexer) emitTrim(typ itemType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lx *lexer) next() (r rune) {
|
func (lx *lexer) next() (r rune) {
|
||||||
|
if lx.atEOF {
|
||||||
|
panic("next called after EOF")
|
||||||
|
}
|
||||||
if lx.pos >= len(lx.input) {
|
if lx.pos >= len(lx.input) {
|
||||||
lx.width = 0
|
lx.atEOF = true
|
||||||
return eof
|
return eof
|
||||||
}
|
}
|
||||||
|
|
||||||
if lx.input[lx.pos] == '\n' {
|
if lx.input[lx.pos] == '\n' {
|
||||||
lx.line++
|
lx.line++
|
||||||
}
|
}
|
||||||
r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:])
|
lx.prevWidths[2] = lx.prevWidths[1]
|
||||||
lx.pos += lx.width
|
lx.prevWidths[1] = lx.prevWidths[0]
|
||||||
|
if lx.nprev < 3 {
|
||||||
|
lx.nprev++
|
||||||
|
}
|
||||||
|
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
||||||
|
lx.prevWidths[0] = w
|
||||||
|
lx.pos += w
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +163,20 @@ func (lx *lexer) ignore() {
|
|||||||
lx.start = lx.pos
|
lx.start = lx.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// backup steps back one rune. Can be called only once per call of next.
|
// backup steps back one rune. Can be called only twice between calls to next.
|
||||||
func (lx *lexer) backup() {
|
func (lx *lexer) backup() {
|
||||||
lx.pos -= lx.width
|
if lx.atEOF {
|
||||||
|
lx.atEOF = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lx.nprev < 1 {
|
||||||
|
panic("backed up too far")
|
||||||
|
}
|
||||||
|
w := lx.prevWidths[0]
|
||||||
|
lx.prevWidths[0] = lx.prevWidths[1]
|
||||||
|
lx.prevWidths[1] = lx.prevWidths[2]
|
||||||
|
lx.nprev--
|
||||||
|
lx.pos -= w
|
||||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
||||||
lx.line--
|
lx.line--
|
||||||
}
|
}
|
||||||
@ -182,7 +213,7 @@ func (lx *lexer) skip(pred func(rune) bool) {
|
|||||||
|
|
||||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
// errorf stops all lexing by emitting an error and returning `nil`.
|
||||||
// Note that any value that is a character is escaped if it's a special
|
// Note that any value that is a character is escaped if it's a special
|
||||||
// character (new lines, tabs, etc.).
|
// character (newlines, tabs, etc.).
|
||||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
||||||
lx.items <- item{
|
lx.items <- item{
|
||||||
itemError,
|
itemError,
|
||||||
@ -198,7 +229,6 @@ func lexTop(lx *lexer) stateFn {
|
|||||||
if isWhitespace(r) || isNL(r) {
|
if isWhitespace(r) || isNL(r) {
|
||||||
return lexSkip(lx, lexTop)
|
return lexSkip(lx, lexTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r {
|
switch r {
|
||||||
case commentStart:
|
case commentStart:
|
||||||
lx.push(lexTop)
|
lx.push(lexTop)
|
||||||
@ -207,7 +237,7 @@ func lexTop(lx *lexer) stateFn {
|
|||||||
return lexTableStart
|
return lexTableStart
|
||||||
case eof:
|
case eof:
|
||||||
if lx.pos > lx.start {
|
if lx.pos > lx.start {
|
||||||
return lx.errorf("Unexpected EOF.")
|
return lx.errorf("unexpected EOF")
|
||||||
}
|
}
|
||||||
lx.emit(itemEOF)
|
lx.emit(itemEOF)
|
||||||
return nil
|
return nil
|
||||||
@ -222,12 +252,12 @@ func lexTop(lx *lexer) stateFn {
|
|||||||
|
|
||||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
||||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
// or a table.) It must see only whitespace, and will turn back to lexTop
|
||||||
// upon a new line. If it sees EOF, it will quit the lexer successfully.
|
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
||||||
func lexTopEnd(lx *lexer) stateFn {
|
func lexTopEnd(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case r == commentStart:
|
case r == commentStart:
|
||||||
// a comment will read to a new line for us.
|
// a comment will read to a newline for us.
|
||||||
lx.push(lexTop)
|
lx.push(lexTop)
|
||||||
return lexCommentStart
|
return lexCommentStart
|
||||||
case isWhitespace(r):
|
case isWhitespace(r):
|
||||||
@ -236,11 +266,11 @@ func lexTopEnd(lx *lexer) stateFn {
|
|||||||
lx.ignore()
|
lx.ignore()
|
||||||
return lexTop
|
return lexTop
|
||||||
case r == eof:
|
case r == eof:
|
||||||
lx.ignore()
|
lx.emit(itemEOF)
|
||||||
return lexTop
|
return nil
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected a top-level item to end with a new line, "+
|
return lx.errorf("expected a top-level item to end with a newline, "+
|
||||||
"comment or EOF, but got %q instead.", r)
|
"comment, or EOF, but got %q instead", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
||||||
@ -267,8 +297,8 @@ func lexTableEnd(lx *lexer) stateFn {
|
|||||||
|
|
||||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
func lexArrayTableEnd(lx *lexer) stateFn {
|
||||||
if r := lx.next(); r != arrayTableEnd {
|
if r := lx.next(); r != arrayTableEnd {
|
||||||
return lx.errorf("Expected end of table array name delimiter %q, "+
|
return lx.errorf("expected end of table array name delimiter %q, "+
|
||||||
"but got %q instead.", arrayTableEnd, r)
|
"but got %q instead", arrayTableEnd, r)
|
||||||
}
|
}
|
||||||
lx.emit(itemArrayTableEnd)
|
lx.emit(itemArrayTableEnd)
|
||||||
return lexTopEnd
|
return lexTopEnd
|
||||||
@ -278,11 +308,11 @@ func lexTableNameStart(lx *lexer) stateFn {
|
|||||||
lx.skip(isWhitespace)
|
lx.skip(isWhitespace)
|
||||||
switch r := lx.peek(); {
|
switch r := lx.peek(); {
|
||||||
case r == tableEnd || r == eof:
|
case r == tableEnd || r == eof:
|
||||||
return lx.errorf("Unexpected end of table name. (Table names cannot " +
|
return lx.errorf("unexpected end of table name " +
|
||||||
"be empty.)")
|
"(table names cannot be empty)")
|
||||||
case r == tableSep:
|
case r == tableSep:
|
||||||
return lx.errorf("Unexpected table separator. (Table names cannot " +
|
return lx.errorf("unexpected table separator " +
|
||||||
"be empty.)")
|
"(table names cannot be empty)")
|
||||||
case r == stringStart || r == rawStringStart:
|
case r == stringStart || r == rawStringStart:
|
||||||
lx.ignore()
|
lx.ignore()
|
||||||
lx.push(lexTableNameEnd)
|
lx.push(lexTableNameEnd)
|
||||||
@ -317,8 +347,8 @@ func lexTableNameEnd(lx *lexer) stateFn {
|
|||||||
case r == tableEnd:
|
case r == tableEnd:
|
||||||
return lx.pop()
|
return lx.pop()
|
||||||
default:
|
default:
|
||||||
return lx.errorf("Expected '.' or ']' to end table name, but got %q "+
|
return lx.errorf("expected '.' or ']' to end table name, "+
|
||||||
"instead.", r)
|
"but got %q instead", r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +358,7 @@ func lexKeyStart(lx *lexer) stateFn {
|
|||||||
r := lx.peek()
|
r := lx.peek()
|
||||||
switch {
|
switch {
|
||||||
case r == keySep:
|
case r == keySep:
|
||||||
return lx.errorf("Unexpected key separator %q.", keySep)
|
return lx.errorf("unexpected key separator %q", keySep)
|
||||||
case isWhitespace(r) || isNL(r):
|
case isWhitespace(r) || isNL(r):
|
||||||
lx.next()
|
lx.next()
|
||||||
return lexSkip(lx, lexKeyStart)
|
return lexSkip(lx, lexKeyStart)
|
||||||
@ -359,7 +389,7 @@ func lexBareKey(lx *lexer) stateFn {
|
|||||||
lx.emit(itemText)
|
lx.emit(itemText)
|
||||||
return lexKeyEnd
|
return lexKeyEnd
|
||||||
default:
|
default:
|
||||||
return lx.errorf("Bare keys cannot contain %q.", r)
|
return lx.errorf("bare keys cannot contain %q", r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,7 +402,7 @@ func lexKeyEnd(lx *lexer) stateFn {
|
|||||||
case isWhitespace(r):
|
case isWhitespace(r):
|
||||||
return lexSkip(lx, lexKeyEnd)
|
return lexSkip(lx, lexKeyEnd)
|
||||||
default:
|
default:
|
||||||
return lx.errorf("Expected key separator %q, but got %q instead.",
|
return lx.errorf("expected key separator %q, but got %q instead",
|
||||||
keySep, r)
|
keySep, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,9 +411,8 @@ func lexKeyEnd(lx *lexer) stateFn {
|
|||||||
// lexValue will ignore whitespace.
|
// lexValue will ignore whitespace.
|
||||||
// After a value is lexed, the last state on the next is popped and returned.
|
// After a value is lexed, the last state on the next is popped and returned.
|
||||||
func lexValue(lx *lexer) stateFn {
|
func lexValue(lx *lexer) stateFn {
|
||||||
// We allow whitespace to precede a value, but NOT new lines.
|
// We allow whitespace to precede a value, but NOT newlines.
|
||||||
// In array syntax, the array states are responsible for ignoring new
|
// In array syntax, the array states are responsible for ignoring newlines.
|
||||||
// lines.
|
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isWhitespace(r):
|
case isWhitespace(r):
|
||||||
@ -397,6 +426,10 @@ func lexValue(lx *lexer) stateFn {
|
|||||||
lx.ignore()
|
lx.ignore()
|
||||||
lx.emit(itemArray)
|
lx.emit(itemArray)
|
||||||
return lexArrayValue
|
return lexArrayValue
|
||||||
|
case inlineTableStart:
|
||||||
|
lx.ignore()
|
||||||
|
lx.emit(itemInlineTableStart)
|
||||||
|
return lexInlineTableValue
|
||||||
case stringStart:
|
case stringStart:
|
||||||
if lx.accept(stringStart) {
|
if lx.accept(stringStart) {
|
||||||
if lx.accept(stringStart) {
|
if lx.accept(stringStart) {
|
||||||
@ -420,7 +453,7 @@ func lexValue(lx *lexer) stateFn {
|
|||||||
case '+', '-':
|
case '+', '-':
|
||||||
return lexNumberStart
|
return lexNumberStart
|
||||||
case '.': // special error case, be kind to users
|
case '.': // special error case, be kind to users
|
||||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
return lx.errorf("floats must start with a digit, not '.'")
|
||||||
}
|
}
|
||||||
if unicode.IsLetter(r) {
|
if unicode.IsLetter(r) {
|
||||||
// Be permissive here; lexBool will give a nice error if the
|
// Be permissive here; lexBool will give a nice error if the
|
||||||
@ -430,11 +463,11 @@ func lexValue(lx *lexer) stateFn {
|
|||||||
lx.backup()
|
lx.backup()
|
||||||
return lexBool
|
return lexBool
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected value but found %q instead.", r)
|
return lx.errorf("expected value but found %q instead", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
||||||
// have already been consumed. All whitespace and new lines are ignored.
|
// have already been consumed. All whitespace and newlines are ignored.
|
||||||
func lexArrayValue(lx *lexer) stateFn {
|
func lexArrayValue(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
@ -443,10 +476,11 @@ func lexArrayValue(lx *lexer) stateFn {
|
|||||||
case r == commentStart:
|
case r == commentStart:
|
||||||
lx.push(lexArrayValue)
|
lx.push(lexArrayValue)
|
||||||
return lexCommentStart
|
return lexCommentStart
|
||||||
case r == arrayValTerm:
|
case r == comma:
|
||||||
return lx.errorf("Unexpected array value terminator %q.",
|
return lx.errorf("unexpected comma")
|
||||||
arrayValTerm)
|
|
||||||
case r == arrayEnd:
|
case r == arrayEnd:
|
||||||
|
// NOTE(caleb): The spec isn't clear about whether you can have
|
||||||
|
// a trailing comma or not, so we'll allow it.
|
||||||
return lexArrayEnd
|
return lexArrayEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,8 +489,9 @@ func lexArrayValue(lx *lexer) stateFn {
|
|||||||
return lexValue
|
return lexValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexArrayValueEnd consumes the cruft between values of an array. Namely,
|
// lexArrayValueEnd consumes everything between the end of an array value and
|
||||||
// it ignores whitespace and expects either a ',' or a ']'.
|
// the next value (or the end of the array): it ignores whitespace and newlines
|
||||||
|
// and expects either a ',' or a ']'.
|
||||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
func lexArrayValueEnd(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
@ -465,31 +500,88 @@ func lexArrayValueEnd(lx *lexer) stateFn {
|
|||||||
case r == commentStart:
|
case r == commentStart:
|
||||||
lx.push(lexArrayValueEnd)
|
lx.push(lexArrayValueEnd)
|
||||||
return lexCommentStart
|
return lexCommentStart
|
||||||
case r == arrayValTerm:
|
case r == comma:
|
||||||
lx.ignore()
|
lx.ignore()
|
||||||
return lexArrayValue // move on to the next value
|
return lexArrayValue // move on to the next value
|
||||||
case r == arrayEnd:
|
case r == arrayEnd:
|
||||||
return lexArrayEnd
|
return lexArrayEnd
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected an array value terminator %q or an array "+
|
return lx.errorf(
|
||||||
"terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r)
|
"expected a comma or array terminator %q, but got %q instead",
|
||||||
|
arrayEnd, r,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has
|
// lexArrayEnd finishes the lexing of an array.
|
||||||
// just been consumed.
|
// It assumes that a ']' has just been consumed.
|
||||||
func lexArrayEnd(lx *lexer) stateFn {
|
func lexArrayEnd(lx *lexer) stateFn {
|
||||||
lx.ignore()
|
lx.ignore()
|
||||||
lx.emit(itemArrayEnd)
|
lx.emit(itemArrayEnd)
|
||||||
return lx.pop()
|
return lx.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lexInlineTableValue consumes one key/value pair in an inline table.
|
||||||
|
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
||||||
|
func lexInlineTableValue(lx *lexer) stateFn {
|
||||||
|
r := lx.next()
|
||||||
|
switch {
|
||||||
|
case isWhitespace(r):
|
||||||
|
return lexSkip(lx, lexInlineTableValue)
|
||||||
|
case isNL(r):
|
||||||
|
return lx.errorf("newlines not allowed within inline tables")
|
||||||
|
case r == commentStart:
|
||||||
|
lx.push(lexInlineTableValue)
|
||||||
|
return lexCommentStart
|
||||||
|
case r == comma:
|
||||||
|
return lx.errorf("unexpected comma")
|
||||||
|
case r == inlineTableEnd:
|
||||||
|
return lexInlineTableEnd
|
||||||
|
}
|
||||||
|
lx.backup()
|
||||||
|
lx.push(lexInlineTableValueEnd)
|
||||||
|
return lexKeyStart
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
||||||
|
// key/value pair and the next pair (or the end of the table):
|
||||||
|
// it ignores whitespace and expects either a ',' or a '}'.
|
||||||
|
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||||
|
r := lx.next()
|
||||||
|
switch {
|
||||||
|
case isWhitespace(r):
|
||||||
|
return lexSkip(lx, lexInlineTableValueEnd)
|
||||||
|
case isNL(r):
|
||||||
|
return lx.errorf("newlines not allowed within inline tables")
|
||||||
|
case r == commentStart:
|
||||||
|
lx.push(lexInlineTableValueEnd)
|
||||||
|
return lexCommentStart
|
||||||
|
case r == comma:
|
||||||
|
lx.ignore()
|
||||||
|
return lexInlineTableValue
|
||||||
|
case r == inlineTableEnd:
|
||||||
|
return lexInlineTableEnd
|
||||||
|
}
|
||||||
|
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
||||||
|
"but got %q instead", inlineTableEnd, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexInlineTableEnd finishes the lexing of an inline table.
|
||||||
|
// It assumes that a '}' has just been consumed.
|
||||||
|
func lexInlineTableEnd(lx *lexer) stateFn {
|
||||||
|
lx.ignore()
|
||||||
|
lx.emit(itemInlineTableEnd)
|
||||||
|
return lx.pop()
|
||||||
|
}
|
||||||
|
|
||||||
// lexString consumes the inner contents of a string. It assumes that the
|
// lexString consumes the inner contents of a string. It assumes that the
|
||||||
// beginning '"' has already been consumed and ignored.
|
// beginning '"' has already been consumed and ignored.
|
||||||
func lexString(lx *lexer) stateFn {
|
func lexString(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
|
case r == eof:
|
||||||
|
return lx.errorf("unexpected EOF")
|
||||||
case isNL(r):
|
case isNL(r):
|
||||||
return lx.errorf("Strings cannot contain new lines.")
|
return lx.errorf("strings cannot contain newlines")
|
||||||
case r == '\\':
|
case r == '\\':
|
||||||
lx.push(lexString)
|
lx.push(lexString)
|
||||||
return lexStringEscape
|
return lexStringEscape
|
||||||
@ -506,11 +598,12 @@ func lexString(lx *lexer) stateFn {
|
|||||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
// lexMultilineString consumes the inner contents of a string. It assumes that
|
||||||
// the beginning '"""' has already been consumed and ignored.
|
// the beginning '"""' has already been consumed and ignored.
|
||||||
func lexMultilineString(lx *lexer) stateFn {
|
func lexMultilineString(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
switch lx.next() {
|
||||||
switch {
|
case eof:
|
||||||
case r == '\\':
|
return lx.errorf("unexpected EOF")
|
||||||
|
case '\\':
|
||||||
return lexMultilineStringEscape
|
return lexMultilineStringEscape
|
||||||
case r == stringEnd:
|
case stringEnd:
|
||||||
if lx.accept(stringEnd) {
|
if lx.accept(stringEnd) {
|
||||||
if lx.accept(stringEnd) {
|
if lx.accept(stringEnd) {
|
||||||
lx.backup()
|
lx.backup()
|
||||||
@ -534,8 +627,10 @@ func lexMultilineString(lx *lexer) stateFn {
|
|||||||
func lexRawString(lx *lexer) stateFn {
|
func lexRawString(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
|
case r == eof:
|
||||||
|
return lx.errorf("unexpected EOF")
|
||||||
case isNL(r):
|
case isNL(r):
|
||||||
return lx.errorf("Strings cannot contain new lines.")
|
return lx.errorf("strings cannot contain newlines")
|
||||||
case r == rawStringEnd:
|
case r == rawStringEnd:
|
||||||
lx.backup()
|
lx.backup()
|
||||||
lx.emit(itemRawString)
|
lx.emit(itemRawString)
|
||||||
@ -547,12 +642,13 @@ func lexRawString(lx *lexer) stateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
||||||
// a string. It assumes that the beginning "'" has already been consumed and
|
// a string. It assumes that the beginning "'''" has already been consumed and
|
||||||
// ignored.
|
// ignored.
|
||||||
func lexMultilineRawString(lx *lexer) stateFn {
|
func lexMultilineRawString(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
switch lx.next() {
|
||||||
switch {
|
case eof:
|
||||||
case r == rawStringEnd:
|
return lx.errorf("unexpected EOF")
|
||||||
|
case rawStringEnd:
|
||||||
if lx.accept(rawStringEnd) {
|
if lx.accept(rawStringEnd) {
|
||||||
if lx.accept(rawStringEnd) {
|
if lx.accept(rawStringEnd) {
|
||||||
lx.backup()
|
lx.backup()
|
||||||
@ -605,10 +701,9 @@ func lexStringEscape(lx *lexer) stateFn {
|
|||||||
case 'U':
|
case 'U':
|
||||||
return lexLongUnicodeEscape
|
return lexLongUnicodeEscape
|
||||||
}
|
}
|
||||||
return lx.errorf("Invalid escape character %q. Only the following "+
|
return lx.errorf("invalid escape character %q; only the following "+
|
||||||
"escape characters are allowed: "+
|
"escape characters are allowed: "+
|
||||||
"\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, "+
|
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
||||||
"\\uXXXX and \\UXXXXXXXX.", r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
||||||
@ -616,8 +711,8 @@ func lexShortUnicodeEscape(lx *lexer) stateFn {
|
|||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
r = lx.next()
|
r = lx.next()
|
||||||
if !isHexadecimal(r) {
|
if !isHexadecimal(r) {
|
||||||
return lx.errorf("Expected four hexadecimal digits after '\\u', "+
|
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
||||||
"but got '%s' instead.", lx.current())
|
"but got %q instead", lx.current())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lx.pop()
|
return lx.pop()
|
||||||
@ -628,8 +723,8 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
|
|||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
r = lx.next()
|
r = lx.next()
|
||||||
if !isHexadecimal(r) {
|
if !isHexadecimal(r) {
|
||||||
return lx.errorf("Expected eight hexadecimal digits after '\\U', "+
|
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
||||||
"but got '%s' instead.", lx.current())
|
"but got %q instead", lx.current())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lx.pop()
|
return lx.pop()
|
||||||
@ -647,9 +742,9 @@ func lexNumberOrDateStart(lx *lexer) stateFn {
|
|||||||
case 'e', 'E':
|
case 'e', 'E':
|
||||||
return lexFloat
|
return lexFloat
|
||||||
case '.':
|
case '.':
|
||||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
return lx.errorf("floats must start with a digit, not '.'")
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected a digit but got %q.", r)
|
return lx.errorf("expected a digit but got %q", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
// lexNumberOrDate consumes either an integer, float or datetime.
|
||||||
@ -697,9 +792,9 @@ func lexNumberStart(lx *lexer) stateFn {
|
|||||||
r := lx.next()
|
r := lx.next()
|
||||||
if !isDigit(r) {
|
if !isDigit(r) {
|
||||||
if r == '.' {
|
if r == '.' {
|
||||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
return lx.errorf("floats must start with a digit, not '.'")
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected a digit but got %q.", r)
|
return lx.errorf("expected a digit but got %q", r)
|
||||||
}
|
}
|
||||||
return lexNumber
|
return lexNumber
|
||||||
}
|
}
|
||||||
@ -757,7 +852,7 @@ func lexBool(lx *lexer) stateFn {
|
|||||||
lx.emit(itemBool)
|
lx.emit(itemBool)
|
||||||
return lx.pop()
|
return lx.pop()
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected value but found %q instead.", s)
|
return lx.errorf("expected value but found %q instead", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexCommentStart begins the lexing of a comment. It will emit
|
// lexCommentStart begins the lexing of a comment. It will emit
|
||||||
@ -769,7 +864,7 @@ func lexCommentStart(lx *lexer) stateFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
||||||
// It will consume *up to* the first new line character, and pass control
|
// It will consume *up to* the first newline character, and pass control
|
||||||
// back to the last state on the stack.
|
// back to the last state on the stack.
|
||||||
func lexComment(lx *lexer) stateFn {
|
func lexComment(lx *lexer) stateFn {
|
||||||
r := lx.peek()
|
r := lx.peek()
|
||||||
|
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
@ -269,6 +269,41 @@ func (p *parser) value(it item) (interface{}, tomlType) {
|
|||||||
types = append(types, typ)
|
types = append(types, typ)
|
||||||
}
|
}
|
||||||
return array, p.typeOfArray(types)
|
return array, p.typeOfArray(types)
|
||||||
|
case itemInlineTableStart:
|
||||||
|
var (
|
||||||
|
hash = make(map[string]interface{})
|
||||||
|
outerContext = p.context
|
||||||
|
outerKey = p.currentKey
|
||||||
|
)
|
||||||
|
|
||||||
|
p.context = append(p.context, p.currentKey)
|
||||||
|
p.currentKey = ""
|
||||||
|
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
||||||
|
if it.typ != itemKeyStart {
|
||||||
|
p.bug("Expected key start but instead found %q, around line %d",
|
||||||
|
it.val, p.approxLine)
|
||||||
|
}
|
||||||
|
if it.typ == itemCommentStart {
|
||||||
|
p.expect(itemText)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve key
|
||||||
|
k := p.next()
|
||||||
|
p.approxLine = k.line
|
||||||
|
kname := p.keyString(k)
|
||||||
|
|
||||||
|
// retrieve value
|
||||||
|
p.currentKey = kname
|
||||||
|
val, typ := p.value(p.next())
|
||||||
|
// make sure we keep metadata up to date
|
||||||
|
p.setType(kname, typ)
|
||||||
|
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
||||||
|
hash[kname] = val
|
||||||
|
}
|
||||||
|
p.context = outerContext
|
||||||
|
p.currentKey = outerKey
|
||||||
|
return hash, tomlHash
|
||||||
}
|
}
|
||||||
p.bug("Unexpected value type: %s", it.typ)
|
p.bug("Unexpected value type: %s", it.typ)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
|
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
// The following code was sourced and modified from the
|
||||||
|
// https://github.com/tebeka/atexit package governed by the following license:
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var handlers = []func(){}
|
||||||
|
|
||||||
|
func runHandler(handler func()) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHandlers() {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
runHandler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||||
|
func Exit(code int) {
|
||||||
|
runHandlers()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||||
|
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||||
|
// made.
|
||||||
|
//
|
||||||
|
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||||
|
// message but also needs to gracefully shutdown. An example usecase could be
|
||||||
|
// closing database connections, or sending a alert that the application is
|
||||||
|
// closing.
|
||||||
|
func RegisterExitHandler(handler func()) {
|
||||||
|
handlers = append(handlers, handler)
|
||||||
|
}
|
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
@ -3,11 +3,21 @@ package logrus
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var bufferPool *sync.Pool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Defines the key when adding errors using WithError.
|
// Defines the key when adding errors using WithError.
|
||||||
var ErrorKey = "error"
|
var ErrorKey = "error"
|
||||||
|
|
||||||
@ -29,6 +39,9 @@ type Entry struct {
|
|||||||
|
|
||||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||||
Message string
|
Message string
|
||||||
|
|
||||||
|
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||||
|
Buffer *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEntry(logger *Logger) *Entry {
|
func NewEntry(logger *Logger) *Entry {
|
||||||
@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
|
||||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
|
||||||
return bytes.NewBuffer(serialized), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string representation from the reader and ultimately the
|
// Returns the string representation from the reader and ultimately the
|
||||||
// formatter.
|
// formatter.
|
||||||
func (entry *Entry) String() (string, error) {
|
func (entry *Entry) String() (string, error) {
|
||||||
reader, err := entry.Reader()
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
str := string(serialized)
|
||||||
return reader.String(), err
|
return str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||||
@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
|||||||
// This function is not declared with a pointer value because otherwise
|
// This function is not declared with a pointer value because otherwise
|
||||||
// race conditions will occur when using multiple goroutines
|
// race conditions will occur when using multiple goroutines
|
||||||
func (entry Entry) log(level Level, msg string) {
|
func (entry Entry) log(level Level, msg string) {
|
||||||
|
var buffer *bytes.Buffer
|
||||||
entry.Time = time.Now()
|
entry.Time = time.Now()
|
||||||
entry.Level = level
|
entry.Level = level
|
||||||
entry.Message = msg
|
entry.Message = msg
|
||||||
@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) {
|
|||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
entry.Logger.mu.Unlock()
|
entry.Logger.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||||
reader, err := entry.Reader()
|
buffer.Reset()
|
||||||
|
defer bufferPool.Put(buffer)
|
||||||
|
entry.Buffer = buffer
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||||
|
entry.Buffer = nil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
entry.Logger.mu.Lock()
|
entry.Logger.mu.Lock()
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
entry.Logger.mu.Unlock()
|
entry.Logger.mu.Unlock()
|
||||||
}
|
} else {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
entry.Logger.mu.Lock()
|
_, err = entry.Logger.Out.Write(serialized)
|
||||||
defer entry.Logger.mu.Unlock()
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
_, err = io.Copy(entry.Logger.Out, reader)
|
}
|
||||||
if err != nil {
|
entry.Logger.mu.Unlock()
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
|||||||
if entry.Logger.Level >= FatalLevel {
|
if entry.Logger.Level >= FatalLevel {
|
||||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|||||||
if entry.Logger.Level >= FatalLevel {
|
if entry.Logger.Level >= FatalLevel {
|
||||||
entry.Fatal(fmt.Sprintf(format, args...))
|
entry.Fatal(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||||
@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
|||||||
if entry.Logger.Level >= FatalLevel {
|
if entry.Logger.Level >= FatalLevel {
|
||||||
entry.Fatal(entry.sprintlnn(args...))
|
entry.Fatal(entry.sprintlnn(args...))
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Panicln(args ...interface{}) {
|
func (entry *Entry) Panicln(args ...interface{}) {
|
||||||
|
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
// "os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logrus.New()
|
var log = logrus.New()
|
||||||
@ -9,6 +10,14 @@ var log = logrus.New()
|
|||||||
func init() {
|
func init() {
|
||||||
log.Formatter = new(logrus.JSONFormatter)
|
log.Formatter = new(logrus.JSONFormatter)
|
||||||
log.Formatter = new(logrus.TextFormatter) // default
|
log.Formatter = new(logrus.TextFormatter) // default
|
||||||
|
|
||||||
|
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
// if err == nil {
|
||||||
|
// log.Out = file
|
||||||
|
// } else {
|
||||||
|
// log.Info("Failed to log to file, using default stderr")
|
||||||
|
// }
|
||||||
|
|
||||||
log.Level = logrus.DebugLevel
|
log.Level = logrus.DebugLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
@ -31,18 +31,15 @@ type Formatter interface {
|
|||||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||||
// avoid code duplication between the two default formatters.
|
// avoid code duplication between the two default formatters.
|
||||||
func prefixFieldClashes(data Fields) {
|
func prefixFieldClashes(data Fields) {
|
||||||
_, ok := data["time"]
|
if t, ok := data["time"]; ok {
|
||||||
if ok {
|
data["fields.time"] = t
|
||||||
data["fields.time"] = data["time"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = data["msg"]
|
if m, ok := data["msg"]; ok {
|
||||||
if ok {
|
data["fields.msg"] = m
|
||||||
data["fields.msg"] = data["msg"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = data["level"]
|
if l, ok := data["level"]; ok {
|
||||||
if ok {
|
data["fields.level"] = l
|
||||||
data["fields.level"] = data["level"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
package logstash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Formatter generates json in logstash format.
|
|
||||||
// Logstash site: http://logstash.net/
|
|
||||||
type LogstashFormatter struct {
|
|
||||||
Type string // if not empty use for logstash type field.
|
|
||||||
|
|
||||||
// TimestampFormat sets the format used for timestamps.
|
|
||||||
TimestampFormat string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|
||||||
fields := make(logrus.Fields)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
fields[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
fields["@version"] = 1
|
|
||||||
|
|
||||||
if f.TimestampFormat == "" {
|
|
||||||
f.TimestampFormat = logrus.DefaultTimestampFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
fields["@timestamp"] = entry.Time.Format(f.TimestampFormat)
|
|
||||||
|
|
||||||
// set message field
|
|
||||||
v, ok := entry.Data["message"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.message"] = v
|
|
||||||
}
|
|
||||||
fields["message"] = entry.Message
|
|
||||||
|
|
||||||
// set level field
|
|
||||||
v, ok = entry.Data["level"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.level"] = v
|
|
||||||
}
|
|
||||||
fields["level"] = entry.Level.String()
|
|
||||||
|
|
||||||
// set type field
|
|
||||||
if f.Type != "" {
|
|
||||||
v, ok = entry.Data["type"]
|
|
||||||
if ok {
|
|
||||||
fields["fields.type"] = v
|
|
||||||
}
|
|
||||||
fields["type"] = f.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
serialized, err := json.Marshal(fields)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
@ -5,9 +5,40 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type fieldKey string
|
||||||
|
type FieldMap map[fieldKey]string
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldKeyMsg = "msg"
|
||||||
|
FieldKeyLevel = "level"
|
||||||
|
FieldKeyTime = "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f FieldMap) resolve(key fieldKey) string {
|
||||||
|
if k, ok := f[key]; ok {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(key)
|
||||||
|
}
|
||||||
|
|
||||||
type JSONFormatter struct {
|
type JSONFormatter struct {
|
||||||
// TimestampFormat sets the format used for marshaling timestamps.
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
TimestampFormat string
|
TimestampFormat string
|
||||||
|
|
||||||
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for various fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &JSONFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyLevel: "@message",
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
FieldMap FieldMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
@ -29,9 +60,11 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|||||||
timestampFormat = DefaultTimestampFormat
|
timestampFormat = DefaultTimestampFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
data["time"] = entry.Time.Format(timestampFormat)
|
if !f.DisableTimestamp {
|
||||||
data["msg"] = entry.Message
|
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||||
data["level"] = entry.Level.String()
|
}
|
||||||
|
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||||
|
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||||
|
|
||||||
serialized, err := json.Marshal(data)
|
serialized, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
@ -26,8 +26,31 @@ type Logger struct {
|
|||||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||||
// logged. `logrus.Debug` is useful in
|
// logged. `logrus.Debug` is useful in
|
||||||
Level Level
|
Level Level
|
||||||
// Used to sync writing to the log.
|
// Used to sync writing to the log. Locking is enabled by Default
|
||||||
mu sync.Mutex
|
mu MutexWrap
|
||||||
|
// Reusable empty entry
|
||||||
|
entryPool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
type MutexWrap struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
disabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Lock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Unlock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Disable() {
|
||||||
|
mw.disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||||
@ -51,162 +74,235 @@ func New() *Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
func (logger *Logger) newEntry() *Entry {
|
||||||
|
entry, ok := logger.entryPool.Get().(*Entry)
|
||||||
|
if ok {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return NewEntry(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||||
|
logger.entryPool.Put(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a field to the log entry, note that it doesn't log until you call
|
||||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||||
// If you want multiple fields, use `WithFields`.
|
// If you want multiple fields, use `WithFields`.
|
||||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
return NewEntry(logger).WithField(key, value)
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithField(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||||
// each `Field`.
|
// each `Field`.
|
||||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||||
return NewEntry(logger).WithFields(fields)
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithFields(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an error as single field to the log entry. All it does is call
|
// Add an error as single field to the log entry. All it does is call
|
||||||
// `WithError` for the given `error`.
|
// `WithError` for the given `error`.
|
||||||
func (logger *Logger) WithError(err error) *Entry {
|
func (logger *Logger) WithError(err error) *Entry {
|
||||||
return NewEntry(logger).WithError(err)
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
if logger.Level >= DebugLevel {
|
if logger.Level >= DebugLevel {
|
||||||
NewEntry(logger).Debugf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Debugf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
if logger.Level >= InfoLevel {
|
if logger.Level >= InfoLevel {
|
||||||
NewEntry(logger).Infof(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
NewEntry(logger).Printf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Printf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warnf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warnf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
if logger.Level >= ErrorLevel {
|
if logger.Level >= ErrorLevel {
|
||||||
NewEntry(logger).Errorf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Errorf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
if logger.Level >= FatalLevel {
|
if logger.Level >= FatalLevel {
|
||||||
NewEntry(logger).Fatalf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
if logger.Level >= PanicLevel {
|
if logger.Level >= PanicLevel {
|
||||||
NewEntry(logger).Panicf(format, args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Panicf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Debug(args ...interface{}) {
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
if logger.Level >= DebugLevel {
|
if logger.Level >= DebugLevel {
|
||||||
NewEntry(logger).Debug(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Debug(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Info(args ...interface{}) {
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
if logger.Level >= InfoLevel {
|
if logger.Level >= InfoLevel {
|
||||||
NewEntry(logger).Info(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Print(args ...interface{}) {
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
NewEntry(logger).Info(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warn(args ...interface{}) {
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warn(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warning(args ...interface{}) {
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warn(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Error(args ...interface{}) {
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
if logger.Level >= ErrorLevel {
|
if logger.Level >= ErrorLevel {
|
||||||
NewEntry(logger).Error(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Error(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Fatal(args ...interface{}) {
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
if logger.Level >= FatalLevel {
|
if logger.Level >= FatalLevel {
|
||||||
NewEntry(logger).Fatal(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Fatal(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Panic(args ...interface{}) {
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
if logger.Level >= PanicLevel {
|
if logger.Level >= PanicLevel {
|
||||||
NewEntry(logger).Panic(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Panic(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Debugln(args ...interface{}) {
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
if logger.Level >= DebugLevel {
|
if logger.Level >= DebugLevel {
|
||||||
NewEntry(logger).Debugln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Debugln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Infoln(args ...interface{}) {
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
if logger.Level >= InfoLevel {
|
if logger.Level >= InfoLevel {
|
||||||
NewEntry(logger).Infoln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Infoln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Println(args ...interface{}) {
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
NewEntry(logger).Println(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Println(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warnln(args ...interface{}) {
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warnln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Warningln(args ...interface{}) {
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
if logger.Level >= WarnLevel {
|
if logger.Level >= WarnLevel {
|
||||||
NewEntry(logger).Warnln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Errorln(args ...interface{}) {
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
if logger.Level >= ErrorLevel {
|
if logger.Level >= ErrorLevel {
|
||||||
NewEntry(logger).Errorln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Errorln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
if logger.Level >= FatalLevel {
|
if logger.Level >= FatalLevel {
|
||||||
NewEntry(logger).Fatalln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) Panicln(args ...interface{}) {
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
if logger.Level >= PanicLevel {
|
if logger.Level >= PanicLevel {
|
||||||
NewEntry(logger).Panicln(args...)
|
entry := logger.newEntry()
|
||||||
|
entry.Panicln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//When file is opened with appending mode, it's safe to
|
||||||
|
//write concurrently to a file (within 4k message on Linux).
|
||||||
|
//In these cases user can choose to disable the lock.
|
||||||
|
func (logger *Logger) SetNoLock() {
|
||||||
|
logger.mu.Disable()
|
||||||
|
}
|
||||||
|
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
Normal file
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||||
|
func IsTerminal(f io.Writer) bool {
|
||||||
|
return true
|
||||||
|
}
|
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
@ -1,4 +1,5 @@
|
|||||||
// +build darwin freebsd openbsd netbsd dragonfly
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
@ -3,6 +3,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
@ -4,18 +4,25 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||||
func IsTerminal() bool {
|
func IsTerminal(f io.Writer) bool {
|
||||||
fd := syscall.Stderr
|
|
||||||
var termios Termios
|
var termios Termios
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
switch v := f.(type) {
|
||||||
return err == 0
|
case *os.File:
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
@ -1,15 +1,21 @@
|
|||||||
// +build solaris
|
// +build solaris,!appengine
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
func IsTerminal() bool {
|
func IsTerminal(f io.Writer) bool {
|
||||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
|
switch v := f.(type) {
|
||||||
return err == nil
|
case *os.File:
|
||||||
|
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
|
||||||
|
return err == nil
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
@ -3,11 +3,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build windows
|
// +build windows,!appengine
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -19,9 +21,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||||
func IsTerminal() bool {
|
func IsTerminal(f io.Writer) bool {
|
||||||
fd := syscall.Stderr
|
switch v := f.(type) {
|
||||||
var st uint32
|
case *os.File:
|
||||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
var st uint32
|
||||||
return r != 0 && e == 0
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
|
||||||
|
return r != 0 && e == 0
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
@ -3,9 +3,9 @@ package logrus
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,16 +20,10 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
baseTimestamp time.Time
|
baseTimestamp time.Time
|
||||||
isTerminal bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
baseTimestamp = time.Now()
|
baseTimestamp = time.Now()
|
||||||
isTerminal = IsTerminal()
|
|
||||||
}
|
|
||||||
|
|
||||||
func miniTS() int {
|
|
||||||
return int(time.Since(baseTimestamp) / time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextFormatter struct {
|
type TextFormatter struct {
|
||||||
@ -54,10 +48,32 @@ type TextFormatter struct {
|
|||||||
// that log extremely frequently and don't use the JSON formatter this may not
|
// that log extremely frequently and don't use the JSON formatter this may not
|
||||||
// be desired.
|
// be desired.
|
||||||
DisableSorting bool
|
DisableSorting bool
|
||||||
|
|
||||||
|
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||||
|
QuoteEmptyFields bool
|
||||||
|
|
||||||
|
// QuoteCharacter can be set to the override the default quoting character "
|
||||||
|
// with something else. For example: ', or `.
|
||||||
|
QuoteCharacter string
|
||||||
|
|
||||||
|
// Whether the logger's out is to a terminal
|
||||||
|
isTerminal bool
|
||||||
|
|
||||||
|
sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
|
if len(f.QuoteCharacter) == 0 {
|
||||||
|
f.QuoteCharacter = "\""
|
||||||
|
}
|
||||||
|
if entry.Logger != nil {
|
||||||
|
f.isTerminal = IsTerminal(entry.Logger.Out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
var keys []string = make([]string, 0, len(entry.Data))
|
var b *bytes.Buffer
|
||||||
|
keys := make([]string, 0, len(entry.Data))
|
||||||
for k := range entry.Data {
|
for k := range entry.Data {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
@ -65,13 +81,17 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
|||||||
if !f.DisableSorting {
|
if !f.DisableSorting {
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
}
|
}
|
||||||
|
if entry.Buffer != nil {
|
||||||
b := &bytes.Buffer{}
|
b = entry.Buffer
|
||||||
|
} else {
|
||||||
|
b = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
prefixFieldClashes(entry.Data)
|
prefixFieldClashes(entry.Data)
|
||||||
|
|
||||||
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
f.Do(func() { f.init(entry) })
|
||||||
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
|
||||||
|
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||||
|
|
||||||
timestampFormat := f.TimestampFormat
|
timestampFormat := f.TimestampFormat
|
||||||
if timestampFormat == "" {
|
if timestampFormat == "" {
|
||||||
@ -111,51 +131,59 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
|||||||
|
|
||||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||||
|
|
||||||
if !f.FullTimestamp {
|
if f.DisableTimestamp {
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||||
|
} else if !f.FullTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||||
}
|
}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
v := entry.Data[k]
|
v := entry.Data[k]
|
||||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||||
|
f.appendValue(b, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func needsQuoting(text string) bool {
|
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||||
|
if f.QuoteEmptyFields && len(text) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
for _, ch := range text {
|
for _, ch := range text {
|
||||||
if !((ch >= 'a' && ch <= 'z') ||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
(ch >= 'A' && ch <= 'Z') ||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
(ch >= '0' && ch <= '9') ||
|
(ch >= '0' && ch <= '9') ||
|
||||||
ch == '-' || ch == '.') {
|
ch == '-' || ch == '.') {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||||
|
|
||||||
b.WriteString(key)
|
b.WriteString(key)
|
||||||
b.WriteByte('=')
|
b.WriteByte('=')
|
||||||
|
f.appendValue(b, value)
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if needsQuoting(value) {
|
if !f.needsQuoting(value) {
|
||||||
b.WriteString(value)
|
b.WriteString(value)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(b, "%q", value)
|
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
|
||||||
}
|
}
|
||||||
case error:
|
case error:
|
||||||
errmsg := value.Error()
|
errmsg := value.Error()
|
||||||
if needsQuoting(errmsg) {
|
if !f.needsQuoting(errmsg) {
|
||||||
b.WriteString(errmsg)
|
b.WriteString(errmsg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(b, "%q", value)
|
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Fprint(b, value)
|
fmt.Fprint(b, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteByte(' ')
|
|
||||||
}
|
}
|
||||||
|
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
@ -7,21 +7,52 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (logger *Logger) Writer() *io.PipeWriter {
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
return logger.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
return NewEntry(logger).WriterLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
|
return entry.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
go logger.writerScanner(reader)
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
printFunc = entry.Debug
|
||||||
|
case InfoLevel:
|
||||||
|
printFunc = entry.Info
|
||||||
|
case WarnLevel:
|
||||||
|
printFunc = entry.Warn
|
||||||
|
case ErrorLevel:
|
||||||
|
printFunc = entry.Error
|
||||||
|
case FatalLevel:
|
||||||
|
printFunc = entry.Fatal
|
||||||
|
case PanicLevel:
|
||||||
|
printFunc = entry.Panic
|
||||||
|
default:
|
||||||
|
printFunc = entry.Print
|
||||||
|
}
|
||||||
|
|
||||||
|
go entry.writerScanner(reader, printFunc)
|
||||||
runtime.SetFinalizer(writer, writerFinalizer)
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
logger.Print(scanner.Text())
|
printFunc(scanner.Text())
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
logger.Errorf("Error while reading from Writer: %s", err)
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
}
|
}
|
||||||
reader.Close()
|
reader.Close()
|
||||||
}
|
}
|
||||||
|
17
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
17
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
@ -21,8 +21,10 @@ import (
|
|||||||
|
|
||||||
// BotAPI allows you to interact with the Telegram Bot API.
|
// BotAPI allows you to interact with the Telegram Bot API.
|
||||||
type BotAPI struct {
|
type BotAPI struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
|
Buffer int `json:"buffer"`
|
||||||
|
|
||||||
Self User `json:"-"`
|
Self User `json:"-"`
|
||||||
Client *http.Client `json:"-"`
|
Client *http.Client `json:"-"`
|
||||||
}
|
}
|
||||||
@ -42,11 +44,12 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
|
|||||||
bot := &BotAPI{
|
bot := &BotAPI{
|
||||||
Token: token,
|
Token: token,
|
||||||
Client: client,
|
Client: client,
|
||||||
|
Buffer: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
self, err := bot.GetMe()
|
self, err := bot.GetMe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &BotAPI{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bot.Self = self
|
bot.Self = self
|
||||||
@ -68,6 +71,10 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
|
|||||||
return APIResponse{}, errors.New(ErrAPIForbidden)
|
return APIResponse{}, errors.New(ErrAPIForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return APIResponse{}, errors.New(http.StatusText(resp.StatusCode))
|
||||||
|
}
|
||||||
|
|
||||||
bytes, err := ioutil.ReadAll(resp.Body)
|
bytes, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIResponse{}, err
|
return APIResponse{}, err
|
||||||
@ -457,7 +464,7 @@ func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
|
|||||||
|
|
||||||
// GetUpdatesChan starts and returns a channel for getting updates.
|
// GetUpdatesChan starts and returns a channel for getting updates.
|
||||||
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
|
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
|
||||||
ch := make(chan Update, 100)
|
ch := make(chan Update, bot.Buffer)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -484,7 +491,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
|
|||||||
|
|
||||||
// ListenForWebhook registers a http handler for a webhook.
|
// ListenForWebhook registers a http handler for a webhook.
|
||||||
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
|
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
|
||||||
ch := make(chan Update, 100)
|
ch := make(chan Update, bot.Buffer)
|
||||||
|
|
||||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||||
bytes, _ := ioutil.ReadAll(r.Body)
|
bytes, _ := ioutil.ReadAll(r.Body)
|
||||||
|
4
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
4
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
@ -768,8 +768,8 @@ type UpdateConfig struct {
|
|||||||
|
|
||||||
// WebhookConfig contains information about a SetWebhook request.
|
// WebhookConfig contains information about a SetWebhook request.
|
||||||
type WebhookConfig struct {
|
type WebhookConfig struct {
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
Certificate interface{}
|
Certificate interface{}
|
||||||
MaxConnections int
|
MaxConnections int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
15
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
@ -318,21 +318,6 @@ func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWebhookWithCert creates a new webhook with a certificate and max_connections.
|
|
||||||
//
|
|
||||||
// link is the url you wish to get webhooks,
|
|
||||||
// file contains a string to a file, FileReader, or FileBytes.
|
|
||||||
// maxConnections defines maximum number of connections from telegram to your server
|
|
||||||
func NewWebhookWithCertAndMaxConnections(link string, file interface{}, maxConnections int) WebhookConfig {
|
|
||||||
u, _ := url.Parse(link)
|
|
||||||
|
|
||||||
return WebhookConfig{
|
|
||||||
URL: u,
|
|
||||||
Certificate: file,
|
|
||||||
MaxConnections: maxConnections,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInlineQueryResultArticle creates a new inline query article.
|
// NewInlineQueryResultArticle creates a new inline query article.
|
||||||
func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
|
func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
|
||||||
return InlineQueryResultArticle{
|
return InlineQueryResultArticle{
|
||||||
|
2
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
2
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
@ -194,7 +194,7 @@ func (m *Message) CommandArguments() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.SplitN(m.Text, " ", 2)[1]
|
return split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageEntity contains information about data in a Message.
|
// MessageEntity contains information about data in a Message.
|
||||||
|
63
vendor/github.com/gorilla/schema/cache.go
generated
vendored
63
vendor/github.com/gorilla/schema/cache.go
generated
vendored
@ -138,7 +138,12 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
|
|||||||
ft = ft.Elem()
|
ft = ft.Elem()
|
||||||
}
|
}
|
||||||
if ft.Kind() == reflect.Struct {
|
if ft.Kind() == reflect.Struct {
|
||||||
|
bef := len(info.fields)
|
||||||
c.create(ft, info)
|
c.create(ft, info)
|
||||||
|
for _, fi := range info.fields[bef:len(info.fields)] {
|
||||||
|
// exclude required check because duplicated to embedded field
|
||||||
|
fi.required = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.createField(field, info)
|
c.createField(field, info)
|
||||||
@ -148,7 +153,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
|
|||||||
|
|
||||||
// createField creates a fieldInfo for the given field.
|
// createField creates a fieldInfo for the given field.
|
||||||
func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
||||||
alias := fieldAlias(field, c.tag)
|
alias, options := fieldAlias(field, c.tag)
|
||||||
if alias == "-" {
|
if alias == "-" {
|
||||||
// Ignore this field.
|
// Ignore this field.
|
||||||
return
|
return
|
||||||
@ -173,17 +178,19 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
|
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
|
||||||
if conv := c.conv[ft.Kind()]; conv == nil {
|
if conv := c.converter(ft); conv == nil {
|
||||||
// Type is not supported.
|
// Type is not supported.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info.fields = append(info.fields, &fieldInfo{
|
info.fields = append(info.fields, &fieldInfo{
|
||||||
typ: field.Type,
|
typ: field.Type,
|
||||||
name: field.Name,
|
name: field.Name,
|
||||||
ss: isSlice && isStruct,
|
ss: isSlice && isStruct,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
anon: field.Anonymous,
|
||||||
|
required: options.Contains("required"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,10 +219,12 @@ func (i *structInfo) get(alias string) *fieldInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type fieldInfo struct {
|
type fieldInfo struct {
|
||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
name string // field name in the struct.
|
name string // field name in the struct.
|
||||||
ss bool // true if this is a slice of structs.
|
ss bool // true if this is a slice of structs.
|
||||||
alias string
|
alias string
|
||||||
|
anon bool // is an embedded field
|
||||||
|
required bool // tag option
|
||||||
}
|
}
|
||||||
|
|
||||||
type pathPart struct {
|
type pathPart struct {
|
||||||
@ -227,19 +236,33 @@ type pathPart struct {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// fieldAlias parses a field tag to get a field alias.
|
// fieldAlias parses a field tag to get a field alias.
|
||||||
func fieldAlias(field reflect.StructField, tagName string) string {
|
func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
|
||||||
var alias string
|
|
||||||
if tag := field.Tag.Get(tagName); tag != "" {
|
if tag := field.Tag.Get(tagName); tag != "" {
|
||||||
// For now tags only support the name but let's follow the
|
alias, options = parseTag(tag)
|
||||||
// comma convention from encoding/json and others.
|
|
||||||
if idx := strings.Index(tag, ","); idx == -1 {
|
|
||||||
alias = tag
|
|
||||||
} else {
|
|
||||||
alias = tag[:idx]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if alias == "" {
|
if alias == "" {
|
||||||
alias = field.Name
|
alias = field.Name
|
||||||
}
|
}
|
||||||
return alias
|
return alias, options
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagOptions is the string following a comma in a struct field's tag, or
|
||||||
|
// the empty string. It does not include the leading comma.
|
||||||
|
type tagOptions []string
|
||||||
|
|
||||||
|
// parseTag splits a struct field's url tag into its name and comma-separated
|
||||||
|
// options.
|
||||||
|
func parseTag(tag string) (string, tagOptions) {
|
||||||
|
s := strings.Split(tag, ",")
|
||||||
|
return s[0], s[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks whether the tagOptions contains the specified option.
|
||||||
|
func (o tagOptions) Contains(option string) bool {
|
||||||
|
for _, s := range o {
|
||||||
|
if s == option {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
51
vendor/github.com/gorilla/schema/decoder.go
generated
vendored
51
vendor/github.com/gorilla/schema/decoder.go
generated
vendored
@ -87,9 +87,60 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
|
|||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
return d.checkRequired(t, src, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRequired checks whether requred field empty
|
||||||
|
//
|
||||||
|
// check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation
|
||||||
|
//
|
||||||
|
// src is the source map for decoding, we use it here to see if those required fields are included in src
|
||||||
|
func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix string) error {
|
||||||
|
struc := d.cache.get(t)
|
||||||
|
if struc == nil {
|
||||||
|
// unexpect, cache.get never return nil
|
||||||
|
return errors.New("cache fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range struc.fields {
|
||||||
|
if f.typ.Kind() == reflect.Struct {
|
||||||
|
err := d.checkRequired(f.typ, src, prefix+f.alias+".")
|
||||||
|
if err != nil {
|
||||||
|
if !f.anon {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// check embedded parent field.
|
||||||
|
err2 := d.checkRequired(f.typ, src, prefix)
|
||||||
|
if err2 != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.required {
|
||||||
|
key := f.alias
|
||||||
|
if prefix != "" {
|
||||||
|
key = prefix + key
|
||||||
|
}
|
||||||
|
if isEmpty(f.typ, src[key]) {
|
||||||
|
return fmt.Errorf("%v is empty", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isEmpty returns true if value is empty for specific type
|
||||||
|
func isEmpty(t reflect.Type, value []string) bool {
|
||||||
|
if len(value) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch t.Kind() {
|
||||||
|
case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type:
|
||||||
|
return len(value[0]) == 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// decode fills a struct field using a parsed path.
|
// decode fills a struct field using a parsed path.
|
||||||
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
|
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
|
||||||
// Get the field walking the struct fields by index.
|
// Get the field walking the struct fields by index.
|
||||||
|
2
vendor/github.com/gorilla/schema/doc.go
generated
vendored
2
vendor/github.com/gorilla/schema/doc.go
generated
vendored
@ -12,7 +12,7 @@ The basic usage is really simple. Given this struct:
|
|||||||
Phone string
|
Phone string
|
||||||
}
|
}
|
||||||
|
|
||||||
...we can fill it passing a map to the Load() function:
|
...we can fill it passing a map to the Decode() function:
|
||||||
|
|
||||||
values := map[string][]string{
|
values := map[string][]string{
|
||||||
"Name": {"John"},
|
"Name": {"John"},
|
||||||
|
161
vendor/github.com/gorilla/schema/encoder.go
generated
vendored
Normal file
161
vendor/github.com/gorilla/schema/encoder.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encoderFunc func(reflect.Value) string
|
||||||
|
|
||||||
|
// Encoder encodes values from a struct into url.Values.
|
||||||
|
type Encoder struct {
|
||||||
|
cache *cache
|
||||||
|
regenc map[reflect.Type]encoderFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new Encoder with defaults.
|
||||||
|
func NewEncoder() *Encoder {
|
||||||
|
return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes a struct into map[string][]string.
|
||||||
|
//
|
||||||
|
// Intended for use with url.Values.
|
||||||
|
func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
|
||||||
|
v := reflect.ValueOf(src)
|
||||||
|
|
||||||
|
return e.encode(v, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterEncoder registers a converter for encoding a custom type.
|
||||||
|
func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
|
||||||
|
e.regenc[reflect.TypeOf(value)] = encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAliasTag changes the tag used to locate custom field aliases.
|
||||||
|
// The default tag is "schema".
|
||||||
|
func (e *Encoder) SetAliasTag(tag string) {
|
||||||
|
e.cache.tag = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return errors.New("schema: interface must be a struct")
|
||||||
|
}
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
errors := MultiError{}
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
name, opts := fieldAlias(t.Field(i), e.cache.tag)
|
||||||
|
if name == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Field(i).Type().Kind() == reflect.Struct {
|
||||||
|
e.encode(v.Field(i), dst)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
encFunc := typeEncoder(v.Field(i).Type(), e.regenc)
|
||||||
|
|
||||||
|
// Encode non-slice types and custom implementations immediately.
|
||||||
|
if encFunc != nil {
|
||||||
|
value := encFunc(v.Field(i))
|
||||||
|
if value == "" && opts.Contains("omitempty") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[name] = append(dst[name], value)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Field(i).Type().Kind() == reflect.Slice {
|
||||||
|
encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if encFunc == nil {
|
||||||
|
errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode a slice.
|
||||||
|
if v.Field(i).Len() == 0 && opts.Contains("omitempty") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[name] = []string{}
|
||||||
|
for j := 0; j < v.Field(i).Len(); j++ {
|
||||||
|
dst[name] = append(dst[name], encFunc(v.Field(i).Index(j)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
|
||||||
|
if f, ok := reg[t]; ok {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return encodeBool
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return encodeInt
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return encodeUint
|
||||||
|
case reflect.Float32:
|
||||||
|
return encodeFloat32
|
||||||
|
case reflect.Float64:
|
||||||
|
return encodeFloat64
|
||||||
|
case reflect.Ptr:
|
||||||
|
f := typeEncoder(t.Elem(), reg)
|
||||||
|
return func(v reflect.Value) string {
|
||||||
|
if v.IsNil() {
|
||||||
|
return "null"
|
||||||
|
}
|
||||||
|
return f(v.Elem())
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
return encodeString
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBool(v reflect.Value) string {
|
||||||
|
return strconv.FormatBool(v.Bool())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInt(v reflect.Value) string {
|
||||||
|
return strconv.FormatInt(int64(v.Int()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUint(v reflect.Value) string {
|
||||||
|
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat(v reflect.Value, bits int) string {
|
||||||
|
return strconv.FormatFloat(v.Float(), 'f', 6, bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat32(v reflect.Value) string {
|
||||||
|
return encodeFloat(v, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat64(v reflect.Value) string {
|
||||||
|
return encodeFloat(v, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeString(v reflect.Value) string {
|
||||||
|
return v.String()
|
||||||
|
}
|
60
vendor/github.com/gorilla/websocket/client.go
generated
vendored
60
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@ -23,6 +23,8 @@ import (
|
|||||||
// invalid.
|
// invalid.
|
||||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||||
|
|
||||||
|
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||||
|
|
||||||
// NewClient creates a new client connection using the given net connection.
|
// NewClient creates a new client connection using the given net connection.
|
||||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||||
@ -64,12 +66,24 @@ type Dialer struct {
|
|||||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
|
|
||||||
// Input and output buffer sizes. If the buffer size is zero, then a
|
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||||
// default value of 4096 is used.
|
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||||
|
// do not limit the size of the messages that can be sent or received.
|
||||||
ReadBufferSize, WriteBufferSize int
|
ReadBufferSize, WriteBufferSize int
|
||||||
|
|
||||||
// Subprotocols specifies the client's requested subprotocols.
|
// Subprotocols specifies the client's requested subprotocols.
|
||||||
Subprotocols []string
|
Subprotocols []string
|
||||||
|
|
||||||
|
// EnableCompression specifies if the client should attempt to negotiate
|
||||||
|
// per message compression (RFC 7692). Setting this value to true does not
|
||||||
|
// guarantee that compression will be supported. Currently only "no context
|
||||||
|
// takeover" modes are supported.
|
||||||
|
EnableCompression bool
|
||||||
|
|
||||||
|
// Jar specifies the cookie jar.
|
||||||
|
// If Jar is nil, cookies are not sent in requests and ignored
|
||||||
|
// in responses.
|
||||||
|
Jar http.CookieJar
|
||||||
}
|
}
|
||||||
|
|
||||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||||
@ -83,7 +97,6 @@ func parseURL(s string) (*url.URL, error) {
|
|||||||
//
|
//
|
||||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||||
|
|
||||||
var u url.URL
|
var u url.URL
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(s, "ws://"):
|
case strings.HasPrefix(s, "ws://"):
|
||||||
@ -193,6 +206,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
Host: u.Host,
|
Host: u.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the cookies present in the cookie jar of the dialer
|
||||||
|
if d.Jar != nil {
|
||||||
|
for _, cookie := range d.Jar.Cookies(u) {
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the request headers using the capitalization for names and values in
|
// Set the request headers using the capitalization for names and values in
|
||||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||||
// servers that depend on it. The Header.Set method is not used because the
|
// servers that depend on it. The Header.Set method is not used because the
|
||||||
@ -214,6 +234,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
k == "Connection" ||
|
k == "Connection" ||
|
||||||
k == "Sec-Websocket-Key" ||
|
k == "Sec-Websocket-Key" ||
|
||||||
k == "Sec-Websocket-Version" ||
|
k == "Sec-Websocket-Version" ||
|
||||||
|
k == "Sec-Websocket-Extensions" ||
|
||||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||||
default:
|
default:
|
||||||
@ -221,6 +242,10 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.EnableCompression {
|
||||||
|
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||||
|
}
|
||||||
|
|
||||||
hostPort, hostNoPort := hostPortNoPort(u)
|
hostPort, hostNoPort := hostPortNoPort(u)
|
||||||
|
|
||||||
var proxyURL *url.URL
|
var proxyURL *url.URL
|
||||||
@ -298,12 +323,8 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "https" {
|
if u.Scheme == "https" {
|
||||||
cfg := d.TLSClientConfig
|
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||||
if cfg == nil {
|
if cfg.ServerName == "" {
|
||||||
cfg = &tls.Config{ServerName: hostNoPort}
|
|
||||||
} else if cfg.ServerName == "" {
|
|
||||||
shallowCopy := *cfg
|
|
||||||
cfg = &shallowCopy
|
|
||||||
cfg.ServerName = hostNoPort
|
cfg.ServerName = hostNoPort
|
||||||
}
|
}
|
||||||
tlsConn := tls.Client(netConn, cfg)
|
tlsConn := tls.Client(netConn, cfg)
|
||||||
@ -328,6 +349,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.Jar != nil {
|
||||||
|
if rc := resp.Cookies(); len(rc) > 0 {
|
||||||
|
d.Jar.SetCookies(u, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 101 ||
|
if resp.StatusCode != 101 ||
|
||||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||||
@ -341,6 +369,20 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
|||||||
return nil, resp, ErrBadHandshake
|
return nil, resp, ErrBadHandshake
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, ext := range parseExtensions(resp.Header) {
|
||||||
|
if ext[""] != "permessage-deflate" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, snct := ext["server_no_context_takeover"]
|
||||||
|
_, cnct := ext["client_no_context_takeover"]
|
||||||
|
if !snct || !cnct {
|
||||||
|
return nil, resp, errInvalidCompression
|
||||||
|
}
|
||||||
|
conn.newCompressionWriter = compressNoContextTakeover
|
||||||
|
conn.newDecompressionReader = decompressNoContextTakeover
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||||
|
|
||||||
|
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2013 The Gorilla WebSocket 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 go1.8
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
return cfg.Clone()
|
||||||
|
}
|
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2013 The Gorilla WebSocket 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 !go1.8
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
// cloneTLSConfig clones all public fields except the fields
|
||||||
|
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||||
|
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||||
|
// config in active use.
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
return &tls.Config{
|
||||||
|
Rand: cfg.Rand,
|
||||||
|
Time: cfg.Time,
|
||||||
|
Certificates: cfg.Certificates,
|
||||||
|
NameToCertificate: cfg.NameToCertificate,
|
||||||
|
GetCertificate: cfg.GetCertificate,
|
||||||
|
RootCAs: cfg.RootCAs,
|
||||||
|
NextProtos: cfg.NextProtos,
|
||||||
|
ServerName: cfg.ServerName,
|
||||||
|
ClientAuth: cfg.ClientAuth,
|
||||||
|
ClientCAs: cfg.ClientCAs,
|
||||||
|
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||||
|
CipherSuites: cfg.CipherSuites,
|
||||||
|
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||||
|
ClientSessionCache: cfg.ClientSessionCache,
|
||||||
|
MinVersion: cfg.MinVersion,
|
||||||
|
MaxVersion: cfg.MaxVersion,
|
||||||
|
CurvePreferences: cfg.CurvePreferences,
|
||||||
|
}
|
||||||
|
}
|
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Copyright 2017 The Gorilla WebSocket 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 websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/flate"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
||||||
|
maxCompressionLevel = flate.BestCompression
|
||||||
|
defaultCompressionLevel = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
||||||
|
flateReaderPool = sync.Pool{New: func() interface{} {
|
||||||
|
return flate.NewReader(nil)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||||
|
const tail =
|
||||||
|
// Add four bytes as specified in RFC
|
||||||
|
"\x00\x00\xff\xff" +
|
||||||
|
// Add final block to squelch unexpected EOF error from flate reader.
|
||||||
|
"\x01\x00\x00\xff\xff"
|
||||||
|
|
||||||
|
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||||
|
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||||
|
return &flateReadWrapper{fr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidCompressionLevel(level int) bool {
|
||||||
|
return minCompressionLevel <= level && level <= maxCompressionLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
||||||
|
p := &flateWriterPools[level-minCompressionLevel]
|
||||||
|
tw := &truncWriter{w: w}
|
||||||
|
fw, _ := p.Get().(*flate.Writer)
|
||||||
|
if fw == nil {
|
||||||
|
fw, _ = flate.NewWriter(tw, level)
|
||||||
|
} else {
|
||||||
|
fw.Reset(tw)
|
||||||
|
}
|
||||||
|
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||||
|
// stream to another io.Writer.
|
||||||
|
type truncWriter struct {
|
||||||
|
w io.WriteCloser
|
||||||
|
n int
|
||||||
|
p [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||||
|
n := 0
|
||||||
|
|
||||||
|
// fill buffer first for simplicity.
|
||||||
|
if w.n < len(w.p) {
|
||||||
|
n = copy(w.p[w.n:], p)
|
||||||
|
p = p[n:]
|
||||||
|
w.n += n
|
||||||
|
if len(p) == 0 {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := len(p)
|
||||||
|
if m > len(w.p) {
|
||||||
|
m = len(w.p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||||
|
return n + nn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(w.p[:], w.p[m:])
|
||||||
|
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||||
|
nn, err := w.w.Write(p[:len(p)-m])
|
||||||
|
return n + nn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type flateWriteWrapper struct {
|
||||||
|
fw *flate.Writer
|
||||||
|
tw *truncWriter
|
||||||
|
p *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
||||||
|
if w.fw == nil {
|
||||||
|
return 0, errWriteClosed
|
||||||
|
}
|
||||||
|
return w.fw.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *flateWriteWrapper) Close() error {
|
||||||
|
if w.fw == nil {
|
||||||
|
return errWriteClosed
|
||||||
|
}
|
||||||
|
err1 := w.fw.Flush()
|
||||||
|
w.p.Put(w.fw)
|
||||||
|
w.fw = nil
|
||||||
|
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||||
|
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||||
|
}
|
||||||
|
err2 := w.tw.w.Close()
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
type flateReadWrapper struct {
|
||||||
|
fr io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||||
|
if r.fr == nil {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
n, err := r.fr.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
// Preemptively place the reader back in the pool. This helps with
|
||||||
|
// scenarios where the application does not call NextReader() soon after
|
||||||
|
// this final read.
|
||||||
|
r.Close()
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flateReadWrapper) Close() error {
|
||||||
|
if r.fr == nil {
|
||||||
|
return io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
err := r.fr.Close()
|
||||||
|
flateReaderPool.Put(r.fr)
|
||||||
|
r.fr = nil
|
||||||
|
return err
|
||||||
|
}
|
632
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
632
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@ -13,15 +13,25 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Frame header byte 0 bits from Section 5.2 of RFC 6455
|
||||||
|
finalBit = 1 << 7
|
||||||
|
rsv1Bit = 1 << 6
|
||||||
|
rsv2Bit = 1 << 5
|
||||||
|
rsv3Bit = 1 << 4
|
||||||
|
|
||||||
|
// Frame header byte 1 bits from Section 5.2 of RFC 6455
|
||||||
|
maskBit = 1 << 7
|
||||||
|
|
||||||
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
||||||
maxControlFramePayloadSize = 125
|
maxControlFramePayloadSize = 125
|
||||||
finalBit = 1 << 7
|
|
||||||
maskBit = 1 << 7
|
writeWait = time.Second
|
||||||
writeWait = time.Second
|
|
||||||
|
|
||||||
defaultReadBufferSize = 4096
|
defaultReadBufferSize = 4096
|
||||||
defaultWriteBufferSize = 4096
|
defaultWriteBufferSize = 4096
|
||||||
@ -43,6 +53,8 @@ const (
|
|||||||
CloseMessageTooBig = 1009
|
CloseMessageTooBig = 1009
|
||||||
CloseMandatoryExtension = 1010
|
CloseMandatoryExtension = 1010
|
||||||
CloseInternalServerErr = 1011
|
CloseInternalServerErr = 1011
|
||||||
|
CloseServiceRestart = 1012
|
||||||
|
CloseTryAgainLater = 1013
|
||||||
CloseTLSHandshake = 1015
|
CloseTLSHandshake = 1015
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -169,6 +181,11 @@ var (
|
|||||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newMaskKey() [4]byte {
|
||||||
|
n := rand.Uint32()
|
||||||
|
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||||
|
}
|
||||||
|
|
||||||
func hideTempErr(err error) error {
|
func hideTempErr(err error) error {
|
||||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||||
@ -184,74 +201,138 @@ func isData(frameType int) bool {
|
|||||||
return frameType == TextMessage || frameType == BinaryMessage
|
return frameType == TextMessage || frameType == BinaryMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
var validReceivedCloseCodes = map[int]bool{
|
||||||
for i := range b {
|
// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
CloseNormalClosure: true,
|
||||||
}
|
CloseGoingAway: true,
|
||||||
return pos & 3
|
CloseProtocolError: true,
|
||||||
|
CloseUnsupportedData: true,
|
||||||
|
CloseNoStatusReceived: false,
|
||||||
|
CloseAbnormalClosure: false,
|
||||||
|
CloseInvalidFramePayloadData: true,
|
||||||
|
ClosePolicyViolation: true,
|
||||||
|
CloseMessageTooBig: true,
|
||||||
|
CloseMandatoryExtension: true,
|
||||||
|
CloseInternalServerErr: true,
|
||||||
|
CloseServiceRestart: true,
|
||||||
|
CloseTryAgainLater: true,
|
||||||
|
CloseTLSHandshake: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMaskKey() [4]byte {
|
func isValidReceivedCloseCode(code int) bool {
|
||||||
n := rand.Uint32()
|
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
||||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn represents a WebSocket connection.
|
// The Conn type represents a WebSocket connection.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
isServer bool
|
isServer bool
|
||||||
subprotocol string
|
subprotocol string
|
||||||
|
|
||||||
// Write fields
|
// Write fields
|
||||||
mu chan bool // used as mutex to protect write to conn and closeSent
|
mu chan bool // used as mutex to protect write to conn
|
||||||
closeSent bool // true if close message was sent
|
writeBuf []byte // frame is constructed in this buffer.
|
||||||
|
writeDeadline time.Time
|
||||||
|
writer io.WriteCloser // the current writer returned to the application
|
||||||
|
isWriting bool // for best-effort concurrent write detection
|
||||||
|
|
||||||
// Message writer fields.
|
writeErrMu sync.Mutex
|
||||||
writeErr error
|
writeErr error
|
||||||
writeBuf []byte // frame is constructed in this buffer.
|
|
||||||
writePos int // end of data in writeBuf.
|
enableWriteCompression bool
|
||||||
writeFrameType int // type of the current frame.
|
compressionLevel int
|
||||||
writeSeq int // incremented to invalidate message writers.
|
newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
|
||||||
writeDeadline time.Time
|
|
||||||
isWriting bool // for best-effort concurrent write detection
|
|
||||||
|
|
||||||
// Read fields
|
// Read fields
|
||||||
|
reader io.ReadCloser // the current reader returned to the application
|
||||||
readErr error
|
readErr error
|
||||||
br *bufio.Reader
|
br *bufio.Reader
|
||||||
readRemaining int64 // bytes remaining in current frame.
|
readRemaining int64 // bytes remaining in current frame.
|
||||||
readFinal bool // true the current message has more frames.
|
readFinal bool // true the current message has more frames.
|
||||||
readSeq int // incremented to invalidate message readers.
|
|
||||||
readLength int64 // Message size.
|
readLength int64 // Message size.
|
||||||
readLimit int64 // Maximum message size.
|
readLimit int64 // Maximum message size.
|
||||||
readMaskPos int
|
readMaskPos int
|
||||||
readMaskKey [4]byte
|
readMaskKey [4]byte
|
||||||
handlePong func(string) error
|
handlePong func(string) error
|
||||||
handlePing func(string) error
|
handlePing func(string) error
|
||||||
|
handleClose func(int, string) error
|
||||||
readErrCount int
|
readErrCount int
|
||||||
|
messageReader *messageReader // the current low-level reader
|
||||||
|
|
||||||
|
readDecompress bool // whether last read frame had RSV1 set
|
||||||
|
newDecompressionReader func(io.Reader) io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
||||||
|
return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeHook struct {
|
||||||
|
p []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||||
|
wh.p = p
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn {
|
||||||
mu := make(chan bool, 1)
|
mu := make(chan bool, 1)
|
||||||
mu <- true
|
mu <- true
|
||||||
|
|
||||||
if readBufferSize == 0 {
|
var br *bufio.Reader
|
||||||
readBufferSize = defaultReadBufferSize
|
if readBufferSize == 0 && brw != nil && brw.Reader != nil {
|
||||||
|
// Reuse the supplied bufio.Reader if the buffer has a useful size.
|
||||||
|
// This code assumes that peek on a reader returns
|
||||||
|
// bufio.Reader.buf[:0].
|
||||||
|
brw.Reader.Reset(conn)
|
||||||
|
if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
|
||||||
|
br = brw.Reader
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if writeBufferSize == 0 {
|
if br == nil {
|
||||||
writeBufferSize = defaultWriteBufferSize
|
if readBufferSize == 0 {
|
||||||
|
readBufferSize = defaultReadBufferSize
|
||||||
|
}
|
||||||
|
if readBufferSize < maxControlFramePayloadSize {
|
||||||
|
readBufferSize = maxControlFramePayloadSize
|
||||||
|
}
|
||||||
|
br = bufio.NewReaderSize(conn, readBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
var writeBuf []byte
|
||||||
|
if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
|
||||||
|
// Use the bufio.Writer's buffer if the buffer has a useful size. This
|
||||||
|
// code assumes that bufio.Writer.buf[:1] is passed to the
|
||||||
|
// bufio.Writer's underlying writer.
|
||||||
|
var wh writeHook
|
||||||
|
brw.Writer.Reset(&wh)
|
||||||
|
brw.Writer.WriteByte(0)
|
||||||
|
brw.Flush()
|
||||||
|
if cap(wh.p) >= maxFrameHeaderSize+256 {
|
||||||
|
writeBuf = wh.p[:cap(wh.p)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeBuf == nil {
|
||||||
|
if writeBufferSize == 0 {
|
||||||
|
writeBufferSize = defaultWriteBufferSize
|
||||||
|
}
|
||||||
|
writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
isServer: isServer,
|
isServer: isServer,
|
||||||
br: bufio.NewReaderSize(conn, readBufferSize),
|
br: br,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
mu: mu,
|
mu: mu,
|
||||||
readFinal: true,
|
readFinal: true,
|
||||||
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
|
writeBuf: writeBuf,
|
||||||
writeFrameType: noFrame,
|
enableWriteCompression: true,
|
||||||
writePos: maxFrameHeaderSize,
|
compressionLevel: defaultCompressionLevel,
|
||||||
}
|
}
|
||||||
|
c.SetCloseHandler(nil)
|
||||||
c.SetPingHandler(nil)
|
c.SetPingHandler(nil)
|
||||||
c.SetPongHandler(nil)
|
c.SetPongHandler(nil)
|
||||||
return c
|
return c
|
||||||
@ -279,29 +360,40 @@ func (c *Conn) RemoteAddr() net.Addr {
|
|||||||
|
|
||||||
// Write methods
|
// Write methods
|
||||||
|
|
||||||
|
func (c *Conn) writeFatal(err error) error {
|
||||||
|
err = hideTempErr(err)
|
||||||
|
c.writeErrMu.Lock()
|
||||||
|
if c.writeErr == nil {
|
||||||
|
c.writeErr = err
|
||||||
|
}
|
||||||
|
c.writeErrMu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
||||||
<-c.mu
|
<-c.mu
|
||||||
defer func() { c.mu <- true }()
|
defer func() { c.mu <- true }()
|
||||||
|
|
||||||
if c.closeSent {
|
c.writeErrMu.Lock()
|
||||||
return ErrCloseSent
|
err := c.writeErr
|
||||||
} else if frameType == CloseMessage {
|
c.writeErrMu.Unlock()
|
||||||
c.closeSent = true
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
c.conn.SetWriteDeadline(deadline)
|
||||||
for _, buf := range bufs {
|
for _, buf := range bufs {
|
||||||
if len(buf) > 0 {
|
if len(buf) > 0 {
|
||||||
n, err := c.conn.Write(buf)
|
_, err := c.conn.Write(buf)
|
||||||
if n != len(buf) {
|
|
||||||
// Close on partial write.
|
|
||||||
c.conn.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if frameType == CloseMessage {
|
||||||
|
c.writeFatal(ErrCloseSent)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,60 +442,104 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
|||||||
}
|
}
|
||||||
defer func() { c.mu <- true }()
|
defer func() { c.mu <- true }()
|
||||||
|
|
||||||
if c.closeSent {
|
c.writeErrMu.Lock()
|
||||||
return ErrCloseSent
|
err := c.writeErr
|
||||||
} else if messageType == CloseMessage {
|
c.writeErrMu.Unlock()
|
||||||
c.closeSent = true
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
c.conn.SetWriteDeadline(deadline)
|
||||||
n, err := c.conn.Write(buf)
|
_, err = c.conn.Write(buf)
|
||||||
if n != 0 && n != len(buf) {
|
if err != nil {
|
||||||
c.conn.Close()
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
return hideTempErr(err)
|
if messageType == CloseMessage {
|
||||||
|
c.writeFatal(ErrCloseSent)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextWriter returns a writer for the next message to send. The writer's
|
func (c *Conn) prepWrite(messageType int) error {
|
||||||
// Close method flushes the complete message to the network.
|
// Close previous writer if not already closed by the application. It's
|
||||||
|
// probably better to return an error in this situation, but we cannot
|
||||||
|
// change this without breaking existing applications.
|
||||||
|
if c.writer != nil {
|
||||||
|
c.writer.Close()
|
||||||
|
c.writer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isControl(messageType) && !isData(messageType) {
|
||||||
|
return errBadWriteOpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeErrMu.Lock()
|
||||||
|
err := c.writeErr
|
||||||
|
c.writeErrMu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextWriter returns a writer for the next message to send. The writer's Close
|
||||||
|
// method flushes the complete message to the network.
|
||||||
//
|
//
|
||||||
// There can be at most one open writer on a connection. NextWriter closes the
|
// There can be at most one open writer on a connection. NextWriter closes the
|
||||||
// previous writer if the application has not already done so.
|
// previous writer if the application has not already done so.
|
||||||
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
|
||||||
if c.writeErr != nil {
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
return nil, c.writeErr
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.writeFrameType != noFrame {
|
mw := &messageWriter{
|
||||||
if err := c.flushFrame(true, nil); err != nil {
|
c: c,
|
||||||
return nil, err
|
frameType: messageType,
|
||||||
}
|
pos: maxFrameHeaderSize,
|
||||||
}
|
}
|
||||||
|
c.writer = mw
|
||||||
if !isControl(messageType) && !isData(messageType) {
|
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
|
||||||
return nil, errBadWriteOpCode
|
w := c.newCompressionWriter(c.writer, c.compressionLevel)
|
||||||
|
mw.compress = true
|
||||||
|
c.writer = w
|
||||||
}
|
}
|
||||||
|
return c.writer, nil
|
||||||
c.writeFrameType = messageType
|
|
||||||
return messageWriter{c, c.writeSeq}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) flushFrame(final bool, extra []byte) error {
|
type messageWriter struct {
|
||||||
length := c.writePos - maxFrameHeaderSize + len(extra)
|
c *Conn
|
||||||
|
compress bool // whether next call to flushFrame should set RSV1
|
||||||
|
pos int // end of data in writeBuf.
|
||||||
|
frameType int // type of the current frame.
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *messageWriter) fatal(err error) error {
|
||||||
|
if w.err != nil {
|
||||||
|
w.err = err
|
||||||
|
w.c.writer = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushFrame writes buffered data and extra as a frame to the network. The
|
||||||
|
// final argument indicates that this is the last frame in the message.
|
||||||
|
func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||||
|
c := w.c
|
||||||
|
length := w.pos - maxFrameHeaderSize + len(extra)
|
||||||
|
|
||||||
// Check for invalid control frames.
|
// Check for invalid control frames.
|
||||||
if isControl(c.writeFrameType) &&
|
if isControl(w.frameType) &&
|
||||||
(!final || length > maxControlFramePayloadSize) {
|
(!final || length > maxControlFramePayloadSize) {
|
||||||
c.writeSeq++
|
return w.fatal(errInvalidControlFrame)
|
||||||
c.writeFrameType = noFrame
|
|
||||||
c.writePos = maxFrameHeaderSize
|
|
||||||
return errInvalidControlFrame
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b0 := byte(c.writeFrameType)
|
b0 := byte(w.frameType)
|
||||||
if final {
|
if final {
|
||||||
b0 |= finalBit
|
b0 |= finalBit
|
||||||
}
|
}
|
||||||
|
if w.compress {
|
||||||
|
b0 |= rsv1Bit
|
||||||
|
}
|
||||||
|
w.compress = false
|
||||||
|
|
||||||
b1 := byte(0)
|
b1 := byte(0)
|
||||||
if !c.isServer {
|
if !c.isServer {
|
||||||
b1 |= maskBit
|
b1 |= maskBit
|
||||||
@ -435,10 +571,9 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
|||||||
if !c.isServer {
|
if !c.isServer {
|
||||||
key := newMaskKey()
|
key := newMaskKey()
|
||||||
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
||||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
|
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])
|
||||||
if len(extra) > 0 {
|
if len(extra) > 0 {
|
||||||
c.writeErr = errors.New("websocket: internal error, extra used in client mode")
|
return c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))
|
||||||
return c.writeErr
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,46 +586,35 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
|||||||
}
|
}
|
||||||
c.isWriting = true
|
c.isWriting = true
|
||||||
|
|
||||||
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
|
||||||
|
|
||||||
if !c.isWriting {
|
if !c.isWriting {
|
||||||
panic("concurrent write to websocket connection")
|
panic("concurrent write to websocket connection")
|
||||||
}
|
}
|
||||||
c.isWriting = false
|
c.isWriting = false
|
||||||
|
|
||||||
// Setup for next frame.
|
if err != nil {
|
||||||
c.writePos = maxFrameHeaderSize
|
return w.fatal(err)
|
||||||
c.writeFrameType = continuationFrame
|
}
|
||||||
|
|
||||||
if final {
|
if final {
|
||||||
c.writeSeq++
|
c.writer = nil
|
||||||
c.writeFrameType = noFrame
|
return nil
|
||||||
}
|
}
|
||||||
return c.writeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageWriter struct {
|
// Setup for next frame.
|
||||||
c *Conn
|
w.pos = maxFrameHeaderSize
|
||||||
seq int
|
w.frameType = continuationFrame
|
||||||
}
|
|
||||||
|
|
||||||
func (w messageWriter) err() error {
|
|
||||||
c := w.c
|
|
||||||
if c.writeSeq != w.seq {
|
|
||||||
return errWriteClosed
|
|
||||||
}
|
|
||||||
if c.writeErr != nil {
|
|
||||||
return c.writeErr
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w messageWriter) ncopy(max int) (int, error) {
|
func (w *messageWriter) ncopy(max int) (int, error) {
|
||||||
n := len(w.c.writeBuf) - w.c.writePos
|
n := len(w.c.writeBuf) - w.pos
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
if err := w.c.flushFrame(false, nil); err != nil {
|
if err := w.flushFrame(false, nil); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
n = len(w.c.writeBuf) - w.c.writePos
|
n = len(w.c.writeBuf) - w.pos
|
||||||
}
|
}
|
||||||
if n > max {
|
if n > max {
|
||||||
n = max
|
n = max
|
||||||
@ -498,14 +622,14 @@ func (w messageWriter) ncopy(max int) (int, error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w messageWriter) write(final bool, p []byte) (int, error) {
|
func (w *messageWriter) Write(p []byte) (int, error) {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
return 0, err
|
return 0, w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
||||||
// Don't buffer large messages.
|
// Don't buffer large messages.
|
||||||
err := w.c.flushFrame(final, p)
|
err := w.flushFrame(false, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -518,20 +642,16 @@ func (w messageWriter) write(final bool, p []byte) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
}
|
}
|
||||||
return nn, nil
|
return nn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w messageWriter) Write(p []byte) (int, error) {
|
func (w *messageWriter) WriteString(p string) (int, error) {
|
||||||
return w.write(false, p)
|
if w.err != nil {
|
||||||
}
|
return 0, w.err
|
||||||
|
|
||||||
func (w messageWriter) WriteString(p string) (int, error) {
|
|
||||||
if err := w.err(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nn := len(p)
|
nn := len(p)
|
||||||
@ -540,27 +660,27 @@ func (w messageWriter) WriteString(p string) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
}
|
}
|
||||||
return nn, nil
|
return nn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
return 0, err
|
return 0, w.err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
if w.c.writePos == len(w.c.writeBuf) {
|
if w.pos == len(w.c.writeBuf) {
|
||||||
err = w.c.flushFrame(false, nil)
|
err = w.flushFrame(false, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var n int
|
var n int
|
||||||
n, err = r.Read(w.c.writeBuf[w.c.writePos:])
|
n, err = r.Read(w.c.writeBuf[w.pos:])
|
||||||
w.c.writePos += n
|
w.pos += n
|
||||||
nn += int64(n)
|
nn += int64(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -572,30 +692,64 @@ func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
|||||||
return nn, err
|
return nn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w messageWriter) Close() error {
|
func (w *messageWriter) Close() error {
|
||||||
if err := w.err(); err != nil {
|
if w.err != nil {
|
||||||
|
return w.err
|
||||||
|
}
|
||||||
|
if err := w.flushFrame(true, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return w.c.flushFrame(true, nil)
|
w.err = errWriteClosed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePreparedMessage writes prepared message into connection.
|
||||||
|
func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {
|
||||||
|
frameType, frameData, err := pm.frame(prepareKey{
|
||||||
|
isServer: c.isServer,
|
||||||
|
compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),
|
||||||
|
compressionLevel: c.compressionLevel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.isWriting {
|
||||||
|
panic("concurrent write to websocket connection")
|
||||||
|
}
|
||||||
|
c.isWriting = true
|
||||||
|
err = c.write(frameType, c.writeDeadline, frameData, nil)
|
||||||
|
if !c.isWriting {
|
||||||
|
panic("concurrent write to websocket connection")
|
||||||
|
}
|
||||||
|
c.isWriting = false
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMessage is a helper method for getting a writer using NextWriter,
|
// WriteMessage is a helper method for getting a writer using NextWriter,
|
||||||
// writing the message and closing the writer.
|
// writing the message and closing the writer.
|
||||||
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
||||||
wr, err := c.NextWriter(messageType)
|
|
||||||
|
if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
|
||||||
|
// Fast path with no allocations and single frame.
|
||||||
|
|
||||||
|
if err := c.prepWrite(messageType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
|
||||||
|
n := copy(c.writeBuf[mw.pos:], data)
|
||||||
|
mw.pos += n
|
||||||
|
data = data[n:]
|
||||||
|
return mw.flushFrame(true, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := c.NextWriter(messageType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w := wr.(messageWriter)
|
if _, err = w.Write(data); err != nil {
|
||||||
if _, err := w.write(true, data); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if c.writeSeq == w.seq {
|
return w.Close()
|
||||||
if err := c.flushFrame(true, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWriteDeadline sets the write deadline on the underlying network
|
// SetWriteDeadline sets the write deadline on the underlying network
|
||||||
@ -609,22 +763,6 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
|||||||
|
|
||||||
// Read methods
|
// Read methods
|
||||||
|
|
||||||
// readFull is like io.ReadFull except that io.EOF is never returned.
|
|
||||||
func (c *Conn) readFull(p []byte) (err error) {
|
|
||||||
var n int
|
|
||||||
for n < len(p) && err == nil {
|
|
||||||
var nn int
|
|
||||||
nn, err = c.br.Read(p[n:])
|
|
||||||
n += nn
|
|
||||||
}
|
|
||||||
if n == len(p) {
|
|
||||||
err = nil
|
|
||||||
} else if err == io.EOF {
|
|
||||||
err = errUnexpectedEOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) advanceFrame() (int, error) {
|
func (c *Conn) advanceFrame() (int, error) {
|
||||||
|
|
||||||
// 1. Skip remainder of previous frame.
|
// 1. Skip remainder of previous frame.
|
||||||
@ -637,19 +775,24 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
|
|
||||||
// 2. Read and parse first two bytes of frame header.
|
// 2. Read and parse first two bytes of frame header.
|
||||||
|
|
||||||
var b [8]byte
|
p, err := c.read(2)
|
||||||
if err := c.readFull(b[:2]); err != nil {
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
|
|
||||||
final := b[0]&finalBit != 0
|
final := p[0]&finalBit != 0
|
||||||
frameType := int(b[0] & 0xf)
|
frameType := int(p[0] & 0xf)
|
||||||
reserved := int((b[0] >> 4) & 0x7)
|
mask := p[1]&maskBit != 0
|
||||||
mask := b[1]&maskBit != 0
|
c.readRemaining = int64(p[1] & 0x7f)
|
||||||
c.readRemaining = int64(b[1] & 0x7f)
|
|
||||||
|
|
||||||
if reserved != 0 {
|
c.readDecompress = false
|
||||||
return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
|
if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {
|
||||||
|
c.readDecompress = true
|
||||||
|
p[0] &^= rsv1Bit
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
|
||||||
|
return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch frameType {
|
switch frameType {
|
||||||
@ -678,15 +821,17 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
|
|
||||||
switch c.readRemaining {
|
switch c.readRemaining {
|
||||||
case 126:
|
case 126:
|
||||||
if err := c.readFull(b[:2]); err != nil {
|
p, err := c.read(2)
|
||||||
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
c.readRemaining = int64(binary.BigEndian.Uint16(b[:2]))
|
c.readRemaining = int64(binary.BigEndian.Uint16(p))
|
||||||
case 127:
|
case 127:
|
||||||
if err := c.readFull(b[:8]); err != nil {
|
p, err := c.read(8)
|
||||||
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
c.readRemaining = int64(binary.BigEndian.Uint64(b[:8]))
|
c.readRemaining = int64(binary.BigEndian.Uint64(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Handle frame masking.
|
// 4. Handle frame masking.
|
||||||
@ -697,9 +842,11 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
|
|
||||||
if mask {
|
if mask {
|
||||||
c.readMaskPos = 0
|
c.readMaskPos = 0
|
||||||
if err := c.readFull(c.readMaskKey[:]); err != nil {
|
p, err := c.read(len(c.readMaskKey))
|
||||||
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
|
copy(c.readMaskKey[:], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. For text and binary messages, enforce read limit and return.
|
// 5. For text and binary messages, enforce read limit and return.
|
||||||
@ -719,9 +866,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
|
|
||||||
var payload []byte
|
var payload []byte
|
||||||
if c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
payload = make([]byte, c.readRemaining)
|
payload, err = c.read(int(c.readRemaining))
|
||||||
c.readRemaining = 0
|
c.readRemaining = 0
|
||||||
if err := c.readFull(payload); err != nil {
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
if c.isServer {
|
if c.isServer {
|
||||||
@ -741,15 +888,21 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
case CloseMessage:
|
case CloseMessage:
|
||||||
echoMessage := []byte{}
|
|
||||||
closeCode := CloseNoStatusReceived
|
closeCode := CloseNoStatusReceived
|
||||||
closeText := ""
|
closeText := ""
|
||||||
if len(payload) >= 2 {
|
if len(payload) >= 2 {
|
||||||
echoMessage = payload[:2]
|
|
||||||
closeCode = int(binary.BigEndian.Uint16(payload))
|
closeCode = int(binary.BigEndian.Uint16(payload))
|
||||||
|
if !isValidReceivedCloseCode(closeCode) {
|
||||||
|
return noFrame, c.handleProtocolError("invalid close code")
|
||||||
|
}
|
||||||
closeText = string(payload[2:])
|
closeText = string(payload[2:])
|
||||||
|
if !utf8.ValidString(closeText) {
|
||||||
|
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := c.handleClose(closeCode, closeText); err != nil {
|
||||||
|
return noFrame, err
|
||||||
}
|
}
|
||||||
c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
|
||||||
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,8 +925,13 @@ func (c *Conn) handleProtocolError(message string) error {
|
|||||||
// permanent. Once this method returns a non-nil error, all subsequent calls to
|
// permanent. Once this method returns a non-nil error, all subsequent calls to
|
||||||
// this method return the same error.
|
// this method return the same error.
|
||||||
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||||
|
// Close previous reader, only relevant for decompression.
|
||||||
|
if c.reader != nil {
|
||||||
|
c.reader.Close()
|
||||||
|
c.reader = nil
|
||||||
|
}
|
||||||
|
|
||||||
c.readSeq++
|
c.messageReader = nil
|
||||||
c.readLength = 0
|
c.readLength = 0
|
||||||
|
|
||||||
for c.readErr == nil {
|
for c.readErr == nil {
|
||||||
@ -783,7 +941,12 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if frameType == TextMessage || frameType == BinaryMessage {
|
if frameType == TextMessage || frameType == BinaryMessage {
|
||||||
return frameType, messageReader{c, c.readSeq}, nil
|
c.messageReader = &messageReader{c}
|
||||||
|
c.reader = c.messageReader
|
||||||
|
if c.readDecompress {
|
||||||
|
c.reader = c.newDecompressionReader(c.reader)
|
||||||
|
}
|
||||||
|
return frameType, c.reader, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,53 +961,57 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|||||||
return noFrame, nil, c.readErr
|
return noFrame, nil, c.readErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type messageReader struct {
|
type messageReader struct{ c *Conn }
|
||||||
c *Conn
|
|
||||||
seq int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r messageReader) Read(b []byte) (int, error) {
|
func (r *messageReader) Read(b []byte) (int, error) {
|
||||||
|
c := r.c
|
||||||
if r.seq != r.c.readSeq {
|
if c.messageReader != r {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
for r.c.readErr == nil {
|
for c.readErr == nil {
|
||||||
|
|
||||||
if r.c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
if int64(len(b)) > r.c.readRemaining {
|
if int64(len(b)) > c.readRemaining {
|
||||||
b = b[:r.c.readRemaining]
|
b = b[:c.readRemaining]
|
||||||
}
|
}
|
||||||
n, err := r.c.br.Read(b)
|
n, err := c.br.Read(b)
|
||||||
r.c.readErr = hideTempErr(err)
|
c.readErr = hideTempErr(err)
|
||||||
if r.c.isServer {
|
if c.isServer {
|
||||||
r.c.readMaskPos = maskBytes(r.c.readMaskKey, r.c.readMaskPos, b[:n])
|
c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
|
||||||
}
|
}
|
||||||
r.c.readRemaining -= int64(n)
|
c.readRemaining -= int64(n)
|
||||||
return n, r.c.readErr
|
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||||
|
c.readErr = errUnexpectedEOF
|
||||||
|
}
|
||||||
|
return n, c.readErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.c.readFinal {
|
if c.readFinal {
|
||||||
r.c.readSeq++
|
c.messageReader = nil
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
frameType, err := r.c.advanceFrame()
|
frameType, err := c.advanceFrame()
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
r.c.readErr = hideTempErr(err)
|
c.readErr = hideTempErr(err)
|
||||||
case frameType == TextMessage || frameType == BinaryMessage:
|
case frameType == TextMessage || frameType == BinaryMessage:
|
||||||
r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := r.c.readErr
|
err := c.readErr
|
||||||
if err == io.EOF && r.seq == r.c.readSeq {
|
if err == io.EOF && c.messageReader == r {
|
||||||
err = errUnexpectedEOF
|
err = errUnexpectedEOF
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *messageReader) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadMessage is a helper method for getting a reader using NextReader and
|
// ReadMessage is a helper method for getting a reader using NextReader and
|
||||||
// reading from that reader to a buffer.
|
// reading from that reader to a buffer.
|
||||||
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
||||||
@ -872,9 +1039,49 @@ func (c *Conn) SetReadLimit(limit int64) {
|
|||||||
c.readLimit = limit
|
c.readLimit = limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseHandler returns the current close handler
|
||||||
|
func (c *Conn) CloseHandler() func(code int, text string) error {
|
||||||
|
return c.handleClose
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||||
|
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||||
|
// if the close message is empty. The default close handler sends a close frame
|
||||||
|
// back to the peer.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process close messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
|
//
|
||||||
|
// The connection read methods return a CloseError when a close frame is
|
||||||
|
// received. Most applications should handle close messages as part of their
|
||||||
|
// normal error handling. Applications should only set a close handler when the
|
||||||
|
// application must perform some action before sending a close frame back to
|
||||||
|
// the peer.
|
||||||
|
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||||
|
if h == nil {
|
||||||
|
h = func(code int, text string) error {
|
||||||
|
message := []byte{}
|
||||||
|
if code != CloseNoStatusReceived {
|
||||||
|
message = FormatCloseMessage(code, "")
|
||||||
|
}
|
||||||
|
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.handleClose = h
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingHandler returns the current ping handler
|
||||||
|
func (c *Conn) PingHandler() func(appData string) error {
|
||||||
|
return c.handlePing
|
||||||
|
}
|
||||||
|
|
||||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||||
// The appData argument to h is the PING frame application data. The default
|
// The appData argument to h is the PING frame application data. The default
|
||||||
// ping handler sends a pong to the peer.
|
// ping handler sends a pong to the peer.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process ping messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(message string) error {
|
h = func(message string) error {
|
||||||
@ -890,9 +1097,17 @@ func (c *Conn) SetPingHandler(h func(appData string) error) {
|
|||||||
c.handlePing = h
|
c.handlePing = h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PongHandler returns the current pong handler
|
||||||
|
func (c *Conn) PongHandler() func(appData string) error {
|
||||||
|
return c.handlePong
|
||||||
|
}
|
||||||
|
|
||||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||||
// The appData argument to h is the PONG frame application data. The default
|
// The appData argument to h is the PONG frame application data. The default
|
||||||
// pong handler does nothing.
|
// pong handler does nothing.
|
||||||
|
//
|
||||||
|
// The application must read the connection to process ping messages as
|
||||||
|
// described in the section on Control Frames above.
|
||||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(string) error { return nil }
|
h = func(string) error { return nil }
|
||||||
@ -906,6 +1121,25 @@ func (c *Conn) UnderlyingConn() net.Conn {
|
|||||||
return c.conn
|
return c.conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableWriteCompression enables and disables write compression of
|
||||||
|
// subsequent text and binary messages. This function is a noop if
|
||||||
|
// compression was not negotiated with the peer.
|
||||||
|
func (c *Conn) EnableWriteCompression(enable bool) {
|
||||||
|
c.enableWriteCompression = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCompressionLevel sets the flate compression level for subsequent text and
|
||||||
|
// binary messages. This function is a noop if compression was not negotiated
|
||||||
|
// with the peer. See the compress/flate package for a description of
|
||||||
|
// compression levels.
|
||||||
|
func (c *Conn) SetCompressionLevel(level int) error {
|
||||||
|
if !isValidCompressionLevel(level) {
|
||||||
|
return errors.New("websocket: invalid compression level")
|
||||||
|
}
|
||||||
|
c.compressionLevel = level
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||||
buf := make([]byte, 2+len(text))
|
buf := make([]byte, 2+len(text))
|
||||||
|
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 go1.5
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (c *Conn) read(n int) ([]byte, error) {
|
||||||
|
p, err := c.br.Peek(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = errUnexpectedEOF
|
||||||
|
}
|
||||||
|
c.br.Discard(len(p))
|
||||||
|
return p, err
|
||||||
|
}
|
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 !go1.5
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (c *Conn) read(n int) ([]byte, error) {
|
||||||
|
p, err := c.br.Peek(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = errUnexpectedEOF
|
||||||
|
}
|
||||||
|
if len(p) > 0 {
|
||||||
|
// advance over the bytes just read
|
||||||
|
io.ReadFull(c.br, p)
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@ -118,9 +118,10 @@
|
|||||||
//
|
//
|
||||||
// Applications are responsible for ensuring that no more than one goroutine
|
// Applications are responsible for ensuring that no more than one goroutine
|
||||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
||||||
// WriteJSON) concurrently and that no more than one goroutine calls the read
|
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
||||||
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
|
// that no more than one goroutine calls the read methods (NextReader,
|
||||||
// SetPingHandler) concurrently.
|
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
||||||
|
// concurrently.
|
||||||
//
|
//
|
||||||
// The Close and WriteControl methods can be called concurrently with all other
|
// The Close and WriteControl methods can be called concurrently with all other
|
||||||
// methods.
|
// methods.
|
||||||
@ -149,4 +150,31 @@
|
|||||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||||
// application's responsibility to check the Origin header before calling
|
// application's responsibility to check the Origin header before calling
|
||||||
// Upgrade.
|
// Upgrade.
|
||||||
|
//
|
||||||
|
// Compression EXPERIMENTAL
|
||||||
|
//
|
||||||
|
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||||
|
// by this package in a limited capacity. Setting the EnableCompression option
|
||||||
|
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||||
|
// support.
|
||||||
|
//
|
||||||
|
// var upgrader = websocket.Upgrader{
|
||||||
|
// EnableCompression: true,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If compression was successfully negotiated with the connection's peer, any
|
||||||
|
// message received in compressed form will be automatically decompressed.
|
||||||
|
// All Read methods will return uncompressed bytes.
|
||||||
|
//
|
||||||
|
// Per message compression of messages written to a connection can be enabled
|
||||||
|
// or disabled by calling the corresponding Conn method:
|
||||||
|
//
|
||||||
|
// conn.EnableWriteCompression(false)
|
||||||
|
//
|
||||||
|
// Currently this package does not support compression with "context takeover".
|
||||||
|
// This means that messages must be compressed and decompressed in isolation,
|
||||||
|
// without retaining sliding window or dictionary state across messages. For
|
||||||
|
// more details refer to RFC 7692.
|
||||||
|
//
|
||||||
|
// Use of compression is experimental and may result in decreased performance.
|
||||||
package websocket
|
package websocket
|
||||||
|
37
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
generated
vendored
37
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
generated
vendored
@ -8,17 +8,19 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 4096,
|
ReadBufferSize: 4096,
|
||||||
WriteBufferSize: 4096,
|
WriteBufferSize: 4096,
|
||||||
|
EnableCompression: true,
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
@ -83,7 +85,7 @@ func echoCopyFull(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// echoReadAll echoes messages from the client by reading the entire message
|
// echoReadAll echoes messages from the client by reading the entire message
|
||||||
// with ioutil.ReadAll.
|
// with ioutil.ReadAll.
|
||||||
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) {
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Upgrade:", err)
|
log.Println("Upgrade:", err)
|
||||||
@ -107,9 +109,21 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if writeMessage {
|
if writeMessage {
|
||||||
err = conn.WriteMessage(mt, b)
|
if !writePrepared {
|
||||||
if err != nil {
|
err = conn.WriteMessage(mt, b)
|
||||||
log.Println("WriteMessage:", err)
|
if err != nil {
|
||||||
|
log.Println("WriteMessage:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pm, err := websocket.NewPreparedMessage(mt, b)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("NewPreparedMessage:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = conn.WritePreparedMessage(pm)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("WritePreparedMessage:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
w, err := conn.NextWriter(mt)
|
w, err := conn.NextWriter(mt)
|
||||||
@ -130,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) {
|
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) {
|
||||||
echoReadAll(w, r, false)
|
echoReadAll(w, r, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {
|
func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {
|
||||||
echoReadAll(w, r, true)
|
echoReadAll(w, r, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
echoReadAll(w, r, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveHome(w http.ResponseWriter, r *http.Request) {
|
func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -159,6 +177,7 @@ func main() {
|
|||||||
http.HandleFunc("/f", echoCopyFull)
|
http.HandleFunc("/f", echoCopyFull)
|
||||||
http.HandleFunc("/r", echoReadAllWriter)
|
http.HandleFunc("/r", echoReadAllWriter)
|
||||||
http.HandleFunc("/m", echoReadAllWriteMessage)
|
http.HandleFunc("/m", echoReadAllWriteMessage)
|
||||||
|
http.HandleFunc("/p", echoReadAllWritePreparedMessage)
|
||||||
err := http.ListenAndServe(*addr, nil)
|
err := http.ListenAndServe(*addr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("ListenAndServe: ", err)
|
log.Fatal("ListenAndServe: ", err)
|
||||||
|
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
Normal file
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2013 The Gorilla WebSocket 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Time allowed to write a message to the peer.
|
||||||
|
writeWait = 10 * time.Second
|
||||||
|
|
||||||
|
// Time allowed to read the next pong message from the peer.
|
||||||
|
pongWait = 60 * time.Second
|
||||||
|
|
||||||
|
// Send pings to peer with this period. Must be less than pongWait.
|
||||||
|
pingPeriod = (pongWait * 9) / 10
|
||||||
|
|
||||||
|
// Maximum message size allowed from peer.
|
||||||
|
maxMessageSize = 512
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
newline = []byte{'\n'}
|
||||||
|
space = []byte{' '}
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is a middleman between the websocket connection and the hub.
|
||||||
|
type Client struct {
|
||||||
|
hub *Hub
|
||||||
|
|
||||||
|
// The websocket connection.
|
||||||
|
conn *websocket.Conn
|
||||||
|
|
||||||
|
// Buffered channel of outbound messages.
|
||||||
|
send chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// readPump pumps messages from the websocket connection to the hub.
|
||||||
|
//
|
||||||
|
// The application runs readPump in a per-connection goroutine. The application
|
||||||
|
// ensures that there is at most one reader on a connection by executing all
|
||||||
|
// reads from this goroutine.
|
||||||
|
func (c *Client) readPump() {
|
||||||
|
defer func() {
|
||||||
|
c.hub.unregister <- c
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
c.conn.SetReadLimit(maxMessageSize)
|
||||||
|
c.conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||||
|
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||||
|
for {
|
||||||
|
_, message, err := c.conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||||
|
log.Printf("error: %v", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
|
||||||
|
c.hub.broadcast <- message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePump pumps messages from the hub to the websocket connection.
|
||||||
|
//
|
||||||
|
// A goroutine running writePump is started for each connection. The
|
||||||
|
// application ensures that there is at most one writer to a connection by
|
||||||
|
// executing all writes from this goroutine.
|
||||||
|
func (c *Client) writePump() {
|
||||||
|
ticker := time.NewTicker(pingPeriod)
|
||||||
|
defer func() {
|
||||||
|
ticker.Stop()
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message, ok := <-c.send:
|
||||||
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if !ok {
|
||||||
|
// The hub closed the channel.
|
||||||
|
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := c.conn.NextWriter(websocket.TextMessage)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(message)
|
||||||
|
|
||||||
|
// Add queued chat messages to the current websocket message.
|
||||||
|
n := len(c.send)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
w.Write(newline)
|
||||||
|
w.Write(<-c.send)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveWs handles websocket requests from the peer.
|
||||||
|
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||||
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
||||||
|
client.hub.register <- client
|
||||||
|
go client.writePump()
|
||||||
|
client.readPump()
|
||||||
|
}
|
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
generated
vendored
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
generated
vendored
@ -1,105 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Time allowed to write a message to the peer.
|
|
||||||
writeWait = 10 * time.Second
|
|
||||||
|
|
||||||
// Time allowed to read the next pong message from the peer.
|
|
||||||
pongWait = 60 * time.Second
|
|
||||||
|
|
||||||
// Send pings to peer with this period. Must be less than pongWait.
|
|
||||||
pingPeriod = (pongWait * 9) / 10
|
|
||||||
|
|
||||||
// Maximum message size allowed from peer.
|
|
||||||
maxMessageSize = 512
|
|
||||||
)
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
}
|
|
||||||
|
|
||||||
// connection is an middleman between the websocket connection and the hub.
|
|
||||||
type connection struct {
|
|
||||||
// The websocket connection.
|
|
||||||
ws *websocket.Conn
|
|
||||||
|
|
||||||
// Buffered channel of outbound messages.
|
|
||||||
send chan []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// readPump pumps messages from the websocket connection to the hub.
|
|
||||||
func (c *connection) readPump() {
|
|
||||||
defer func() {
|
|
||||||
h.unregister <- c
|
|
||||||
c.ws.Close()
|
|
||||||
}()
|
|
||||||
c.ws.SetReadLimit(maxMessageSize)
|
|
||||||
c.ws.SetReadDeadline(time.Now().Add(pongWait))
|
|
||||||
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
|
||||||
for {
|
|
||||||
_, message, err := c.ws.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
|
||||||
log.Printf("error: %v", err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
h.broadcast <- message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// write writes a message with the given message type and payload.
|
|
||||||
func (c *connection) write(mt int, payload []byte) error {
|
|
||||||
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
|
|
||||||
return c.ws.WriteMessage(mt, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// writePump pumps messages from the hub to the websocket connection.
|
|
||||||
func (c *connection) writePump() {
|
|
||||||
ticker := time.NewTicker(pingPeriod)
|
|
||||||
defer func() {
|
|
||||||
ticker.Stop()
|
|
||||||
c.ws.Close()
|
|
||||||
}()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case message, ok := <-c.send:
|
|
||||||
if !ok {
|
|
||||||
c.write(websocket.CloseMessage, []byte{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := c.write(websocket.TextMessage, message); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-ticker.C:
|
|
||||||
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// serveWs handles websocket requests from the peer.
|
|
||||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ws, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c := &connection{send: make(chan []byte, 256), ws: ws}
|
|
||||||
h.register <- c
|
|
||||||
go c.writePump()
|
|
||||||
c.readPump()
|
|
||||||
}
|
|
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
generated
vendored
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
generated
vendored
@ -4,46 +4,48 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// hub maintains the set of active connections and broadcasts messages to the
|
// hub maintains the set of active clients and broadcasts messages to the
|
||||||
// connections.
|
// clients.
|
||||||
type hub struct {
|
type Hub struct {
|
||||||
// Registered connections.
|
// Registered clients.
|
||||||
connections map[*connection]bool
|
clients map[*Client]bool
|
||||||
|
|
||||||
// Inbound messages from the connections.
|
// Inbound messages from the clients.
|
||||||
broadcast chan []byte
|
broadcast chan []byte
|
||||||
|
|
||||||
// Register requests from the connections.
|
// Register requests from the clients.
|
||||||
register chan *connection
|
register chan *Client
|
||||||
|
|
||||||
// Unregister requests from connections.
|
// Unregister requests from clients.
|
||||||
unregister chan *connection
|
unregister chan *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var h = hub{
|
func newHub() *Hub {
|
||||||
broadcast: make(chan []byte),
|
return &Hub{
|
||||||
register: make(chan *connection),
|
broadcast: make(chan []byte),
|
||||||
unregister: make(chan *connection),
|
register: make(chan *Client),
|
||||||
connections: make(map[*connection]bool),
|
unregister: make(chan *Client),
|
||||||
|
clients: make(map[*Client]bool),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hub) run() {
|
func (h *Hub) run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case c := <-h.register:
|
case client := <-h.register:
|
||||||
h.connections[c] = true
|
h.clients[client] = true
|
||||||
case c := <-h.unregister:
|
case client := <-h.unregister:
|
||||||
if _, ok := h.connections[c]; ok {
|
if _, ok := h.clients[client]; ok {
|
||||||
delete(h.connections, c)
|
delete(h.clients, client)
|
||||||
close(c.send)
|
close(client.send)
|
||||||
}
|
}
|
||||||
case m := <-h.broadcast:
|
case message := <-h.broadcast:
|
||||||
for c := range h.connections {
|
for client := range h.clients {
|
||||||
select {
|
select {
|
||||||
case c.send <- m:
|
case client.send <- message:
|
||||||
default:
|
default:
|
||||||
close(c.send)
|
close(client.send)
|
||||||
delete(h.connections, c)
|
delete(h.clients, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
vendor/github.com/gorilla/websocket/examples/chat/main.go
generated
vendored
13
vendor/github.com/gorilla/websocket/examples/chat/main.go
generated
vendored
@ -8,13 +8,12 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var addr = flag.String("addr", ":8080", "http service address")
|
var addr = flag.String("addr", ":8080", "http service address")
|
||||||
var homeTempl = template.Must(template.ParseFiles("home.html"))
|
|
||||||
|
|
||||||
func serveHome(w http.ResponseWriter, r *http.Request) {
|
func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println(r.URL)
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
http.Error(w, "Not found", 404)
|
http.Error(w, "Not found", 404)
|
||||||
return
|
return
|
||||||
@ -23,15 +22,17 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Method not allowed", 405)
|
http.Error(w, "Method not allowed", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
http.ServeFile(w, r, "home.html")
|
||||||
homeTempl.Execute(w, r.Host)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
go h.run()
|
hub := newHub()
|
||||||
|
go hub.run()
|
||||||
http.HandleFunc("/", serveHome)
|
http.HandleFunc("/", serveHome)
|
||||||
http.HandleFunc("/ws", serveWs)
|
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
serveWs(hub, w, r)
|
||||||
|
})
|
||||||
err := http.ListenAndServe(*addr, nil)
|
err := http.ListenAndServe(*addr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("ListenAndServe: ", err)
|
log.Fatal("ListenAndServe: ", err)
|
||||||
|
21
vendor/github.com/gorilla/websocket/examples/command/main.go
generated
vendored
21
vendor/github.com/gorilla/websocket/examples/command/main.go
generated
vendored
@ -12,16 +12,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
addr = flag.String("addr", "127.0.0.1:8080", "http service address")
|
addr = flag.String("addr", "127.0.0.1:8080", "http service address")
|
||||||
cmdPath string
|
cmdPath string
|
||||||
homeTempl = template.Must(template.ParseFiles("home.html"))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -36,6 +34,9 @@ const (
|
|||||||
|
|
||||||
// Send pings to peer with this period. Must be less than pongWait.
|
// Send pings to peer with this period. Must be less than pongWait.
|
||||||
pingPeriod = (pongWait * 9) / 10
|
pingPeriod = (pongWait * 9) / 10
|
||||||
|
|
||||||
|
// Time to wait before force close on connection.
|
||||||
|
closeGracePeriod = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
||||||
@ -57,19 +58,24 @@ func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
|||||||
|
|
||||||
func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {
|
func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {
|
||||||
defer func() {
|
defer func() {
|
||||||
ws.Close()
|
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
s := bufio.NewScanner(r)
|
s := bufio.NewScanner(r)
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
|
if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
|
||||||
|
ws.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.Err() != nil {
|
if s.Err() != nil {
|
||||||
log.Println("scan:", s.Err())
|
log.Println("scan:", s.Err())
|
||||||
}
|
}
|
||||||
|
close(done)
|
||||||
|
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
time.Sleep(closeGracePeriod)
|
||||||
|
ws.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ping(ws *websocket.Conn, done chan struct{}) {
|
func ping(ws *websocket.Conn, done chan struct{}) {
|
||||||
@ -168,8 +174,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Method not allowed", 405)
|
http.Error(w, "Method not allowed", 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
http.ServeFile(w, r, "home.html")
|
||||||
homeTempl.Execute(w, r.Host)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
4
vendor/github.com/gorilla/websocket/examples/filewatch/main.go
generated
vendored
4
vendor/github.com/gorilla/websocket/examples/filewatch/main.go
generated
vendored
@ -6,12 +6,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
@ -120,7 +120,7 @@ func serveWs(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lastMod time.Time
|
var lastMod time.Time
|
||||||
if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil {
|
if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil {
|
||||||
lastMod = time.Unix(0, n)
|
lastMod = time.Unix(0, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 !appengine
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
|
||||||
|
// Mask one byte at a time for small buffers.
|
||||||
|
if len(b) < 2*wordSize {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time to word boundary.
|
||||||
|
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||||
|
n = wordSize - n
|
||||||
|
for i := range b[:n] {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create aligned word size key.
|
||||||
|
var k [wordSize]byte
|
||||||
|
for i := range k {
|
||||||
|
k[i] = key[(pos+i)&3]
|
||||||
|
}
|
||||||
|
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||||
|
|
||||||
|
// Mask one word at a time.
|
||||||
|
n := (len(b) / wordSize) * wordSize
|
||||||
|
for i := 0; i < n; i += wordSize {
|
||||||
|
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time for remaining bytes.
|
||||||
|
b = b[n:]
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos & 3
|
||||||
|
}
|
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2016 The Gorilla WebSocket 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 appengine
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2017 The Gorilla WebSocket 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 websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PreparedMessage caches on the wire representations of a message payload.
|
||||||
|
// Use PreparedMessage to efficiently send a message payload to multiple
|
||||||
|
// connections. PreparedMessage is especially useful when compression is used
|
||||||
|
// because the CPU and memory expensive compression operation can be executed
|
||||||
|
// once for a given set of compression options.
|
||||||
|
type PreparedMessage struct {
|
||||||
|
messageType int
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
mu sync.Mutex
|
||||||
|
frames map[prepareKey]*preparedFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
|
||||||
|
type prepareKey struct {
|
||||||
|
isServer bool
|
||||||
|
compress bool
|
||||||
|
compressionLevel int
|
||||||
|
}
|
||||||
|
|
||||||
|
// preparedFrame contains data in wire representation.
|
||||||
|
type preparedFrame struct {
|
||||||
|
once sync.Once
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
|
||||||
|
// it to connection using WritePreparedMessage method. Valid wire
|
||||||
|
// representation will be calculated lazily only once for a set of current
|
||||||
|
// connection options.
|
||||||
|
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
|
||||||
|
pm := &PreparedMessage{
|
||||||
|
messageType: messageType,
|
||||||
|
frames: make(map[prepareKey]*preparedFrame),
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare a plain server frame.
|
||||||
|
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// To protect against caller modifying the data argument, remember the data
|
||||||
|
// copied to the plain server frame.
|
||||||
|
pm.data = frameData[len(frameData)-len(data):]
|
||||||
|
return pm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
|
||||||
|
pm.mu.Lock()
|
||||||
|
frame, ok := pm.frames[key]
|
||||||
|
if !ok {
|
||||||
|
frame = &preparedFrame{}
|
||||||
|
pm.frames[key] = frame
|
||||||
|
}
|
||||||
|
pm.mu.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
frame.once.Do(func() {
|
||||||
|
// Prepare a frame using a 'fake' connection.
|
||||||
|
// TODO: Refactor code in conn.go to allow more direct construction of
|
||||||
|
// the frame.
|
||||||
|
mu := make(chan bool, 1)
|
||||||
|
mu <- true
|
||||||
|
var nc prepareConn
|
||||||
|
c := &Conn{
|
||||||
|
conn: &nc,
|
||||||
|
mu: mu,
|
||||||
|
isServer: key.isServer,
|
||||||
|
compressionLevel: key.compressionLevel,
|
||||||
|
enableWriteCompression: true,
|
||||||
|
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
|
||||||
|
}
|
||||||
|
if key.compress {
|
||||||
|
c.newCompressionWriter = compressNoContextTakeover
|
||||||
|
}
|
||||||
|
err = c.WriteMessage(pm.messageType, pm.data)
|
||||||
|
frame.data = nc.buf.Bytes()
|
||||||
|
})
|
||||||
|
return pm.messageType, frame.data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type prepareConn struct {
|
||||||
|
buf bytes.Buffer
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
|
||||||
|
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
|
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@ -28,8 +28,9 @@ type Upgrader struct {
|
|||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
|
|
||||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||||
// size is zero, then a default value of 4096 is used. The I/O buffer sizes
|
// size is zero, then buffers allocated by the HTTP server are used. The
|
||||||
// do not limit the size of the messages that can be sent or received.
|
// I/O buffer sizes do not limit the size of the messages that can be sent
|
||||||
|
// or received.
|
||||||
ReadBufferSize, WriteBufferSize int
|
ReadBufferSize, WriteBufferSize int
|
||||||
|
|
||||||
// Subprotocols specifies the server's supported protocols in order of
|
// Subprotocols specifies the server's supported protocols in order of
|
||||||
@ -46,6 +47,12 @@ type Upgrader struct {
|
|||||||
// CheckOrigin is nil, the host in the Origin header must not be set or
|
// CheckOrigin is nil, the host in the Origin header must not be set or
|
||||||
// must match the host of the request.
|
// must match the host of the request.
|
||||||
CheckOrigin func(r *http.Request) bool
|
CheckOrigin func(r *http.Request) bool
|
||||||
|
|
||||||
|
// EnableCompression specify if the server should attempt to negotiate per
|
||||||
|
// message compression (RFC 7692). Setting this value to true does not
|
||||||
|
// guarantee that compression will be supported. Currently only "no context
|
||||||
|
// takeover" modes are supported.
|
||||||
|
EnableCompression bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||||
@ -53,6 +60,7 @@ func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status in
|
|||||||
if u.Error != nil {
|
if u.Error != nil {
|
||||||
u.Error(w, r, status, err)
|
u.Error(w, r, status, err)
|
||||||
} else {
|
} else {
|
||||||
|
w.Header().Set("Sec-Websocket-Version", "13")
|
||||||
http.Error(w, http.StatusText(status), status)
|
http.Error(w, http.StatusText(status), status)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -97,18 +105,23 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
|||||||
// response.
|
// response.
|
||||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||||
if r.Method != "GET" {
|
if r.Method != "GET" {
|
||||||
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET")
|
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||||
}
|
}
|
||||||
if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" {
|
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
|
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||||
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||||
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOrigin := u.CheckOrigin
|
checkOrigin := u.CheckOrigin
|
||||||
@ -116,19 +129,30 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
checkOrigin = checkSameOrigin
|
checkOrigin = checkSameOrigin
|
||||||
}
|
}
|
||||||
if !checkOrigin(r) {
|
if !checkOrigin(r) {
|
||||||
return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
|
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||||
if challengeKey == "" {
|
if challengeKey == "" {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||||
|
|
||||||
|
// Negotiate PMCE
|
||||||
|
var compress bool
|
||||||
|
if u.EnableCompression {
|
||||||
|
for _, ext := range parseExtensions(r.Header) {
|
||||||
|
if ext[""] != "permessage-deflate" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
compress = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
netConn net.Conn
|
netConn net.Conn
|
||||||
br *bufio.Reader
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -136,21 +160,25 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
if !ok {
|
if !ok {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||||
}
|
}
|
||||||
var rw *bufio.ReadWriter
|
var brw *bufio.ReadWriter
|
||||||
netConn, rw, err = h.Hijack()
|
netConn, brw, err = h.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
br = rw.Reader
|
|
||||||
|
|
||||||
if br.Buffered() > 0 {
|
if brw.Reader.Buffered() > 0 {
|
||||||
netConn.Close()
|
netConn.Close()
|
||||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
|
c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
|
||||||
c.subprotocol = subprotocol
|
c.subprotocol = subprotocol
|
||||||
|
|
||||||
|
if compress {
|
||||||
|
c.newCompressionWriter = compressNoContextTakeover
|
||||||
|
c.newDecompressionReader = decompressNoContextTakeover
|
||||||
|
}
|
||||||
|
|
||||||
p := c.writeBuf[:0]
|
p := c.writeBuf[:0]
|
||||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||||
p = append(p, computeAcceptKey(challengeKey)...)
|
p = append(p, computeAcceptKey(challengeKey)...)
|
||||||
@ -160,6 +188,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
p = append(p, c.subprotocol...)
|
p = append(p, c.subprotocol...)
|
||||||
p = append(p, "\r\n"...)
|
p = append(p, "\r\n"...)
|
||||||
}
|
}
|
||||||
|
if compress {
|
||||||
|
p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||||
|
}
|
||||||
for k, vs := range responseHeader {
|
for k, vs := range responseHeader {
|
||||||
if k == "Sec-Websocket-Protocol" {
|
if k == "Sec-Websocket-Protocol" {
|
||||||
continue
|
continue
|
||||||
|
196
vendor/github.com/gorilla/websocket/util.go
generated
vendored
196
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@ -13,19 +13,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tokenListContainsValue returns true if the 1#token header with the given
|
|
||||||
// name contains token.
|
|
||||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
|
||||||
for _, v := range header[name] {
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if strings.EqualFold(value, strings.TrimSpace(s)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
|
||||||
func computeAcceptKey(challengeKey string) string {
|
func computeAcceptKey(challengeKey string) string {
|
||||||
@ -42,3 +29,186 @@ func generateChallengeKey() (string, error) {
|
|||||||
}
|
}
|
||||||
return base64.StdEncoding.EncodeToString(p), nil
|
return base64.StdEncoding.EncodeToString(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Octet types from RFC 2616.
|
||||||
|
var octetTypes [256]byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
isTokenOctet = 1 << iota
|
||||||
|
isSpaceOctet
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// From RFC 2616
|
||||||
|
//
|
||||||
|
// OCTET = <any 8-bit sequence of data>
|
||||||
|
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||||
|
// CR = <US-ASCII CR, carriage return (13)>
|
||||||
|
// LF = <US-ASCII LF, linefeed (10)>
|
||||||
|
// SP = <US-ASCII SP, space (32)>
|
||||||
|
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||||
|
// <"> = <US-ASCII double-quote mark (34)>
|
||||||
|
// CRLF = CR LF
|
||||||
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||||
|
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||||
|
// token = 1*<any CHAR except CTLs or separators>
|
||||||
|
// qdtext = <any TEXT except <">>
|
||||||
|
|
||||||
|
for c := 0; c < 256; c++ {
|
||||||
|
var t byte
|
||||||
|
isCtl := c <= 31 || c == 127
|
||||||
|
isChar := 0 <= c && c <= 127
|
||||||
|
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||||
|
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||||
|
t |= isSpaceOctet
|
||||||
|
}
|
||||||
|
if isChar && !isCtl && !isSeparator {
|
||||||
|
t |= isTokenOctet
|
||||||
|
}
|
||||||
|
octetTypes[c] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipSpace(s string) (rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isSpaceOctet == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextToken(s string) (token, rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isTokenOctet == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[:i], s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||||
|
if !strings.HasPrefix(s, "\"") {
|
||||||
|
return nextToken(s)
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '"':
|
||||||
|
return s[:i], s[i+1:]
|
||||||
|
case '\\':
|
||||||
|
p := make([]byte, len(s)-1)
|
||||||
|
j := copy(p, s[:i])
|
||||||
|
escape := true
|
||||||
|
for i = i + 1; i < len(s); i++ {
|
||||||
|
b := s[i]
|
||||||
|
switch {
|
||||||
|
case escape:
|
||||||
|
escape = false
|
||||||
|
p[j] = b
|
||||||
|
j += 1
|
||||||
|
case b == '\\':
|
||||||
|
escape = true
|
||||||
|
case b == '"':
|
||||||
|
return string(p[:j]), s[i+1:]
|
||||||
|
default:
|
||||||
|
p[j] = b
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenListContainsValue returns true if the 1#token header with the given
|
||||||
|
// name contains token.
|
||||||
|
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||||
|
headers:
|
||||||
|
for _, s := range header[name] {
|
||||||
|
for {
|
||||||
|
var t string
|
||||||
|
t, s = nextToken(skipSpace(s))
|
||||||
|
if t == "" {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
s = skipSpace(s)
|
||||||
|
if s != "" && s[0] != ',' {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
if strings.EqualFold(t, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseExtensiosn parses WebSocket extensions from a header.
|
||||||
|
func parseExtensions(header http.Header) []map[string]string {
|
||||||
|
|
||||||
|
// From RFC 6455:
|
||||||
|
//
|
||||||
|
// Sec-WebSocket-Extensions = extension-list
|
||||||
|
// extension-list = 1#extension
|
||||||
|
// extension = extension-token *( ";" extension-param )
|
||||||
|
// extension-token = registered-token
|
||||||
|
// registered-token = token
|
||||||
|
// extension-param = token [ "=" (token | quoted-string) ]
|
||||||
|
// ;When using the quoted-string syntax variant, the value
|
||||||
|
// ;after quoted-string unescaping MUST conform to the
|
||||||
|
// ;'token' ABNF.
|
||||||
|
|
||||||
|
var result []map[string]string
|
||||||
|
headers:
|
||||||
|
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||||
|
for {
|
||||||
|
var t string
|
||||||
|
t, s = nextToken(skipSpace(s))
|
||||||
|
if t == "" {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
ext := map[string]string{"": t}
|
||||||
|
for {
|
||||||
|
s = skipSpace(s)
|
||||||
|
if !strings.HasPrefix(s, ";") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var k string
|
||||||
|
k, s = nextToken(skipSpace(s[1:]))
|
||||||
|
if k == "" {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
s = skipSpace(s)
|
||||||
|
var v string
|
||||||
|
if strings.HasPrefix(s, "=") {
|
||||||
|
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||||
|
s = skipSpace(s)
|
||||||
|
}
|
||||||
|
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
ext[k] = v
|
||||||
|
}
|
||||||
|
if s != "" && s[0] != ',' {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
result = append(result, ext)
|
||||||
|
if s == "" {
|
||||||
|
continue headers
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
21
vendor/github.com/jpillora/backoff/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jpillora/backoff/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Jaime Pillora
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
83
vendor/github.com/jpillora/backoff/backoff.go
generated
vendored
83
vendor/github.com/jpillora/backoff/backoff.go
generated
vendored
@ -1,3 +1,4 @@
|
|||||||
|
// Package backoff provides an exponential-backoff implementation.
|
||||||
package backoff
|
package backoff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,64 +7,82 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Backoff is a time.Duration counter. It starts at Min.
|
// Backoff is a time.Duration counter, starting at Min. After every call to
|
||||||
//After every call to Duration() it is multiplied by Factor.
|
// the Duration method the current timing is multiplied by Factor, but it
|
||||||
//It is capped at Max. It returns to Min on every call to Reset().
|
// never exceeds Max.
|
||||||
//Used in conjunction with the time package.
|
|
||||||
//
|
//
|
||||||
// Backoff is not threadsafe, but the ForAttempt method can be
|
// Backoff is not generally concurrent-safe, but the ForAttempt method can
|
||||||
// used concurrently if non-zero values for Factor, Max, and Min
|
// be used concurrently.
|
||||||
// are set on the Backoff shared among threads.
|
|
||||||
type Backoff struct {
|
type Backoff struct {
|
||||||
//Factor is the multiplying factor for each increment step
|
//Factor is the multiplying factor for each increment step
|
||||||
attempts, Factor float64
|
attempt, Factor float64
|
||||||
//Jitter eases contention by randomizing backoff steps
|
//Jitter eases contention by randomizing backoff steps
|
||||||
Jitter bool
|
Jitter bool
|
||||||
//Min and Max are the minimum and maximum values of the counter
|
//Min and Max are the minimum and maximum values of the counter
|
||||||
Min, Max time.Duration
|
Min, Max time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns the current value of the counter and then
|
// Duration returns the duration for the current attempt before incrementing
|
||||||
//multiplies it Factor
|
// the attempt counter. See ForAttempt.
|
||||||
func (b *Backoff) Duration() time.Duration {
|
func (b *Backoff) Duration() time.Duration {
|
||||||
d := b.ForAttempt(b.attempts)
|
d := b.ForAttempt(b.attempt)
|
||||||
b.attempts++
|
b.attempt++
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxInt64 = float64(math.MaxInt64 - 512)
|
||||||
|
|
||||||
// ForAttempt returns the duration for a specific attempt. This is useful if
|
// ForAttempt returns the duration for a specific attempt. This is useful if
|
||||||
// you have a large number of independent Backoffs, but don't want use
|
// you have a large number of independent Backoffs, but don't want use
|
||||||
// unnecessary memory storing the Backoff parameters per Backoff. The first
|
// unnecessary memory storing the Backoff parameters per Backoff. The first
|
||||||
// attempt should be 0.
|
// attempt should be 0.
|
||||||
//
|
//
|
||||||
// ForAttempt is threadsafe iff non-zero values for Factor, Max, and Min
|
// ForAttempt is concurrent-safe.
|
||||||
// are set before any calls to ForAttempt are made.
|
|
||||||
func (b *Backoff) ForAttempt(attempt float64) time.Duration {
|
func (b *Backoff) ForAttempt(attempt float64) time.Duration {
|
||||||
//Zero-values are nonsensical, so we use
|
// Zero-values are nonsensical, so we use
|
||||||
//them to apply defaults
|
// them to apply defaults
|
||||||
if b.Min == 0 {
|
min := b.Min
|
||||||
b.Min = 100 * time.Millisecond
|
if min <= 0 {
|
||||||
|
min = 100 * time.Millisecond
|
||||||
}
|
}
|
||||||
if b.Max == 0 {
|
max := b.Max
|
||||||
b.Max = 10 * time.Second
|
if max <= 0 {
|
||||||
|
max = 10 * time.Second
|
||||||
}
|
}
|
||||||
if b.Factor == 0 {
|
if min >= max {
|
||||||
b.Factor = 2
|
// short-circuit
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
factor := b.Factor
|
||||||
|
if factor <= 0 {
|
||||||
|
factor = 2
|
||||||
}
|
}
|
||||||
//calculate this duration
|
//calculate this duration
|
||||||
dur := float64(b.Min) * math.Pow(b.Factor, attempt)
|
minf := float64(min)
|
||||||
if b.Jitter == true {
|
durf := minf * math.Pow(factor, attempt)
|
||||||
dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
|
if b.Jitter {
|
||||||
|
durf = rand.Float64()*(durf-minf) + minf
|
||||||
}
|
}
|
||||||
//cap!
|
//ensure float64 wont overflow int64
|
||||||
if dur > float64(b.Max) {
|
if durf > maxInt64 {
|
||||||
return b.Max
|
return max
|
||||||
}
|
}
|
||||||
//return as a time.Duration
|
dur := time.Duration(durf)
|
||||||
return time.Duration(dur)
|
//keep within bounds
|
||||||
|
if dur < min {
|
||||||
|
return min
|
||||||
|
} else if dur > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return dur
|
||||||
}
|
}
|
||||||
|
|
||||||
//Resets the current value of the counter back to Min
|
// Reset restarts the current attempt counter at zero.
|
||||||
func (b *Backoff) Reset() {
|
func (b *Backoff) Reset() {
|
||||||
b.attempts = 0
|
b.attempt = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt returns the current attempt counter value.
|
||||||
|
func (b *Backoff) Attempt() float64 {
|
||||||
|
return b.attempt
|
||||||
}
|
}
|
||||||
|
7
vendor/github.com/mattermost/platform/einterfaces/cluster.go
generated
vendored
7
vendor/github.com/mattermost/platform/einterfaces/cluster.go
generated
vendored
@ -12,10 +12,15 @@ type ClusterInterface interface {
|
|||||||
StopInterNodeCommunication()
|
StopInterNodeCommunication()
|
||||||
GetClusterInfos() []*model.ClusterInfo
|
GetClusterInfos() []*model.ClusterInfo
|
||||||
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
|
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
|
||||||
RemoveAllSessionsForUserId(userId string)
|
ClearSessionCacheForUser(userId string)
|
||||||
InvalidateCacheForUser(userId string)
|
InvalidateCacheForUser(userId string)
|
||||||
InvalidateCacheForChannel(channelId string)
|
InvalidateCacheForChannel(channelId string)
|
||||||
|
InvalidateCacheForChannelByName(teamId, name string)
|
||||||
|
InvalidateCacheForChannelMembers(channelId string)
|
||||||
|
InvalidateCacheForChannelMembersNotifyProps(channelId string)
|
||||||
InvalidateCacheForChannelPosts(channelId string)
|
InvalidateCacheForChannelPosts(channelId string)
|
||||||
|
InvalidateCacheForWebhook(webhookId string)
|
||||||
|
InvalidateCacheForReactions(postId string)
|
||||||
Publish(event *model.WebSocketEvent)
|
Publish(event *model.WebSocketEvent)
|
||||||
UpdateStatus(status *model.Status)
|
UpdateStatus(status *model.Status)
|
||||||
GetLogs() ([]string, *model.AppError)
|
GetLogs() ([]string, *model.AppError)
|
||||||
|
8
vendor/github.com/mattermost/platform/einterfaces/metrics.go
generated
vendored
8
vendor/github.com/mattermost/platform/einterfaces/metrics.go
generated
vendored
@ -8,6 +8,7 @@ type MetricsInterface interface {
|
|||||||
StopServer()
|
StopServer()
|
||||||
|
|
||||||
IncrementPostCreate()
|
IncrementPostCreate()
|
||||||
|
IncrementWebhookPost()
|
||||||
IncrementPostSentEmail()
|
IncrementPostSentEmail()
|
||||||
IncrementPostSentPush()
|
IncrementPostSentPush()
|
||||||
IncrementPostBroadcast()
|
IncrementPostBroadcast()
|
||||||
@ -17,6 +18,9 @@ type MetricsInterface interface {
|
|||||||
IncrementHttpError()
|
IncrementHttpError()
|
||||||
ObserveHttpRequestDuration(elapsed float64)
|
ObserveHttpRequestDuration(elapsed float64)
|
||||||
|
|
||||||
|
IncrementClusterRequest()
|
||||||
|
ObserveClusterRequestDuration(elapsed float64)
|
||||||
|
|
||||||
IncrementLogin()
|
IncrementLogin()
|
||||||
IncrementLoginFail()
|
IncrementLoginFail()
|
||||||
|
|
||||||
@ -25,6 +29,10 @@ type MetricsInterface interface {
|
|||||||
|
|
||||||
IncrementMemCacheHitCounter(cacheName string)
|
IncrementMemCacheHitCounter(cacheName string)
|
||||||
IncrementMemCacheMissCounter(cacheName string)
|
IncrementMemCacheMissCounter(cacheName string)
|
||||||
|
IncrementMemCacheMissCounterSession()
|
||||||
|
IncrementMemCacheHitCounterSession()
|
||||||
|
|
||||||
|
IncrementWebsocketEvent(eventType string)
|
||||||
|
|
||||||
AddMemCacheHitCounter(cacheName string, amount float64)
|
AddMemCacheHitCounter(cacheName string, amount float64)
|
||||||
AddMemCacheMissCounter(cacheName string, amount float64)
|
AddMemCacheMissCounter(cacheName string, amount float64)
|
||||||
|
2
vendor/github.com/mattermost/platform/einterfaces/saml.go
generated
vendored
2
vendor/github.com/mattermost/platform/einterfaces/saml.go
generated
vendored
@ -10,7 +10,7 @@ import (
|
|||||||
type SamlInterface interface {
|
type SamlInterface interface {
|
||||||
ConfigureSP() *model.AppError
|
ConfigureSP() *model.AppError
|
||||||
BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError)
|
BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError)
|
||||||
DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError)
|
DoLogin(encodedXML string, relayState map[string]string, siteURL string) (*model.User, *model.AppError)
|
||||||
GetMetadata() (string, *model.AppError)
|
GetMetadata() (string, *model.AppError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
vendor/github.com/mattermost/platform/model/authorization.go
generated
vendored
55
vendor/github.com/mattermost/platform/model/authorization.go
generated
vendored
@ -27,7 +27,10 @@ var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission
|
|||||||
var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission
|
var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission
|
||||||
var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission
|
var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission
|
||||||
var PERMISSION_MANAGE_ROLES *Permission
|
var PERMISSION_MANAGE_ROLES *Permission
|
||||||
|
var PERMISSION_MANAGE_TEAM_ROLES *Permission
|
||||||
|
var PERMISSION_MANAGE_CHANNEL_ROLES *Permission
|
||||||
var PERMISSION_CREATE_DIRECT_CHANNEL *Permission
|
var PERMISSION_CREATE_DIRECT_CHANNEL *Permission
|
||||||
|
var PERMISSION_CREATE_GROUP_CHANNEL *Permission
|
||||||
var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission
|
var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission
|
||||||
var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission
|
var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission
|
||||||
var PERMISSION_LIST_TEAM_CHANNELS *Permission
|
var PERMISSION_LIST_TEAM_CHANNELS *Permission
|
||||||
@ -46,9 +49,13 @@ var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
|
|||||||
var PERMISSION_CREATE_POST *Permission
|
var PERMISSION_CREATE_POST *Permission
|
||||||
var PERMISSION_EDIT_POST *Permission
|
var PERMISSION_EDIT_POST *Permission
|
||||||
var PERMISSION_EDIT_OTHERS_POSTS *Permission
|
var PERMISSION_EDIT_OTHERS_POSTS *Permission
|
||||||
|
var PERMISSION_DELETE_POST *Permission
|
||||||
|
var PERMISSION_DELETE_OTHERS_POSTS *Permission
|
||||||
var PERMISSION_REMOVE_USER_FROM_TEAM *Permission
|
var PERMISSION_REMOVE_USER_FROM_TEAM *Permission
|
||||||
|
var PERMISSION_CREATE_TEAM *Permission
|
||||||
var PERMISSION_MANAGE_TEAM *Permission
|
var PERMISSION_MANAGE_TEAM *Permission
|
||||||
var PERMISSION_IMPORT_TEAM *Permission
|
var PERMISSION_IMPORT_TEAM *Permission
|
||||||
|
var PERMISSION_VIEW_TEAM *Permission
|
||||||
|
|
||||||
// General permission that encompases all system admin functions
|
// General permission that encompases all system admin functions
|
||||||
// in the future this could be broken up to allow access to some
|
// in the future this could be broken up to allow access to some
|
||||||
@ -123,6 +130,16 @@ func InitalizePermissions() {
|
|||||||
"authentication.permissions.manage_roles.name",
|
"authentication.permissions.manage_roles.name",
|
||||||
"authentication.permissions.manage_roles.description",
|
"authentication.permissions.manage_roles.description",
|
||||||
}
|
}
|
||||||
|
PERMISSION_MANAGE_TEAM_ROLES = &Permission{
|
||||||
|
"manage_team_roles",
|
||||||
|
"authentication.permissions.manage_team_roles.name",
|
||||||
|
"authentication.permissions.manage_team_roles.description",
|
||||||
|
}
|
||||||
|
PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{
|
||||||
|
"manage_channel_roles",
|
||||||
|
"authentication.permissions.manage_channel_roles.name",
|
||||||
|
"authentication.permissions.manage_channel_roles.description",
|
||||||
|
}
|
||||||
PERMISSION_MANAGE_SYSTEM = &Permission{
|
PERMISSION_MANAGE_SYSTEM = &Permission{
|
||||||
"manage_system",
|
"manage_system",
|
||||||
"authentication.permissions.manage_system.name",
|
"authentication.permissions.manage_system.name",
|
||||||
@ -133,6 +150,11 @@ func InitalizePermissions() {
|
|||||||
"authentication.permissions.create_direct_channel.name",
|
"authentication.permissions.create_direct_channel.name",
|
||||||
"authentication.permissions.create_direct_channel.description",
|
"authentication.permissions.create_direct_channel.description",
|
||||||
}
|
}
|
||||||
|
PERMISSION_CREATE_GROUP_CHANNEL = &Permission{
|
||||||
|
"create_group_channel",
|
||||||
|
"authentication.permissions.create_group_channel.name",
|
||||||
|
"authentication.permissions.create_group_channel.description",
|
||||||
|
}
|
||||||
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
|
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
|
||||||
"manage__publicchannel_properties",
|
"manage__publicchannel_properties",
|
||||||
"authentication.permissions.manage_public_channel_properties.name",
|
"authentication.permissions.manage_public_channel_properties.name",
|
||||||
@ -223,11 +245,26 @@ func InitalizePermissions() {
|
|||||||
"authentication.permissions.edit_others_posts.name",
|
"authentication.permissions.edit_others_posts.name",
|
||||||
"authentication.permissions.edit_others_posts.description",
|
"authentication.permissions.edit_others_posts.description",
|
||||||
}
|
}
|
||||||
|
PERMISSION_DELETE_POST = &Permission{
|
||||||
|
"delete_post",
|
||||||
|
"authentication.permissions.delete_post.name",
|
||||||
|
"authentication.permissions.delete_post.description",
|
||||||
|
}
|
||||||
|
PERMISSION_DELETE_OTHERS_POSTS = &Permission{
|
||||||
|
"delete_others_posts",
|
||||||
|
"authentication.permissions.delete_others_posts.name",
|
||||||
|
"authentication.permissions.delete_others_posts.description",
|
||||||
|
}
|
||||||
PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
|
PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
|
||||||
"remove_user_from_team",
|
"remove_user_from_team",
|
||||||
"authentication.permissions.remove_user_from_team.name",
|
"authentication.permissions.remove_user_from_team.name",
|
||||||
"authentication.permissions.remove_user_from_team.description",
|
"authentication.permissions.remove_user_from_team.description",
|
||||||
}
|
}
|
||||||
|
PERMISSION_CREATE_TEAM = &Permission{
|
||||||
|
"create_team",
|
||||||
|
"authentication.permissions.create_team.name",
|
||||||
|
"authentication.permissions.create_team.description",
|
||||||
|
}
|
||||||
PERMISSION_MANAGE_TEAM = &Permission{
|
PERMISSION_MANAGE_TEAM = &Permission{
|
||||||
"manage_team",
|
"manage_team",
|
||||||
"authentication.permissions.manage_team.name",
|
"authentication.permissions.manage_team.name",
|
||||||
@ -238,6 +275,11 @@ func InitalizePermissions() {
|
|||||||
"authentication.permissions.import_team.name",
|
"authentication.permissions.import_team.name",
|
||||||
"authentication.permissions.import_team.description",
|
"authentication.permissions.import_team.description",
|
||||||
}
|
}
|
||||||
|
PERMISSION_VIEW_TEAM = &Permission{
|
||||||
|
"view_team",
|
||||||
|
"authentication.permissions.view_team.name",
|
||||||
|
"authentication.permissions.view_team.description",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitalizeRoles() {
|
func InitalizeRoles() {
|
||||||
@ -264,7 +306,9 @@ func InitalizeRoles() {
|
|||||||
"channel_admin",
|
"channel_admin",
|
||||||
"authentication.roles.channel_admin.name",
|
"authentication.roles.channel_admin.name",
|
||||||
"authentication.roles.channel_admin.description",
|
"authentication.roles.channel_admin.description",
|
||||||
[]string{},
|
[]string{
|
||||||
|
PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN
|
BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN
|
||||||
ROLE_CHANNEL_GUEST = &Role{
|
ROLE_CHANNEL_GUEST = &Role{
|
||||||
@ -282,6 +326,7 @@ func InitalizeRoles() {
|
|||||||
[]string{
|
[]string{
|
||||||
PERMISSION_LIST_TEAM_CHANNELS.Id,
|
PERMISSION_LIST_TEAM_CHANNELS.Id,
|
||||||
PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
|
PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
|
||||||
|
PERMISSION_VIEW_TEAM.Id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER
|
BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER
|
||||||
@ -295,7 +340,8 @@ func InitalizeRoles() {
|
|||||||
PERMISSION_REMOVE_USER_FROM_TEAM.Id,
|
PERMISSION_REMOVE_USER_FROM_TEAM.Id,
|
||||||
PERMISSION_MANAGE_TEAM.Id,
|
PERMISSION_MANAGE_TEAM.Id,
|
||||||
PERMISSION_IMPORT_TEAM.Id,
|
PERMISSION_IMPORT_TEAM.Id,
|
||||||
PERMISSION_MANAGE_ROLES.Id,
|
PERMISSION_MANAGE_TEAM_ROLES.Id,
|
||||||
|
PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||||
PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
|
PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
|
||||||
PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
||||||
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
|
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
|
||||||
@ -310,6 +356,7 @@ func InitalizeRoles() {
|
|||||||
"authentication.roles.global_user.description",
|
"authentication.roles.global_user.description",
|
||||||
[]string{
|
[]string{
|
||||||
PERMISSION_CREATE_DIRECT_CHANNEL.Id,
|
PERMISSION_CREATE_DIRECT_CHANNEL.Id,
|
||||||
|
PERMISSION_CREATE_GROUP_CHANNEL.Id,
|
||||||
PERMISSION_PERMANENT_DELETE_USER.Id,
|
PERMISSION_PERMANENT_DELETE_USER.Id,
|
||||||
PERMISSION_MANAGE_OAUTH.Id,
|
PERMISSION_MANAGE_OAUTH.Id,
|
||||||
},
|
},
|
||||||
@ -329,6 +376,7 @@ func InitalizeRoles() {
|
|||||||
[]string{
|
[]string{
|
||||||
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
|
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
|
||||||
PERMISSION_MANAGE_SYSTEM.Id,
|
PERMISSION_MANAGE_SYSTEM.Id,
|
||||||
|
PERMISSION_MANAGE_ROLES.Id,
|
||||||
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
|
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
|
||||||
PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
|
PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
|
||||||
PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
|
PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
|
||||||
@ -340,6 +388,9 @@ func InitalizeRoles() {
|
|||||||
PERMISSION_EDIT_OTHER_USERS.Id,
|
PERMISSION_EDIT_OTHER_USERS.Id,
|
||||||
PERMISSION_MANAGE_OAUTH.Id,
|
PERMISSION_MANAGE_OAUTH.Id,
|
||||||
PERMISSION_INVITE_USER.Id,
|
PERMISSION_INVITE_USER.Id,
|
||||||
|
PERMISSION_DELETE_POST.Id,
|
||||||
|
PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||||
|
PERMISSION_CREATE_TEAM.Id,
|
||||||
},
|
},
|
||||||
ROLE_TEAM_USER.Permissions...,
|
ROLE_TEAM_USER.Permissions...,
|
||||||
),
|
),
|
||||||
|
47
vendor/github.com/mattermost/platform/model/channel.go
generated
vendored
47
vendor/github.com/mattermost/platform/model/channel.go
generated
vendored
@ -4,8 +4,12 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,11 +17,16 @@ const (
|
|||||||
CHANNEL_OPEN = "O"
|
CHANNEL_OPEN = "O"
|
||||||
CHANNEL_PRIVATE = "P"
|
CHANNEL_PRIVATE = "P"
|
||||||
CHANNEL_DIRECT = "D"
|
CHANNEL_DIRECT = "D"
|
||||||
|
CHANNEL_GROUP = "G"
|
||||||
|
CHANNEL_GROUP_MAX_USERS = 8
|
||||||
|
CHANNEL_GROUP_MIN_USERS = 3
|
||||||
DEFAULT_CHANNEL = "town-square"
|
DEFAULT_CHANNEL = "town-square"
|
||||||
CHANNEL_DISPLAY_NAME_MAX_RUNES = 64
|
CHANNEL_DISPLAY_NAME_MAX_RUNES = 64
|
||||||
|
CHANNEL_NAME_MIN_LENGTH = 2
|
||||||
CHANNEL_NAME_MAX_LENGTH = 64
|
CHANNEL_NAME_MAX_LENGTH = 64
|
||||||
CHANNEL_HEADER_MAX_RUNES = 1024
|
CHANNEL_HEADER_MAX_RUNES = 1024
|
||||||
CHANNEL_PURPOSE_MAX_RUNES = 250
|
CHANNEL_PURPOSE_MAX_RUNES = 250
|
||||||
|
CHANNEL_CACHE_SIZE = 25000
|
||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
@ -83,15 +92,11 @@ func (o *Channel) IsValid() *AppError {
|
|||||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id)
|
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Name) > CHANNEL_NAME_MAX_LENGTH {
|
|
||||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsValidChannelIdentifier(o.Name) {
|
if !IsValidChannelIdentifier(o.Name) {
|
||||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id)
|
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) {
|
if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP) {
|
||||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id)
|
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +133,10 @@ func (o *Channel) ExtraUpdated() {
|
|||||||
o.ExtraUpdateAt = GetMillis()
|
o.ExtraUpdateAt = GetMillis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Channel) IsGroupOrDirect() bool {
|
||||||
|
return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP
|
||||||
|
}
|
||||||
|
|
||||||
func GetDMNameFromIds(userId1, userId2 string) string {
|
func GetDMNameFromIds(userId1, userId2 string) string {
|
||||||
if userId1 > userId2 {
|
if userId1 > userId2 {
|
||||||
return userId2 + "__" + userId1
|
return userId2 + "__" + userId1
|
||||||
@ -135,3 +144,31 @@ func GetDMNameFromIds(userId1, userId2 string) string {
|
|||||||
return userId1 + "__" + userId2
|
return userId1 + "__" + userId2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGroupDisplayNameFromUsers(users []*User, truncate bool) string {
|
||||||
|
usernames := make([]string, len(users))
|
||||||
|
for index, user := range users {
|
||||||
|
usernames[index] = user.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(usernames)
|
||||||
|
|
||||||
|
name := strings.Join(usernames, ", ")
|
||||||
|
|
||||||
|
if truncate && len(name) > CHANNEL_NAME_MAX_LENGTH {
|
||||||
|
name = name[:CHANNEL_NAME_MAX_LENGTH]
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupNameFromUserIds(userIds []string) string {
|
||||||
|
sort.Strings(userIds)
|
||||||
|
|
||||||
|
h := sha1.New()
|
||||||
|
for _, id := range userIds {
|
||||||
|
io.WriteString(h, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
28
vendor/github.com/mattermost/platform/model/channel_member.go
generated
vendored
28
vendor/github.com/mattermost/platform/model/channel_member.go
generated
vendored
@ -88,18 +88,32 @@ func (o *ChannelMember) IsValid() *AppError {
|
|||||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "")
|
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyLevel := o.NotifyProps["desktop"]
|
notifyLevel := o.NotifyProps[DESKTOP_NOTIFY_PROP]
|
||||||
if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) {
|
if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) {
|
||||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error",
|
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error",
|
||||||
nil, "notify_level="+notifyLevel)
|
nil, "notify_level="+notifyLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
markUnreadLevel := o.NotifyProps["mark_unread"]
|
markUnreadLevel := o.NotifyProps[MARK_UNREAD_NOTIFY_PROP]
|
||||||
if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) {
|
if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) {
|
||||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error",
|
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error",
|
||||||
nil, "mark_unread_level="+markUnreadLevel)
|
nil, "mark_unread_level="+markUnreadLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pushLevel, ok := o.NotifyProps[PUSH_NOTIFY_PROP]; ok {
|
||||||
|
if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) {
|
||||||
|
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error",
|
||||||
|
nil, "push_notification_level="+pushLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sendEmail, ok := o.NotifyProps[EMAIL_NOTIFY_PROP]; ok {
|
||||||
|
if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) {
|
||||||
|
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error",
|
||||||
|
nil, "push_notification_level="+sendEmail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,9 +140,15 @@ func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool {
|
|||||||
return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION
|
return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsSendEmailValid(sendEmail string) bool {
|
||||||
|
return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false"
|
||||||
|
}
|
||||||
|
|
||||||
func GetDefaultChannelNotifyProps() StringMap {
|
func GetDefaultChannelNotifyProps() StringMap {
|
||||||
return StringMap{
|
return StringMap{
|
||||||
"desktop": CHANNEL_NOTIFY_DEFAULT,
|
DESKTOP_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||||
"mark_unread": CHANNEL_MARK_UNREAD_ALL,
|
MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL,
|
||||||
|
PUSH_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||||
|
EMAIL_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
76
vendor/github.com/mattermost/platform/model/client.go
generated
vendored
76
vendor/github.com/mattermost/platform/model/client.go
generated
vendored
@ -35,12 +35,14 @@ const (
|
|||||||
STATUS = "status"
|
STATUS = "status"
|
||||||
STATUS_OK = "OK"
|
STATUS_OK = "OK"
|
||||||
STATUS_FAIL = "FAIL"
|
STATUS_FAIL = "FAIL"
|
||||||
|
STATUS_REMOVE = "REMOVE"
|
||||||
|
|
||||||
CLIENT_DIR = "webapp/dist"
|
CLIENT_DIR = "webapp/dist"
|
||||||
|
|
||||||
API_URL_SUFFIX_V1 = "/api/v1"
|
API_URL_SUFFIX_V1 = "/api/v1"
|
||||||
API_URL_SUFFIX_V3 = "/api/v3"
|
API_URL_SUFFIX_V3 = "/api/v3"
|
||||||
API_URL_SUFFIX = API_URL_SUFFIX_V3
|
API_URL_SUFFIX_V4 = "/api/v4"
|
||||||
|
API_URL_SUFFIX = API_URL_SUFFIX_V4
|
||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@ -71,7 +73,7 @@ type Client struct {
|
|||||||
// NewClient constructs a new client with convienence methods for talking to
|
// NewClient constructs a new client with convienence methods for talking to
|
||||||
// the server.
|
// the server.
|
||||||
func NewClient(url string) *Client {
|
func NewClient(url string) *Client {
|
||||||
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", "", "", "", "", ""}
|
return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeBody(r *http.Response) {
|
func closeBody(r *http.Response) {
|
||||||
@ -782,7 +784,7 @@ func (c *Client) GetSessions(id string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) {
|
func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoApiPost("/users/claim/email_to_sso", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
defer closeBody(r)
|
defer closeBody(r)
|
||||||
@ -1119,6 +1121,16 @@ func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
||||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil {
|
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1471,6 +1483,21 @@ func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPermalink returns a post list, based on the provided channel and post ID.
|
||||||
|
func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) {
|
||||||
|
if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil {
|
||||||
|
return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return PostListFromJson(r.Body),
|
||||||
|
&ResponseMetadata{
|
||||||
|
StatusCode: r.StatusCode,
|
||||||
|
RequestId: r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
Etag: r.Header.Get(HEADER_ETAG_SERVER),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
||||||
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
|
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1991,6 +2018,16 @@ func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppErro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) {
|
func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil {
|
if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -2082,6 +2119,16 @@ func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppErro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) {
|
func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) {
|
||||||
data := make(map[string]string)
|
data := make(map[string]string)
|
||||||
data["id"] = id
|
data["id"] = id
|
||||||
@ -2319,3 +2366,26 @@ func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *A
|
|||||||
return ReactionsFromJson(r.Body), nil
|
return ReactionsFromJson(r.Body), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Updates the user's roles in the channel by replacing them with the roles provided.
|
||||||
|
func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) {
|
||||||
|
data := make(map[string]string)
|
||||||
|
data["new_roles"] = roles
|
||||||
|
data["user_id"] = userId
|
||||||
|
|
||||||
|
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil {
|
||||||
|
metadata := ResponseMetadata{Error: err}
|
||||||
|
if r != nil {
|
||||||
|
metadata.StatusCode = r.StatusCode
|
||||||
|
}
|
||||||
|
return nil, &metadata
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return MapFromJson(r.Body),
|
||||||
|
&ResponseMetadata{
|
||||||
|
StatusCode: r.StatusCode,
|
||||||
|
RequestId: r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
Etag: r.Header.Get(HEADER_ETAG_SERVER),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1006
vendor/github.com/mattermost/platform/model/client4.go
generated
vendored
Normal file
1006
vendor/github.com/mattermost/platform/model/client4.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
30
vendor/github.com/mattermost/platform/model/command_response.go
generated
vendored
30
vendor/github.com/mattermost/platform/model/command_response.go
generated
vendored
@ -5,6 +5,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,12 +15,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CommandResponse struct {
|
type CommandResponse struct {
|
||||||
ResponseType string `json:"response_type"`
|
ResponseType string `json:"response_type"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
IconURL string `json:"icon_url"`
|
IconURL string `json:"icon_url"`
|
||||||
GotoLocation string `json:"goto_location"`
|
GotoLocation string `json:"goto_location"`
|
||||||
Attachments interface{} `json:"attachments"`
|
Attachments []*SlackAttachment `json:"attachments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CommandResponse) ToJson() string {
|
func (o *CommandResponse) ToJson() string {
|
||||||
@ -34,10 +35,19 @@ func (o *CommandResponse) ToJson() string {
|
|||||||
func CommandResponseFromJson(data io.Reader) *CommandResponse {
|
func CommandResponseFromJson(data io.Reader) *CommandResponse {
|
||||||
decoder := json.NewDecoder(data)
|
decoder := json.NewDecoder(data)
|
||||||
var o CommandResponse
|
var o CommandResponse
|
||||||
err := decoder.Decode(&o)
|
|
||||||
if err == nil {
|
if err := decoder.Decode(&o); err != nil {
|
||||||
return &o
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure attachment fields are stored as strings
|
||||||
|
for _, attachment := range o.Attachments {
|
||||||
|
for _, field := range attachment.Fields {
|
||||||
|
if field.Value != nil {
|
||||||
|
field.Value = fmt.Sprintf("%v", field.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &o
|
||||||
}
|
}
|
||||||
|
245
vendor/github.com/mattermost/platform/model/config.go
generated
vendored
245
vendor/github.com/mattermost/platform/model/config.go
generated
vendored
@ -49,49 +49,109 @@ const (
|
|||||||
RESTRICT_EMOJI_CREATION_ADMIN = "admin"
|
RESTRICT_EMOJI_CREATION_ADMIN = "admin"
|
||||||
RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
|
RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
|
||||||
|
|
||||||
|
PERMISSIONS_DELETE_POST_ALL = "all"
|
||||||
|
PERMISSIONS_DELETE_POST_TEAM_ADMIN = "team_admin"
|
||||||
|
PERMISSIONS_DELETE_POST_SYSTEM_ADMIN = "system_admin"
|
||||||
|
|
||||||
|
ALLOW_EDIT_POST_ALWAYS = "always"
|
||||||
|
ALLOW_EDIT_POST_NEVER = "never"
|
||||||
|
ALLOW_EDIT_POST_TIME_LIMIT = "time_limit"
|
||||||
|
|
||||||
EMAIL_BATCHING_BUFFER_SIZE = 256
|
EMAIL_BATCHING_BUFFER_SIZE = 256
|
||||||
EMAIL_BATCHING_INTERVAL = 30
|
EMAIL_BATCHING_INTERVAL = 30
|
||||||
|
|
||||||
SITENAME_MAX_LENGTH = 30
|
SITENAME_MAX_LENGTH = 30
|
||||||
|
|
||||||
|
SERVICE_SETTINGS_DEFAULT_SITE_URL = ""
|
||||||
|
SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE = ""
|
||||||
|
SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE = ""
|
||||||
|
SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT = 300
|
||||||
|
SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT = 300
|
||||||
|
SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = ""
|
||||||
|
|
||||||
|
TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT = ""
|
||||||
|
TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT = ""
|
||||||
|
TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT = 300
|
||||||
|
|
||||||
|
EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = ""
|
||||||
|
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/"
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/"
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/"
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/"
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/"
|
||||||
|
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
|
||||||
|
|
||||||
|
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = ""
|
||||||
|
|
||||||
|
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE = ""
|
||||||
|
SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
||||||
|
|
||||||
|
NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK = "https://about.mattermost.com/downloads/"
|
||||||
|
NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-android-app/"
|
||||||
|
NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-ios-app/"
|
||||||
|
|
||||||
|
WEBRTC_SETTINGS_DEFAULT_STUN_URI = ""
|
||||||
|
WEBRTC_SETTINGS_DEFAULT_TURN_URI = ""
|
||||||
|
|
||||||
|
ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceSettings struct {
|
type ServiceSettings struct {
|
||||||
SiteURL *string
|
SiteURL *string
|
||||||
ListenAddress string
|
ListenAddress string
|
||||||
ConnectionSecurity *string
|
ConnectionSecurity *string
|
||||||
TLSCertFile *string
|
TLSCertFile *string
|
||||||
TLSKeyFile *string
|
TLSKeyFile *string
|
||||||
UseLetsEncrypt *bool
|
UseLetsEncrypt *bool
|
||||||
LetsEncryptCertificateCacheFile *string
|
LetsEncryptCertificateCacheFile *string
|
||||||
Forward80To443 *bool
|
Forward80To443 *bool
|
||||||
ReadTimeout *int
|
ReadTimeout *int
|
||||||
WriteTimeout *int
|
WriteTimeout *int
|
||||||
MaximumLoginAttempts int
|
MaximumLoginAttempts int
|
||||||
SegmentDeveloperKey string
|
GoogleDeveloperKey string
|
||||||
GoogleDeveloperKey string
|
EnableOAuthServiceProvider bool
|
||||||
EnableOAuthServiceProvider bool
|
EnableIncomingWebhooks bool
|
||||||
EnableIncomingWebhooks bool
|
EnableOutgoingWebhooks bool
|
||||||
EnableOutgoingWebhooks bool
|
EnableCommands *bool
|
||||||
EnableCommands *bool
|
EnableOnlyAdminIntegrations *bool
|
||||||
EnableOnlyAdminIntegrations *bool
|
EnablePostUsernameOverride bool
|
||||||
EnablePostUsernameOverride bool
|
EnablePostIconOverride bool
|
||||||
EnablePostIconOverride bool
|
EnableLinkPreviews *bool
|
||||||
EnableTesting bool
|
EnableTesting bool
|
||||||
EnableDeveloper *bool
|
EnableDeveloper *bool
|
||||||
EnableSecurityFixAlert *bool
|
EnableSecurityFixAlert *bool
|
||||||
EnableInsecureOutgoingConnections *bool
|
EnableInsecureOutgoingConnections *bool
|
||||||
EnableMultifactorAuthentication *bool
|
EnableMultifactorAuthentication *bool
|
||||||
EnforceMultifactorAuthentication *bool
|
EnforceMultifactorAuthentication *bool
|
||||||
AllowCorsFrom *string
|
AllowCorsFrom *string
|
||||||
SessionLengthWebInDays *int
|
SessionLengthWebInDays *int
|
||||||
SessionLengthMobileInDays *int
|
SessionLengthMobileInDays *int
|
||||||
SessionLengthSSOInDays *int
|
SessionLengthSSOInDays *int
|
||||||
SessionCacheInMinutes *int
|
SessionCacheInMinutes *int
|
||||||
WebsocketSecurePort *int
|
WebsocketSecurePort *int
|
||||||
WebsocketPort *int
|
WebsocketPort *int
|
||||||
WebserverMode *string
|
WebserverMode *string
|
||||||
EnableCustomEmoji *bool
|
EnableCustomEmoji *bool
|
||||||
RestrictCustomEmojiCreation *string
|
RestrictCustomEmojiCreation *string
|
||||||
|
RestrictPostDelete *string
|
||||||
|
AllowEditPost *string
|
||||||
|
PostEditTimeLimit *int
|
||||||
|
TimeBetweenUserTypingUpdatesMilliseconds *int64
|
||||||
|
EnableUserTypingMessages *bool
|
||||||
|
ClusterLogTimeoutMilliseconds *int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterSettings struct {
|
type ClusterSettings struct {
|
||||||
@ -433,7 +493,12 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.ServiceSettings.SiteURL == nil {
|
if o.ServiceSettings.SiteURL == nil {
|
||||||
o.ServiceSettings.SiteURL = new(string)
|
o.ServiceSettings.SiteURL = new(string)
|
||||||
*o.ServiceSettings.SiteURL = ""
|
*o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.EnableLinkPreviews == nil {
|
||||||
|
o.ServiceSettings.EnableLinkPreviews = new(bool)
|
||||||
|
*o.ServiceSettings.EnableLinkPreviews = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.EnableDeveloper == nil {
|
if o.ServiceSettings.EnableDeveloper == nil {
|
||||||
@ -493,12 +558,12 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.TeamSettings.CustomBrandText == nil {
|
if o.TeamSettings.CustomBrandText == nil {
|
||||||
o.TeamSettings.CustomBrandText = new(string)
|
o.TeamSettings.CustomBrandText = new(string)
|
||||||
*o.TeamSettings.CustomBrandText = ""
|
*o.TeamSettings.CustomBrandText = TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.TeamSettings.CustomDescriptionText == nil {
|
if o.TeamSettings.CustomDescriptionText == nil {
|
||||||
o.TeamSettings.CustomDescriptionText = new(string)
|
o.TeamSettings.CustomDescriptionText = new(string)
|
||||||
*o.TeamSettings.CustomDescriptionText = ""
|
*o.TeamSettings.CustomDescriptionText = TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.TeamSettings.EnableOpenServer == nil {
|
if o.TeamSettings.EnableOpenServer == nil {
|
||||||
@ -552,7 +617,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.TeamSettings.UserStatusAwayTimeout == nil {
|
if o.TeamSettings.UserStatusAwayTimeout == nil {
|
||||||
o.TeamSettings.UserStatusAwayTimeout = new(int64)
|
o.TeamSettings.UserStatusAwayTimeout = new(int64)
|
||||||
*o.TeamSettings.UserStatusAwayTimeout = 300
|
*o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.TeamSettings.MaxChannelsPerTeam == nil {
|
if o.TeamSettings.MaxChannelsPerTeam == nil {
|
||||||
@ -597,7 +662,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.EmailSettings.FeedbackOrganization == nil {
|
if o.EmailSettings.FeedbackOrganization == nil {
|
||||||
o.EmailSettings.FeedbackOrganization = new(string)
|
o.EmailSettings.FeedbackOrganization = new(string)
|
||||||
*o.EmailSettings.FeedbackOrganization = ""
|
*o.EmailSettings.FeedbackOrganization = EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.EmailSettings.EnableEmailBatching == nil {
|
if o.EmailSettings.EnableEmailBatching == nil {
|
||||||
@ -621,7 +686,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SupportSettings.TermsOfServiceLink == nil {
|
if o.SupportSettings.TermsOfServiceLink == nil {
|
||||||
o.SupportSettings.TermsOfServiceLink = new(string)
|
o.SupportSettings.TermsOfServiceLink = new(string)
|
||||||
*o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/"
|
*o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
|
if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
|
||||||
@ -630,7 +695,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SupportSettings.PrivacyPolicyLink == nil {
|
if o.SupportSettings.PrivacyPolicyLink == nil {
|
||||||
o.SupportSettings.PrivacyPolicyLink = new(string)
|
o.SupportSettings.PrivacyPolicyLink = new(string)
|
||||||
*o.SupportSettings.PrivacyPolicyLink = ""
|
*o.SupportSettings.PrivacyPolicyLink = SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsSafeLink(o.SupportSettings.AboutLink) {
|
if !IsSafeLink(o.SupportSettings.AboutLink) {
|
||||||
@ -639,7 +704,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SupportSettings.AboutLink == nil {
|
if o.SupportSettings.AboutLink == nil {
|
||||||
o.SupportSettings.AboutLink = new(string)
|
o.SupportSettings.AboutLink = new(string)
|
||||||
*o.SupportSettings.AboutLink = ""
|
*o.SupportSettings.AboutLink = SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsSafeLink(o.SupportSettings.HelpLink) {
|
if !IsSafeLink(o.SupportSettings.HelpLink) {
|
||||||
@ -648,7 +713,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SupportSettings.HelpLink == nil {
|
if o.SupportSettings.HelpLink == nil {
|
||||||
o.SupportSettings.HelpLink = new(string)
|
o.SupportSettings.HelpLink = new(string)
|
||||||
*o.SupportSettings.HelpLink = ""
|
*o.SupportSettings.HelpLink = SUPPORT_SETTINGS_DEFAULT_HELP_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
|
if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
|
||||||
@ -657,12 +722,12 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SupportSettings.ReportAProblemLink == nil {
|
if o.SupportSettings.ReportAProblemLink == nil {
|
||||||
o.SupportSettings.ReportAProblemLink = new(string)
|
o.SupportSettings.ReportAProblemLink = new(string)
|
||||||
*o.SupportSettings.ReportAProblemLink = ""
|
*o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SupportSettings.SupportEmail == nil {
|
if o.SupportSettings.SupportEmail == nil {
|
||||||
o.SupportSettings.SupportEmail = new(string)
|
o.SupportSettings.SupportEmail = new(string)
|
||||||
*o.SupportSettings.SupportEmail = "feedback@mattermost.com"
|
*o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.Enable == nil {
|
if o.LdapSettings.Enable == nil {
|
||||||
@ -707,37 +772,37 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.LdapSettings.FirstNameAttribute == nil {
|
if o.LdapSettings.FirstNameAttribute == nil {
|
||||||
o.LdapSettings.FirstNameAttribute = new(string)
|
o.LdapSettings.FirstNameAttribute = new(string)
|
||||||
*o.LdapSettings.FirstNameAttribute = ""
|
*o.LdapSettings.FirstNameAttribute = LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.LastNameAttribute == nil {
|
if o.LdapSettings.LastNameAttribute == nil {
|
||||||
o.LdapSettings.LastNameAttribute = new(string)
|
o.LdapSettings.LastNameAttribute = new(string)
|
||||||
*o.LdapSettings.LastNameAttribute = ""
|
*o.LdapSettings.LastNameAttribute = LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.EmailAttribute == nil {
|
if o.LdapSettings.EmailAttribute == nil {
|
||||||
o.LdapSettings.EmailAttribute = new(string)
|
o.LdapSettings.EmailAttribute = new(string)
|
||||||
*o.LdapSettings.EmailAttribute = ""
|
*o.LdapSettings.EmailAttribute = LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.UsernameAttribute == nil {
|
if o.LdapSettings.UsernameAttribute == nil {
|
||||||
o.LdapSettings.UsernameAttribute = new(string)
|
o.LdapSettings.UsernameAttribute = new(string)
|
||||||
*o.LdapSettings.UsernameAttribute = ""
|
*o.LdapSettings.UsernameAttribute = LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.NicknameAttribute == nil {
|
if o.LdapSettings.NicknameAttribute == nil {
|
||||||
o.LdapSettings.NicknameAttribute = new(string)
|
o.LdapSettings.NicknameAttribute = new(string)
|
||||||
*o.LdapSettings.NicknameAttribute = ""
|
*o.LdapSettings.NicknameAttribute = LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.IdAttribute == nil {
|
if o.LdapSettings.IdAttribute == nil {
|
||||||
o.LdapSettings.IdAttribute = new(string)
|
o.LdapSettings.IdAttribute = new(string)
|
||||||
*o.LdapSettings.IdAttribute = ""
|
*o.LdapSettings.IdAttribute = LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.PositionAttribute == nil {
|
if o.LdapSettings.PositionAttribute == nil {
|
||||||
o.LdapSettings.PositionAttribute = new(string)
|
o.LdapSettings.PositionAttribute = new(string)
|
||||||
*o.LdapSettings.PositionAttribute = ""
|
*o.LdapSettings.PositionAttribute = LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LdapSettings.SyncIntervalMinutes == nil {
|
if o.LdapSettings.SyncIntervalMinutes == nil {
|
||||||
@ -762,7 +827,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.LdapSettings.LoginFieldName == nil {
|
if o.LdapSettings.LoginFieldName == nil {
|
||||||
o.LdapSettings.LoginFieldName = new(string)
|
o.LdapSettings.LoginFieldName = new(string)
|
||||||
*o.LdapSettings.LoginFieldName = ""
|
*o.LdapSettings.LoginFieldName = LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.SessionLengthWebInDays == nil {
|
if o.ServiceSettings.SessionLengthWebInDays == nil {
|
||||||
@ -807,7 +872,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.ServiceSettings.AllowCorsFrom == nil {
|
if o.ServiceSettings.AllowCorsFrom == nil {
|
||||||
o.ServiceSettings.AllowCorsFrom = new(string)
|
o.ServiceSettings.AllowCorsFrom = new(string)
|
||||||
*o.ServiceSettings.AllowCorsFrom = ""
|
*o.ServiceSettings.AllowCorsFrom = SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.WebserverMode == nil {
|
if o.ServiceSettings.WebserverMode == nil {
|
||||||
@ -827,6 +892,21 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL
|
*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.RestrictPostDelete == nil {
|
||||||
|
o.ServiceSettings.RestrictPostDelete = new(string)
|
||||||
|
*o.ServiceSettings.RestrictPostDelete = PERMISSIONS_DELETE_POST_ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.AllowEditPost == nil {
|
||||||
|
o.ServiceSettings.AllowEditPost = new(string)
|
||||||
|
*o.ServiceSettings.AllowEditPost = ALLOW_EDIT_POST_ALWAYS
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.PostEditTimeLimit == nil {
|
||||||
|
o.ServiceSettings.PostEditTimeLimit = new(int)
|
||||||
|
*o.ServiceSettings.PostEditTimeLimit = 300
|
||||||
|
}
|
||||||
|
|
||||||
if o.ClusterSettings.InterNodeListenAddress == nil {
|
if o.ClusterSettings.InterNodeListenAddress == nil {
|
||||||
o.ClusterSettings.InterNodeListenAddress = new(string)
|
o.ClusterSettings.InterNodeListenAddress = new(string)
|
||||||
*o.ClusterSettings.InterNodeListenAddress = ":8075"
|
*o.ClusterSettings.InterNodeListenAddress = ":8075"
|
||||||
@ -853,7 +933,7 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.AnalyticsSettings.MaxUsersForStatistics == nil {
|
if o.AnalyticsSettings.MaxUsersForStatistics == nil {
|
||||||
o.AnalyticsSettings.MaxUsersForStatistics = new(int)
|
o.AnalyticsSettings.MaxUsersForStatistics = new(int)
|
||||||
*o.AnalyticsSettings.MaxUsersForStatistics = 2500
|
*o.AnalyticsSettings.MaxUsersForStatistics = ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ComplianceSettings.Enable == nil {
|
if o.ComplianceSettings.Enable == nil {
|
||||||
@ -943,52 +1023,52 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.SamlSettings.FirstNameAttribute == nil {
|
if o.SamlSettings.FirstNameAttribute == nil {
|
||||||
o.SamlSettings.FirstNameAttribute = new(string)
|
o.SamlSettings.FirstNameAttribute = new(string)
|
||||||
*o.SamlSettings.FirstNameAttribute = ""
|
*o.SamlSettings.FirstNameAttribute = SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.LastNameAttribute == nil {
|
if o.SamlSettings.LastNameAttribute == nil {
|
||||||
o.SamlSettings.LastNameAttribute = new(string)
|
o.SamlSettings.LastNameAttribute = new(string)
|
||||||
*o.SamlSettings.LastNameAttribute = ""
|
*o.SamlSettings.LastNameAttribute = SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.EmailAttribute == nil {
|
if o.SamlSettings.EmailAttribute == nil {
|
||||||
o.SamlSettings.EmailAttribute = new(string)
|
o.SamlSettings.EmailAttribute = new(string)
|
||||||
*o.SamlSettings.EmailAttribute = ""
|
*o.SamlSettings.EmailAttribute = SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.UsernameAttribute == nil {
|
if o.SamlSettings.UsernameAttribute == nil {
|
||||||
o.SamlSettings.UsernameAttribute = new(string)
|
o.SamlSettings.UsernameAttribute = new(string)
|
||||||
*o.SamlSettings.UsernameAttribute = ""
|
*o.SamlSettings.UsernameAttribute = SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.NicknameAttribute == nil {
|
if o.SamlSettings.NicknameAttribute == nil {
|
||||||
o.SamlSettings.NicknameAttribute = new(string)
|
o.SamlSettings.NicknameAttribute = new(string)
|
||||||
*o.SamlSettings.NicknameAttribute = ""
|
*o.SamlSettings.NicknameAttribute = SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.PositionAttribute == nil {
|
if o.SamlSettings.PositionAttribute == nil {
|
||||||
o.SamlSettings.PositionAttribute = new(string)
|
o.SamlSettings.PositionAttribute = new(string)
|
||||||
*o.SamlSettings.PositionAttribute = ""
|
*o.SamlSettings.PositionAttribute = SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SamlSettings.LocaleAttribute == nil {
|
if o.SamlSettings.LocaleAttribute == nil {
|
||||||
o.SamlSettings.LocaleAttribute = new(string)
|
o.SamlSettings.LocaleAttribute = new(string)
|
||||||
*o.SamlSettings.LocaleAttribute = ""
|
*o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.NativeAppSettings.AppDownloadLink == nil {
|
if o.NativeAppSettings.AppDownloadLink == nil {
|
||||||
o.NativeAppSettings.AppDownloadLink = new(string)
|
o.NativeAppSettings.AppDownloadLink = new(string)
|
||||||
*o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/"
|
*o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.NativeAppSettings.AndroidAppDownloadLink == nil {
|
if o.NativeAppSettings.AndroidAppDownloadLink == nil {
|
||||||
o.NativeAppSettings.AndroidAppDownloadLink = new(string)
|
o.NativeAppSettings.AndroidAppDownloadLink = new(string)
|
||||||
*o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/"
|
*o.NativeAppSettings.AndroidAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.NativeAppSettings.IosAppDownloadLink == nil {
|
if o.NativeAppSettings.IosAppDownloadLink == nil {
|
||||||
o.NativeAppSettings.IosAppDownloadLink = new(string)
|
o.NativeAppSettings.IosAppDownloadLink = new(string)
|
||||||
*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
|
*o.NativeAppSettings.IosAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.RateLimitSettings.Enable == nil {
|
if o.RateLimitSettings.Enable == nil {
|
||||||
@ -1008,12 +1088,12 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.ServiceSettings.TLSKeyFile == nil {
|
if o.ServiceSettings.TLSKeyFile == nil {
|
||||||
o.ServiceSettings.TLSKeyFile = new(string)
|
o.ServiceSettings.TLSKeyFile = new(string)
|
||||||
*o.ServiceSettings.TLSKeyFile = ""
|
*o.ServiceSettings.TLSKeyFile = SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.TLSCertFile == nil {
|
if o.ServiceSettings.TLSCertFile == nil {
|
||||||
o.ServiceSettings.TLSCertFile = new(string)
|
o.ServiceSettings.TLSCertFile = new(string)
|
||||||
*o.ServiceSettings.TLSCertFile = ""
|
*o.ServiceSettings.TLSCertFile = SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.UseLetsEncrypt == nil {
|
if o.ServiceSettings.UseLetsEncrypt == nil {
|
||||||
@ -1028,12 +1108,12 @@ func (o *Config) SetDefaults() {
|
|||||||
|
|
||||||
if o.ServiceSettings.ReadTimeout == nil {
|
if o.ServiceSettings.ReadTimeout == nil {
|
||||||
o.ServiceSettings.ReadTimeout = new(int)
|
o.ServiceSettings.ReadTimeout = new(int)
|
||||||
*o.ServiceSettings.ReadTimeout = 300
|
*o.ServiceSettings.ReadTimeout = SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.WriteTimeout == nil {
|
if o.ServiceSettings.WriteTimeout == nil {
|
||||||
o.ServiceSettings.WriteTimeout = new(int)
|
o.ServiceSettings.WriteTimeout = new(int)
|
||||||
*o.ServiceSettings.WriteTimeout = 300
|
*o.ServiceSettings.WriteTimeout = SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ServiceSettings.Forward80To443 == nil {
|
if o.ServiceSettings.Forward80To443 == nil {
|
||||||
@ -1046,6 +1126,21 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.MetricsSettings.BlockProfileRate = 0
|
*o.MetricsSettings.BlockProfileRate = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds == nil {
|
||||||
|
o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = new(int64)
|
||||||
|
*o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.EnableUserTypingMessages == nil {
|
||||||
|
o.ServiceSettings.EnableUserTypingMessages = new(bool)
|
||||||
|
*o.ServiceSettings.EnableUserTypingMessages = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil {
|
||||||
|
o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int)
|
||||||
|
*o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000
|
||||||
|
}
|
||||||
|
|
||||||
o.defaultWebrtcSettings()
|
o.defaultWebrtcSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,6 +1372,10 @@ func (o *Config) IsValid() *AppError {
|
|||||||
return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "")
|
return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds < 1000 {
|
||||||
|
return NewLocAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1339,12 +1438,12 @@ func (o *Config) defaultWebrtcSettings() {
|
|||||||
|
|
||||||
if o.WebrtcSettings.StunURI == nil {
|
if o.WebrtcSettings.StunURI == nil {
|
||||||
o.WebrtcSettings.StunURI = new(string)
|
o.WebrtcSettings.StunURI = new(string)
|
||||||
*o.WebrtcSettings.StunURI = ""
|
*o.WebrtcSettings.StunURI = WEBRTC_SETTINGS_DEFAULT_STUN_URI
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.WebrtcSettings.TurnURI == nil {
|
if o.WebrtcSettings.TurnURI == nil {
|
||||||
o.WebrtcSettings.TurnURI = new(string)
|
o.WebrtcSettings.TurnURI = new(string)
|
||||||
*o.WebrtcSettings.TurnURI = ""
|
*o.WebrtcSettings.TurnURI = WEBRTC_SETTINGS_DEFAULT_TURN_URI
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.WebrtcSettings.TurnUsername == nil {
|
if o.WebrtcSettings.TurnUsername == nil {
|
||||||
|
4
vendor/github.com/mattermost/platform/model/file.go
generated
vendored
4
vendor/github.com/mattermost/platform/model/file.go
generated
vendored
@ -8,6 +8,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"}
|
IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"}
|
||||||
IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"}
|
IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"}
|
||||||
|
9
vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
generated
vendored
9
vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
generated
vendored
@ -65,6 +65,15 @@ func gitLabUserFromJson(data io.Reader) *GitLabUser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (glu *GitLabUser) ToJson() string {
|
||||||
|
b, err := json.Marshal(glu)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (glu *GitLabUser) IsValid() bool {
|
func (glu *GitLabUser) IsValid() bool {
|
||||||
if glu.Id == 0 {
|
if glu.Id == 0 {
|
||||||
return false
|
return false
|
||||||
|
46
vendor/github.com/mattermost/platform/model/incoming_webhook.go
generated
vendored
46
vendor/github.com/mattermost/platform/model/incoming_webhook.go
generated
vendored
@ -29,13 +29,13 @@ type IncomingWebhook struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IncomingWebhookRequest struct {
|
type IncomingWebhookRequest struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
IconURL string `json:"icon_url"`
|
IconURL string `json:"icon_url"`
|
||||||
ChannelName string `json:"channel"`
|
ChannelName string `json:"channel"`
|
||||||
Props StringInterface `json:"props"`
|
Props StringInterface `json:"props"`
|
||||||
Attachments interface{} `json:"attachments"`
|
Attachments []*SlackAttachment `json:"attachments"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *IncomingWebhook) ToJson() string {
|
func (o *IncomingWebhook) ToJson() string {
|
||||||
@ -212,31 +212,15 @@ func expandAnnouncement(text string) string {
|
|||||||
func expandAnnouncements(i *IncomingWebhookRequest) {
|
func expandAnnouncements(i *IncomingWebhookRequest) {
|
||||||
i.Text = expandAnnouncement(i.Text)
|
i.Text = expandAnnouncement(i.Text)
|
||||||
|
|
||||||
if i.Attachments != nil {
|
for _, attachment := range i.Attachments {
|
||||||
attachments := i.Attachments.([]interface{})
|
attachment.Pretext = expandAnnouncement(attachment.Pretext)
|
||||||
for _, attachment := range attachments {
|
attachment.Text = expandAnnouncement(attachment.Text)
|
||||||
a := attachment.(map[string]interface{})
|
attachment.Title = expandAnnouncement(attachment.Title)
|
||||||
|
|
||||||
if a["pretext"] != nil {
|
for _, field := range attachment.Fields {
|
||||||
a["pretext"] = expandAnnouncement(a["pretext"].(string))
|
if field.Value != nil {
|
||||||
}
|
// Ensure the value is set to a string if it is set
|
||||||
|
field.Value = expandAnnouncement(fmt.Sprintf("%v", field.Value))
|
||||||
if a["text"] != nil {
|
|
||||||
a["text"] = expandAnnouncement(a["text"].(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
if a["title"] != nil {
|
|
||||||
a["title"] = expandAnnouncement(a["title"].(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
if a["fields"] != nil {
|
|
||||||
fields := a["fields"].([]interface{})
|
|
||||||
for _, field := range fields {
|
|
||||||
f := field.(map[string]interface{})
|
|
||||||
if f["value"] != nil {
|
|
||||||
f["value"] = expandAnnouncement(fmt.Sprintf("%v", f["value"]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
vendor/github.com/mattermost/platform/model/job.go
generated
vendored
4
vendor/github.com/mattermost/platform/model/job.go
generated
vendored
@ -14,8 +14,8 @@ type ScheduledTask struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Interval time.Duration `json:"interval"`
|
Interval time.Duration `json:"interval"`
|
||||||
Recurring bool `json:"recurring"`
|
Recurring bool `json:"recurring"`
|
||||||
function TaskFunc `json:",omitempty"`
|
function TaskFunc
|
||||||
timer *time.Timer `json:",omitempty"`
|
timer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
var tasks = make(map[string]*ScheduledTask)
|
var tasks = make(map[string]*ScheduledTask)
|
||||||
|
5
vendor/github.com/mattermost/platform/model/license.go
generated
vendored
5
vendor/github.com/mattermost/platform/model/license.go
generated
vendored
@ -8,6 +8,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error"
|
||||||
|
INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error"
|
||||||
|
)
|
||||||
|
|
||||||
type LicenseRecord struct {
|
type LicenseRecord struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
CreateAt int64 `json:"create_at"`
|
CreateAt int64 `json:"create_at"`
|
||||||
|
14
vendor/github.com/mattermost/platform/model/post.go
generated
vendored
14
vendor/github.com/mattermost/platform/model/post.go
generated
vendored
@ -14,10 +14,15 @@ const (
|
|||||||
POST_DEFAULT = ""
|
POST_DEFAULT = ""
|
||||||
POST_SLACK_ATTACHMENT = "slack_attachment"
|
POST_SLACK_ATTACHMENT = "slack_attachment"
|
||||||
POST_SYSTEM_GENERIC = "system_generic"
|
POST_SYSTEM_GENERIC = "system_generic"
|
||||||
POST_JOIN_LEAVE = "system_join_leave"
|
POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead
|
||||||
POST_ADD_REMOVE = "system_add_remove"
|
POST_JOIN_CHANNEL = "system_join_channel"
|
||||||
|
POST_LEAVE_CHANNEL = "system_leave_channel"
|
||||||
|
POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead
|
||||||
|
POST_ADD_TO_CHANNEL = "system_add_to_channel"
|
||||||
|
POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel"
|
||||||
POST_HEADER_CHANGE = "system_header_change"
|
POST_HEADER_CHANGE = "system_header_change"
|
||||||
POST_DISPLAYNAME_CHANGE = "system_displayname_change"
|
POST_DISPLAYNAME_CHANGE = "system_displayname_change"
|
||||||
|
POST_PURPOSE_CHANGE = "system_purpose_change"
|
||||||
POST_CHANNEL_DELETED = "system_channel_deleted"
|
POST_CHANNEL_DELETED = "system_channel_deleted"
|
||||||
POST_EPHEMERAL = "system_ephemeral"
|
POST_EPHEMERAL = "system_ephemeral"
|
||||||
POST_FILEIDS_MAX_RUNES = 150
|
POST_FILEIDS_MAX_RUNES = 150
|
||||||
@ -31,6 +36,7 @@ type Post struct {
|
|||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
CreateAt int64 `json:"create_at"`
|
CreateAt int64 `json:"create_at"`
|
||||||
UpdateAt int64 `json:"update_at"`
|
UpdateAt int64 `json:"update_at"`
|
||||||
|
EditAt int64 `json:"edit_at"`
|
||||||
DeleteAt int64 `json:"delete_at"`
|
DeleteAt int64 `json:"delete_at"`
|
||||||
UserId string `json:"user_id"`
|
UserId string `json:"user_id"`
|
||||||
ChannelId string `json:"channel_id"`
|
ChannelId string `json:"channel_id"`
|
||||||
@ -119,7 +125,9 @@ func (o *Post) IsValid() *AppError {
|
|||||||
|
|
||||||
// should be removed once more message types are supported
|
// should be removed once more message types are supported
|
||||||
if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE ||
|
if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE ||
|
||||||
o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE ||
|
o.Type == POST_JOIN_CHANNEL || o.Type == POST_LEAVE_CHANNEL ||
|
||||||
|
o.Type == POST_REMOVE_FROM_CHANNEL || o.Type == POST_ADD_TO_CHANNEL ||
|
||||||
|
o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || o.Type == POST_PURPOSE_CHANGE ||
|
||||||
o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) {
|
o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) {
|
||||||
return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type)
|
return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type)
|
||||||
}
|
}
|
||||||
|
17
vendor/github.com/mattermost/platform/model/post_list.go
generated
vendored
17
vendor/github.com/mattermost/platform/model/post_list.go
generated
vendored
@ -13,6 +13,13 @@ type PostList struct {
|
|||||||
Posts map[string]*Post `json:"posts"`
|
Posts map[string]*Post `json:"posts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPostList() *PostList {
|
||||||
|
return &PostList{
|
||||||
|
Order: make([]string, 0),
|
||||||
|
Posts: make(map[string]*Post),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *PostList) ToJson() string {
|
func (o *PostList) ToJson() string {
|
||||||
b, err := json.Marshal(o)
|
b, err := json.Marshal(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -72,10 +79,18 @@ func (o *PostList) Etag() string {
|
|||||||
if v.UpdateAt > t {
|
if v.UpdateAt > t {
|
||||||
t = v.UpdateAt
|
t = v.UpdateAt
|
||||||
id = v.Id
|
id = v.Id
|
||||||
|
} else if v.UpdateAt == t && v.Id > id {
|
||||||
|
t = v.UpdateAt
|
||||||
|
id = v.Id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Etag(id, t)
|
orderId := ""
|
||||||
|
if len(o.Order) > 0 {
|
||||||
|
orderId = o.Order[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return Etag(orderId, id, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *PostList) IsChannelId(channelId string) bool {
|
func (o *PostList) IsChannelId(channelId string) bool {
|
||||||
|
18
vendor/github.com/mattermost/platform/model/push_notification.go
generated
vendored
18
vendor/github.com/mattermost/platform/model/push_notification.go
generated
vendored
@ -10,8 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PUSH_NOTIFY_APPLE = "apple"
|
PUSH_NOTIFY_APPLE = "apple"
|
||||||
PUSH_NOTIFY_ANDROID = "android"
|
PUSH_NOTIFY_ANDROID = "android"
|
||||||
|
PUSH_NOTIFY_APPLE_REACT_NATIVE = "apple_rn"
|
||||||
|
PUSH_NOTIFY_ANDROID_REACT_NATIVE = "android_rn"
|
||||||
|
|
||||||
PUSH_TYPE_MESSAGE = "message"
|
PUSH_TYPE_MESSAGE = "message"
|
||||||
PUSH_TYPE_CLEAR = "clear"
|
PUSH_TYPE_CLEAR = "clear"
|
||||||
@ -46,12 +48,12 @@ func (me *PushNotification) ToJson() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
|
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
|
||||||
if strings.HasPrefix(deviceId, PUSH_NOTIFY_APPLE+":") {
|
|
||||||
me.Platform = PUSH_NOTIFY_APPLE
|
index := strings.Index(deviceId, ":")
|
||||||
me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_APPLE+":")
|
|
||||||
} else if strings.HasPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") {
|
if index > -1 {
|
||||||
me.Platform = PUSH_NOTIFY_ANDROID
|
me.Platform = deviceId[:index]
|
||||||
me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_ANDROID+":")
|
me.DeviceId = deviceId[index+1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
57
vendor/github.com/mattermost/platform/model/push_response.go
generated
vendored
Normal file
57
vendor/github.com/mattermost/platform/model/push_response.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PUSH_STATUS = "status"
|
||||||
|
PUSH_STATUS_OK = "OK"
|
||||||
|
PUSH_STATUS_FAIL = "FAIL"
|
||||||
|
PUSH_STATUS_REMOVE = "REMOVE"
|
||||||
|
PUSH_STATUS_ERROR_MSG = "error"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PushResponse map[string]string
|
||||||
|
|
||||||
|
func NewOkPushResponse() PushResponse {
|
||||||
|
m := make(map[string]string)
|
||||||
|
m[PUSH_STATUS] = PUSH_STATUS_OK
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRemovePushResponse() PushResponse {
|
||||||
|
m := make(map[string]string)
|
||||||
|
m[PUSH_STATUS] = PUSH_STATUS_REMOVE
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorPushResponse(message string) PushResponse {
|
||||||
|
m := make(map[string]string)
|
||||||
|
m[PUSH_STATUS] = PUSH_STATUS_FAIL
|
||||||
|
m[PUSH_STATUS_ERROR_MSG] = message
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *PushResponse) ToJson() string {
|
||||||
|
if b, err := json.Marshal(me); err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PushResponseFromJson(data io.Reader) PushResponse {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
|
||||||
|
var objmap PushResponse
|
||||||
|
if err := decoder.Decode(&objmap); err != nil {
|
||||||
|
return make(map[string]string)
|
||||||
|
} else {
|
||||||
|
return objmap
|
||||||
|
}
|
||||||
|
}
|
5
vendor/github.com/mattermost/platform/model/session.go
generated
vendored
5
vendor/github.com/mattermost/platform/model/session.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
|
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
|
||||||
SESSION_CACHE_SIZE = 25000
|
SESSION_CACHE_SIZE = 35000
|
||||||
SESSION_PROP_PLATFORM = "platform"
|
SESSION_PROP_PLATFORM = "platform"
|
||||||
SESSION_PROP_OS = "os"
|
SESSION_PROP_OS = "os"
|
||||||
SESSION_PROP_BROWSER = "browser"
|
SESSION_PROP_BROWSER = "browser"
|
||||||
@ -111,8 +111,7 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (me *Session) IsMobileApp() bool {
|
func (me *Session) IsMobileApp() bool {
|
||||||
return len(me.DeviceId) > 0 &&
|
return len(me.DeviceId) > 0
|
||||||
(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *Session) GetUserRoles() []string {
|
func (me *Session) GetUserRoles() []string {
|
||||||
|
29
vendor/github.com/mattermost/platform/model/slack_attachment.go
generated
vendored
Normal file
29
vendor/github.com/mattermost/platform/model/slack_attachment.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
type SlackAttachment struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Fallback string `json:"fallback"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
Pretext string `json:"pretext"`
|
||||||
|
AuthorName string `json:"author_name"`
|
||||||
|
AuthorLink string `json:"author_link"`
|
||||||
|
AuthorIcon string `json:"author_icon"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
TitleLink string `json:"title_link"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Fields []*SlackAttachmentField `json:"fields"`
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
|
ThumbURL string `json:"thumb_url"`
|
||||||
|
Footer string `json:"footer"`
|
||||||
|
FooterIcon string `json:"footer_icon"`
|
||||||
|
Timestamp interface{} `json:"ts"` // This is either a string or an int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SlackAttachmentField struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
Short bool `json:"short"`
|
||||||
|
}
|
2
vendor/github.com/mattermost/platform/model/status.go
generated
vendored
2
vendor/github.com/mattermost/platform/model/status.go
generated
vendored
@ -12,7 +12,7 @@ const (
|
|||||||
STATUS_OFFLINE = "offline"
|
STATUS_OFFLINE = "offline"
|
||||||
STATUS_AWAY = "away"
|
STATUS_AWAY = "away"
|
||||||
STATUS_ONLINE = "online"
|
STATUS_ONLINE = "online"
|
||||||
STATUS_CACHE_SIZE = 25000
|
STATUS_CACHE_SIZE = SESSION_CACHE_SIZE
|
||||||
STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds
|
STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds
|
||||||
STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes
|
STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes
|
||||||
)
|
)
|
||||||
|
80
vendor/github.com/mattermost/platform/model/team.go
generated
vendored
80
vendor/github.com/mattermost/platform/model/team.go
generated
vendored
@ -7,14 +7,22 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TEAM_OPEN = "O"
|
TEAM_OPEN = "O"
|
||||||
TEAM_INVITE = "I"
|
TEAM_INVITE = "I"
|
||||||
|
TEAM_ALLOWED_DOMAINS_MAX_LENGTH = 500
|
||||||
|
TEAM_COMPANY_NAME_MAX_LENGTH = 64
|
||||||
|
TEAM_DESCRIPTION_MAX_LENGTH = 255
|
||||||
|
TEAM_DISPLAY_NAME_MAX_RUNES = 64
|
||||||
|
TEAM_EMAIL_MAX_LENGTH = 128
|
||||||
|
TEAM_NAME_MAX_LENGTH = 64
|
||||||
|
TEAM_NAME_MIN_LENGTH = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type Team struct {
|
type Team struct {
|
||||||
@ -48,6 +56,14 @@ func InvitesFromJson(data io.Reader) *Invites {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Invites) ToEmailList() []string {
|
||||||
|
emailList := make([]string, len(o.Invites))
|
||||||
|
for _, invite := range o.Invites {
|
||||||
|
emailList = append(emailList, invite["email"])
|
||||||
|
}
|
||||||
|
return emailList
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Invites) ToJson() string {
|
func (o *Invites) ToJson() string {
|
||||||
b, err := json.Marshal(o)
|
b, err := json.Marshal(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -97,6 +113,26 @@ func TeamMapFromJson(data io.Reader) map[string]*Team {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TeamListToJson(t []*Team) string {
|
||||||
|
b, err := json.Marshal(t)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TeamListFromJson(data io.Reader) []*Team {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var teams []*Team
|
||||||
|
err := decoder.Decode(&teams)
|
||||||
|
if err == nil {
|
||||||
|
return teams
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Team) Etag() string {
|
func (o *Team) Etag() string {
|
||||||
return Etag(o.Id, o.UpdateAt)
|
return Etag(o.Id, o.UpdateAt)
|
||||||
}
|
}
|
||||||
@ -104,55 +140,55 @@ func (o *Team) Etag() string {
|
|||||||
func (o *Team) IsValid() *AppError {
|
func (o *Team) IsValid() *AppError {
|
||||||
|
|
||||||
if len(o.Id) != 26 {
|
if len(o.Id) != 26 {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "")
|
return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.CreateAt == 0 {
|
if o.CreateAt == 0 {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.UpdateAt == 0 {
|
if o.UpdateAt == 0 {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Email) > 128 {
|
if len(o.Email) > TEAM_EMAIL_MAX_LENGTH {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Email) > 0 && !IsValidEmail(o.Email) {
|
if len(o.Email) > 0 && !IsValidEmail(o.Email) {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > 64 {
|
if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > TEAM_DISPLAY_NAME_MAX_RUNES {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Name) > 64 {
|
if len(o.Name) > TEAM_NAME_MAX_LENGTH {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Description) > 255 {
|
if len(o.Description) > TEAM_DESCRIPTION_MAX_LENGTH {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsReservedTeamName(o.Name) {
|
if IsReservedTeamName(o.Name) {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsValidTeamName(o.Name) {
|
if !IsValidTeamName(o.Name) {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
|
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.CompanyName) > 64 {
|
if len(o.CompanyName) > TEAM_COMPANY_NAME_MAX_LENGTH {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.AllowedDomains) > 500 {
|
if len(o.AllowedDomains) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id)
|
return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -193,7 +229,7 @@ func IsValidTeamName(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) <= 1 {
|
if len(s) < TEAM_NAME_MIN_LENGTH {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
143
vendor/github.com/mattermost/platform/model/user.go
generated
vendored
143
vendor/github.com/mattermost/platform/model/user.go
generated
vendored
@ -7,20 +7,36 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
USER_NOTIFY_ALL = "all"
|
USER_NOTIFY_ALL = "all"
|
||||||
USER_NOTIFY_MENTION = "mention"
|
USER_NOTIFY_MENTION = "mention"
|
||||||
USER_NOTIFY_NONE = "none"
|
USER_NOTIFY_NONE = "none"
|
||||||
|
DESKTOP_NOTIFY_PROP = "desktop"
|
||||||
|
MARK_UNREAD_NOTIFY_PROP = "mark_unread"
|
||||||
|
PUSH_NOTIFY_PROP = "push"
|
||||||
|
EMAIL_NOTIFY_PROP = "email"
|
||||||
|
|
||||||
DEFAULT_LOCALE = "en"
|
DEFAULT_LOCALE = "en"
|
||||||
USER_AUTH_SERVICE_EMAIL = "email"
|
USER_AUTH_SERVICE_EMAIL = "email"
|
||||||
USER_AUTH_SERVICE_USERNAME = "username"
|
USER_AUTH_SERVICE_USERNAME = "username"
|
||||||
|
|
||||||
|
USER_EMAIL_MAX_LENGTH = 128
|
||||||
|
USER_NICKNAME_MAX_RUNES = 64
|
||||||
|
USER_POSITION_MAX_RUNES = 35
|
||||||
|
USER_FIRST_NAME_MAX_RUNES = 64
|
||||||
|
USER_LAST_NAME_MAX_RUNES = 64
|
||||||
|
USER_AUTH_DATA_MAX_LENGTH = 128
|
||||||
|
USER_NAME_MAX_LENGTH = 64
|
||||||
|
USER_NAME_MIN_LENGTH = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@ -51,56 +67,68 @@ type User struct {
|
|||||||
LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
|
LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserPatch struct {
|
||||||
|
Username *string `json:"username"`
|
||||||
|
Nickname *string `json:"nickname"`
|
||||||
|
FirstName *string `json:"first_name"`
|
||||||
|
LastName *string `json:"last_name"`
|
||||||
|
Position *string `json:"position"`
|
||||||
|
Email *string `json:"email"`
|
||||||
|
Props *StringMap `json:"props,omitempty"`
|
||||||
|
NotifyProps *StringMap `json:"notify_props,omitempty"`
|
||||||
|
Locale *string `json:"locale"`
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid validates the user and returns an error if it isn't configured
|
// IsValid validates the user and returns an error if it isn't configured
|
||||||
// correctly.
|
// correctly.
|
||||||
func (u *User) IsValid() *AppError {
|
func (u *User) IsValid() *AppError {
|
||||||
|
|
||||||
if len(u.Id) != 26 {
|
if len(u.Id) != 26 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "")
|
return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.CreateAt == 0 {
|
if u.CreateAt == 0 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.UpdateAt == 0 {
|
if u.UpdateAt == 0 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsValidUsername(u.Username) {
|
if !IsValidUsername(u.Username) {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(u.Email) > 128 || len(u.Email) == 0 {
|
if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(u.Nickname) > 64 {
|
if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(u.Position) > 35 {
|
if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(u.FirstName) > 64 {
|
if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(u.LastName) > 64 {
|
if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.AuthData != nil && len(*u.AuthData) > 128 {
|
if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
|
if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
|
if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id)
|
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -115,7 +143,7 @@ func (u *User) PreSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Username == "" {
|
if u.Username == "" {
|
||||||
u.Username = NewId()
|
u.Username = "n" + NewId()
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.AuthData != nil && *u.AuthData == "" {
|
if u.AuthData != nil && *u.AuthData == "" {
|
||||||
@ -205,6 +233,44 @@ func (user *User) UpdateMentionKeysFromUsername(oldUsername string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) Patch(patch *UserPatch) {
|
||||||
|
if patch.Username != nil {
|
||||||
|
u.Username = *patch.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Nickname != nil {
|
||||||
|
u.Nickname = *patch.Nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.FirstName != nil {
|
||||||
|
u.FirstName = *patch.FirstName
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.LastName != nil {
|
||||||
|
u.LastName = *patch.LastName
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Position != nil {
|
||||||
|
u.Position = *patch.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Email != nil {
|
||||||
|
u.Email = *patch.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Props != nil {
|
||||||
|
u.Props = *patch.Props
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.NotifyProps != nil {
|
||||||
|
u.NotifyProps = *patch.NotifyProps
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.Locale != nil {
|
||||||
|
u.Locale = *patch.Locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ToJson convert a User to a json string
|
// ToJson convert a User to a json string
|
||||||
func (u *User) ToJson() string {
|
func (u *User) ToJson() string {
|
||||||
b, err := json.Marshal(u)
|
b, err := json.Marshal(u)
|
||||||
@ -215,6 +281,15 @@ func (u *User) ToJson() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserPatch) ToJson() string {
|
||||||
|
b, err := json.Marshal(u)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a valid strong etag so the browser can cache the results
|
// Generate a valid strong etag so the browser can cache the results
|
||||||
func (u *User) Etag(showFullName, showEmail bool) string {
|
func (u *User) Etag(showFullName, showEmail bool) string {
|
||||||
return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
|
return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
|
||||||
@ -376,6 +451,13 @@ func IsInRole(userRoles string, inRole string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) IsSSOUser() bool {
|
||||||
|
if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (u *User) IsOAuthUser() bool {
|
func (u *User) IsOAuthUser() bool {
|
||||||
if u.AuthService == USER_AUTH_SERVICE_GITLAB {
|
if u.AuthService == USER_AUTH_SERVICE_GITLAB {
|
||||||
return true
|
return true
|
||||||
@ -402,6 +484,17 @@ func UserFromJson(data io.Reader) *User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserPatchFromJson(data io.Reader) *UserPatch {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var user UserPatch
|
||||||
|
err := decoder.Decode(&user)
|
||||||
|
if err == nil {
|
||||||
|
return &user
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UserMapToJson(u map[string]*User) string {
|
func UserMapToJson(u map[string]*User) string {
|
||||||
b, err := json.Marshal(u)
|
b, err := json.Marshal(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -472,7 +565,7 @@ var restrictedUsernames = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsValidUsername(s string) bool {
|
func IsValidUsername(s string) bool {
|
||||||
if len(s) == 0 || len(s) > 64 {
|
if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,6 +573,10 @@ func IsValidUsername(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !unicode.IsLetter(rune(s[0])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for _, restrictedUsername := range restrictedUsernames {
|
for _, restrictedUsername := range restrictedUsernames {
|
||||||
if s == restrictedUsername {
|
if s == restrictedUsername {
|
||||||
return false
|
return false
|
||||||
|
32
vendor/github.com/mattermost/platform/model/utils.go
generated
vendored
32
vendor/github.com/mattermost/platform/model/utils.go
generated
vendored
@ -34,14 +34,14 @@ type StringArray []string
|
|||||||
type EncryptStringMap map[string]string
|
type EncryptStringMap map[string]string
|
||||||
|
|
||||||
type AppError struct {
|
type AppError struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Message string `json:"message"` // Message to be display to the end user without debugging information
|
Message string `json:"message"` // Message to be display to the end user without debugging information
|
||||||
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
||||||
RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
|
RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
|
||||||
StatusCode int `json:"status_code,omitempty"` // The http status code
|
StatusCode int `json:"status_code,omitempty"` // The http status code
|
||||||
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
||||||
IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
|
IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
|
||||||
params map[string]interface{} `json:"-"`
|
params map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (er *AppError) Error() string {
|
func (er *AppError) Error() string {
|
||||||
@ -93,6 +93,18 @@ func AppErrorFromJson(data io.Reader) *AppError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
|
||||||
|
ap := &AppError{}
|
||||||
|
ap.Id = id
|
||||||
|
ap.params = params
|
||||||
|
ap.Message = id
|
||||||
|
ap.Where = where
|
||||||
|
ap.DetailedError = details
|
||||||
|
ap.StatusCode = status
|
||||||
|
ap.IsOAuth = false
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
|
func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
|
||||||
ap := &AppError{}
|
ap := &AppError{}
|
||||||
ap.Id = id
|
ap.Id = id
|
||||||
@ -268,7 +280,7 @@ func IsValidChannelIdentifier(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) < 2 {
|
if len(s) < CHANNEL_NAME_MIN_LENGTH {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +382,7 @@ func ClearMentionTags(post string) string {
|
|||||||
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
||||||
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
|
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
|
||||||
|
|
||||||
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
|
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true, '^': true, '#': true, '$': true, '&': true}
|
||||||
|
|
||||||
func IsValidHttpUrl(rawUrl string) bool {
|
func IsValidHttpUrl(rawUrl string) bool {
|
||||||
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
|
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
|
||||||
|
1
vendor/github.com/mattermost/platform/model/version.go
generated
vendored
1
vendor/github.com/mattermost/platform/model/version.go
generated
vendored
@ -13,6 +13,7 @@ import (
|
|||||||
// It should be maitained in chronological order with most current
|
// It should be maitained in chronological order with most current
|
||||||
// release at the front of the list.
|
// release at the front of the list.
|
||||||
var versions = []string{
|
var versions = []string{
|
||||||
|
"3.7.0",
|
||||||
"3.6.0",
|
"3.6.0",
|
||||||
"3.5.0",
|
"3.5.0",
|
||||||
"3.4.0",
|
"3.4.0",
|
||||||
|
8
vendor/github.com/mattermost/platform/model/websocket_client.go
generated
vendored
8
vendor/github.com/mattermost/platform/model/websocket_client.go
generated
vendored
@ -8,6 +8,10 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB
|
||||||
|
)
|
||||||
|
|
||||||
type WebSocketClient struct {
|
type WebSocketClient struct {
|
||||||
Url string // The location of the server like "ws://localhost:8065"
|
Url string // The location of the server like "ws://localhost:8065"
|
||||||
ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3"
|
ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3"
|
||||||
@ -22,14 +26,14 @@ type WebSocketClient struct {
|
|||||||
// NewWebSocketClient constructs a new WebSocket client with convienence
|
// NewWebSocketClient constructs a new WebSocket client with convienence
|
||||||
// methods for talking to the server.
|
// methods for talking to the server.
|
||||||
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
|
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
|
||||||
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil)
|
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
|
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &WebSocketClient{
|
client := &WebSocketClient{
|
||||||
url,
|
url,
|
||||||
url + API_URL_SUFFIX,
|
url + API_URL_SUFFIX_V3,
|
||||||
conn,
|
conn,
|
||||||
authToken,
|
authToken,
|
||||||
1,
|
1,
|
||||||
|
3
vendor/github.com/mattermost/platform/model/websocket_message.go
generated
vendored
3
vendor/github.com/mattermost/platform/model/websocket_message.go
generated
vendored
@ -14,8 +14,9 @@ const (
|
|||||||
WEBSOCKET_EVENT_POST_EDITED = "post_edited"
|
WEBSOCKET_EVENT_POST_EDITED = "post_edited"
|
||||||
WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
|
WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
|
||||||
WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
|
WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
|
||||||
WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
|
WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
|
||||||
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
|
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
|
||||||
|
WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
|
||||||
WEBSOCKET_EVENT_NEW_USER = "new_user"
|
WEBSOCKET_EVENT_NEW_USER = "new_user"
|
||||||
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
|
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
|
||||||
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
|
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
|
||||||
|
897
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
generated
vendored
Normal file
897
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,897 @@
|
|||||||
|
Mattermost Licensing
|
||||||
|
|
||||||
|
SOFTWARE LICENSING
|
||||||
|
|
||||||
|
You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE
|
||||||
|
|
||||||
|
- See MIT-COMPILED-LICENSE.md included in compiled versions for details
|
||||||
|
|
||||||
|
You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways:
|
||||||
|
|
||||||
|
1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or
|
||||||
|
2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com
|
||||||
|
|
||||||
|
You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,
|
||||||
|
webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0.
|
||||||
|
|
||||||
|
We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not
|
||||||
|
link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and
|
||||||
|
(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of
|
||||||
|
a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license.
|
||||||
|
|
||||||
|
MATTERMOST TRADEMARK GUIDELINES
|
||||||
|
|
||||||
|
Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark
|
||||||
|
Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions
|
||||||
|
you have about using these trademarks, please email trademark@mattermost.com
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The software is released under the terms of the GNU Affero General Public
|
||||||
|
License, version 3.
|
||||||
|
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
288
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
generated
vendored
Normal file
288
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
generated
vendored
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||||
|
|
||||||
|
package log4go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlProperty struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type xmlFilter struct {
|
||||||
|
Enabled string `xml:"enabled,attr"`
|
||||||
|
Tag string `xml:"tag"`
|
||||||
|
Level string `xml:"level"`
|
||||||
|
Type string `xml:"type"`
|
||||||
|
Property []xmlProperty `xml:"property"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type xmlLoggerConfig struct {
|
||||||
|
Filter []xmlFilter `xml:"filter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load XML configuration; see examples/example.xml for documentation
|
||||||
|
func (log Logger) LoadConfiguration(filename string) {
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
// Open the configuration file
|
||||||
|
fd, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := ioutil.ReadAll(fd)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
xc := new(xmlLoggerConfig)
|
||||||
|
if err := xml.Unmarshal(contents, xc); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, xmlfilt := range xc.Filter {
|
||||||
|
var filt LogWriter
|
||||||
|
var lvl Level
|
||||||
|
bad, good, enabled := false, true, false
|
||||||
|
|
||||||
|
// Check required children
|
||||||
|
if len(xmlfilt.Enabled) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
|
||||||
|
bad = true
|
||||||
|
} else {
|
||||||
|
enabled = xmlfilt.Enabled != "false"
|
||||||
|
}
|
||||||
|
if len(xmlfilt.Tag) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
|
||||||
|
bad = true
|
||||||
|
}
|
||||||
|
if len(xmlfilt.Type) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
|
||||||
|
bad = true
|
||||||
|
}
|
||||||
|
if len(xmlfilt.Level) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
|
||||||
|
bad = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch xmlfilt.Level {
|
||||||
|
case "FINEST":
|
||||||
|
lvl = FINEST
|
||||||
|
case "FINE":
|
||||||
|
lvl = FINE
|
||||||
|
case "DEBUG":
|
||||||
|
lvl = DEBUG
|
||||||
|
case "TRACE":
|
||||||
|
lvl = TRACE
|
||||||
|
case "INFO":
|
||||||
|
lvl = INFO
|
||||||
|
case "WARNING":
|
||||||
|
lvl = WARNING
|
||||||
|
case "ERROR":
|
||||||
|
lvl = ERROR
|
||||||
|
case "CRITICAL":
|
||||||
|
lvl = CRITICAL
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
|
||||||
|
bad = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just so all of the required attributes are errored at the same time if missing
|
||||||
|
if bad {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch xmlfilt.Type {
|
||||||
|
case "console":
|
||||||
|
filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
|
||||||
|
case "file":
|
||||||
|
filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
|
||||||
|
case "xml":
|
||||||
|
filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
|
||||||
|
case "socket":
|
||||||
|
filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just so all of the required params are errored at the same time if wrong
|
||||||
|
if !good {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're disabled (syntax and correctness checks only), don't add to logger
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log[xmlfilt.Tag] = &Filter{lvl, filt}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
|
||||||
|
// Parse properties
|
||||||
|
for _, prop := range props {
|
||||||
|
switch prop.Name {
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's disabled, we're just checking syntax
|
||||||
|
if !enabled {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewConsoleLogWriter(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
|
||||||
|
func strToNumSuffix(str string, mult int) int {
|
||||||
|
num := 1
|
||||||
|
if len(str) > 1 {
|
||||||
|
switch str[len(str)-1] {
|
||||||
|
case 'G', 'g':
|
||||||
|
num *= mult
|
||||||
|
fallthrough
|
||||||
|
case 'M', 'm':
|
||||||
|
num *= mult
|
||||||
|
fallthrough
|
||||||
|
case 'K', 'k':
|
||||||
|
num *= mult
|
||||||
|
str = str[0 : len(str)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsed, _ := strconv.Atoi(str)
|
||||||
|
return parsed * num
|
||||||
|
}
|
||||||
|
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||||
|
file := ""
|
||||||
|
format := "[%D %T] [%L] (%S) %M"
|
||||||
|
maxlines := 0
|
||||||
|
maxsize := 0
|
||||||
|
daily := false
|
||||||
|
rotate := false
|
||||||
|
|
||||||
|
// Parse properties
|
||||||
|
for _, prop := range props {
|
||||||
|
switch prop.Name {
|
||||||
|
case "filename":
|
||||||
|
file = strings.Trim(prop.Value, " \r\n")
|
||||||
|
case "format":
|
||||||
|
format = strings.Trim(prop.Value, " \r\n")
|
||||||
|
case "maxlines":
|
||||||
|
maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||||
|
case "maxsize":
|
||||||
|
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||||
|
case "daily":
|
||||||
|
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||||
|
case "rotate":
|
||||||
|
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check properties
|
||||||
|
if len(file) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's disabled, we're just checking syntax
|
||||||
|
if !enabled {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
flw := NewFileLogWriter(file, rotate)
|
||||||
|
flw.SetFormat(format)
|
||||||
|
flw.SetRotateLines(maxlines)
|
||||||
|
flw.SetRotateSize(maxsize)
|
||||||
|
flw.SetRotateDaily(daily)
|
||||||
|
return flw, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||||
|
file := ""
|
||||||
|
maxrecords := 0
|
||||||
|
maxsize := 0
|
||||||
|
daily := false
|
||||||
|
rotate := false
|
||||||
|
|
||||||
|
// Parse properties
|
||||||
|
for _, prop := range props {
|
||||||
|
switch prop.Name {
|
||||||
|
case "filename":
|
||||||
|
file = strings.Trim(prop.Value, " \r\n")
|
||||||
|
case "maxrecords":
|
||||||
|
maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||||
|
case "maxsize":
|
||||||
|
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||||
|
case "daily":
|
||||||
|
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||||
|
case "rotate":
|
||||||
|
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check properties
|
||||||
|
if len(file) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's disabled, we're just checking syntax
|
||||||
|
if !enabled {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
xlw := NewXMLLogWriter(file, rotate)
|
||||||
|
xlw.SetRotateLines(maxrecords)
|
||||||
|
xlw.SetRotateSize(maxsize)
|
||||||
|
xlw.SetRotateDaily(daily)
|
||||||
|
return xlw, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
|
||||||
|
endpoint := ""
|
||||||
|
protocol := "udp"
|
||||||
|
|
||||||
|
// Parse properties
|
||||||
|
for _, prop := range props {
|
||||||
|
switch prop.Name {
|
||||||
|
case "endpoint":
|
||||||
|
endpoint = strings.Trim(prop.Value, " \r\n")
|
||||||
|
case "protocol":
|
||||||
|
protocol = strings.Trim(prop.Value, " \r\n")
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check properties
|
||||||
|
if len(endpoint) == 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's disabled, we're just checking syntax
|
||||||
|
if !enabled {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSocketLogWriter(protocol, endpoint), true
|
||||||
|
}
|
14
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
generated
vendored
Normal file
14
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
import l4g "code.google.com/p/log4go"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := l4g.NewLogger()
|
||||||
|
defer log.Close()
|
||||||
|
log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
|
||||||
|
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||||
|
}
|
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
generated
vendored
Normal file
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
import l4g "code.google.com/p/log4go"
|
||||||
|
|
||||||
|
const (
|
||||||
|
filename = "flw.log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Get a new logger instance
|
||||||
|
log := l4g.NewLogger()
|
||||||
|
|
||||||
|
// Create a default logger that is logging messages of FINE or higher
|
||||||
|
log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false))
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
/* Can also specify manually via the following: (these are the defaults) */
|
||||||
|
flw := l4g.NewFileLogWriter(filename, false)
|
||||||
|
flw.SetFormat("[%D %T] [%L] (%S) %M")
|
||||||
|
flw.SetRotate(false)
|
||||||
|
flw.SetRotateSize(0)
|
||||||
|
flw.SetRotateLines(0)
|
||||||
|
flw.SetRotateDaily(false)
|
||||||
|
log.AddFilter("file", l4g.FINE, flw)
|
||||||
|
|
||||||
|
// Log some experimental messages
|
||||||
|
log.Finest("Everything is created now (notice that I will not be printing to the file)")
|
||||||
|
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||||
|
log.Critical("Time to close out!")
|
||||||
|
|
||||||
|
// Close the log
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
// Print what was logged to the file (yes, I know I'm skipping error checking)
|
||||||
|
fd, _ := os.Open(filename)
|
||||||
|
in := bufio.NewReader(fd)
|
||||||
|
fmt.Print("Messages logged to file were: (line numbers not included)\n")
|
||||||
|
for lineno := 1; ; lineno++ {
|
||||||
|
line, err := in.ReadString('\n')
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Printf("%3d:\t%s", lineno, line)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
// Remove the file so it's not lying around
|
||||||
|
os.Remove(filename)
|
||||||
|
}
|
42
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
generated
vendored
Normal file
42
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
port = flag.String("p", "12124", "Port number to listen on")
|
||||||
|
)
|
||||||
|
|
||||||
|
func e(err error) {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erroring out: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Bind to the port
|
||||||
|
bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port)
|
||||||
|
e(err)
|
||||||
|
|
||||||
|
// Create listener
|
||||||
|
listener, err := net.ListenUDP("udp", bind)
|
||||||
|
e(err)
|
||||||
|
|
||||||
|
fmt.Printf("Listening to port %s...\n", *port)
|
||||||
|
for {
|
||||||
|
// read into a new buffer
|
||||||
|
buffer := make([]byte, 1024)
|
||||||
|
_, _, err := listener.ReadFrom(buffer)
|
||||||
|
e(err)
|
||||||
|
|
||||||
|
// log to standard output
|
||||||
|
fmt.Println(string(buffer))
|
||||||
|
}
|
||||||
|
}
|
18
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
generated
vendored
Normal file
18
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
import l4g "code.google.com/p/log4go"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := l4g.NewLogger()
|
||||||
|
log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124"))
|
||||||
|
|
||||||
|
// Run `nc -u -l -p 12124` or similar before you run this to see the following message
|
||||||
|
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||||
|
|
||||||
|
// This makes sure the output stream buffer is written
|
||||||
|
log.Close()
|
||||||
|
}
|
13
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
generated
vendored
Normal file
13
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import l4g "code.google.com/p/log4go"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Load the configuration (isn't this easy?)
|
||||||
|
l4g.LoadConfiguration("example.xml")
|
||||||
|
|
||||||
|
// And now we're ready!
|
||||||
|
l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.")
|
||||||
|
l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2)
|
||||||
|
l4g.Info("About that time, eh chaps?")
|
||||||
|
}
|
264
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
generated
vendored
Normal file
264
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||||
|
|
||||||
|
package log4go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This log writer sends output to a file
|
||||||
|
type FileLogWriter struct {
|
||||||
|
rec chan *LogRecord
|
||||||
|
rot chan bool
|
||||||
|
|
||||||
|
// The opened file
|
||||||
|
filename string
|
||||||
|
file *os.File
|
||||||
|
|
||||||
|
// The logging format
|
||||||
|
format string
|
||||||
|
|
||||||
|
// File header/trailer
|
||||||
|
header, trailer string
|
||||||
|
|
||||||
|
// Rotate at linecount
|
||||||
|
maxlines int
|
||||||
|
maxlines_curlines int
|
||||||
|
|
||||||
|
// Rotate at size
|
||||||
|
maxsize int
|
||||||
|
maxsize_cursize int
|
||||||
|
|
||||||
|
// Rotate daily
|
||||||
|
daily bool
|
||||||
|
daily_opendate int
|
||||||
|
|
||||||
|
// Keep old logfiles (.001, .002, etc)
|
||||||
|
rotate bool
|
||||||
|
maxbackup int
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the FileLogWriter's output method
|
||||||
|
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
|
||||||
|
w.rec <- rec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *FileLogWriter) Close() {
|
||||||
|
close(w.rec)
|
||||||
|
w.file.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileLogWriter creates a new LogWriter which writes to the given file and
|
||||||
|
// has rotation enabled if rotate is true.
|
||||||
|
//
|
||||||
|
// If rotate is true, any time a new log file is opened, the old one is renamed
|
||||||
|
// with a .### extension to preserve it. The various Set* methods can be used
|
||||||
|
// to configure log rotation based on lines, size, and daily.
|
||||||
|
//
|
||||||
|
// The standard log-line format is:
|
||||||
|
// [%D %T] [%L] (%S) %M
|
||||||
|
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||||
|
w := &FileLogWriter{
|
||||||
|
rec: make(chan *LogRecord, LogBufferLength),
|
||||||
|
rot: make(chan bool),
|
||||||
|
filename: fname,
|
||||||
|
format: "[%D %T] [%L] (%S) %M",
|
||||||
|
rotate: rotate,
|
||||||
|
maxbackup: 999,
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file for the first time
|
||||||
|
if err := w.intRotate(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if w.file != nil {
|
||||||
|
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||||
|
w.file.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.rot:
|
||||||
|
if err := w.intRotate(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case rec, ok := <-w.rec:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
|
||||||
|
(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
|
||||||
|
(w.daily && now.Day() != w.daily_opendate) {
|
||||||
|
if err := w.intRotate(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the write
|
||||||
|
n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the counts
|
||||||
|
w.maxlines_curlines++
|
||||||
|
w.maxsize_cursize += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request that the logs rotate
|
||||||
|
func (w *FileLogWriter) Rotate() {
|
||||||
|
w.rot <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is called in a threaded context, it MUST be synchronized
|
||||||
|
func (w *FileLogWriter) intRotate() error {
|
||||||
|
// Close any log file that may be open
|
||||||
|
if w.file != nil {
|
||||||
|
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||||
|
w.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are keeping log files, move it to the next available number
|
||||||
|
if w.rotate {
|
||||||
|
_, err := os.Lstat(w.filename)
|
||||||
|
if err == nil { // file exists
|
||||||
|
// Find the next available number
|
||||||
|
num := 1
|
||||||
|
fname := ""
|
||||||
|
if w.daily && time.Now().Day() != w.daily_opendate {
|
||||||
|
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||||
|
|
||||||
|
for ; err == nil && num <= 999; num++ {
|
||||||
|
fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num)
|
||||||
|
_, err = os.Lstat(fname)
|
||||||
|
}
|
||||||
|
// return error if the last file checked still existed
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
num = w.maxbackup - 1
|
||||||
|
for ; num >= 1; num-- {
|
||||||
|
fname = w.filename + fmt.Sprintf(".%d", num)
|
||||||
|
nfname := w.filename + fmt.Sprintf(".%d", num+1)
|
||||||
|
_, err = os.Lstat(fname)
|
||||||
|
if err == nil {
|
||||||
|
os.Rename(fname, nfname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.file.Close()
|
||||||
|
// Rename the file to its newfound home
|
||||||
|
err = os.Rename(w.filename, fname)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Rotate: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the log file
|
||||||
|
fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.file = fd
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
|
||||||
|
|
||||||
|
// Set the daily open date to the current date
|
||||||
|
w.daily_opendate = now.Day()
|
||||||
|
|
||||||
|
// initialize rotation values
|
||||||
|
w.maxlines_curlines = 0
|
||||||
|
w.maxsize_cursize = 0
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the logging format (chainable). Must be called before the first log
|
||||||
|
// message is written.
|
||||||
|
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
|
||||||
|
w.format = format
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the logfile header and footer (chainable). Must be called before the first log
|
||||||
|
// message is written. These are formatted similar to the FormatLogRecord (e.g.
|
||||||
|
// you can use %D and %T in your header/footer for date and time).
|
||||||
|
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
|
||||||
|
w.header, w.trailer = head, foot
|
||||||
|
if w.maxlines_curlines == 0 {
|
||||||
|
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set rotate at linecount (chainable). Must be called before the first log
|
||||||
|
// message is written.
|
||||||
|
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
|
||||||
|
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
|
||||||
|
w.maxlines = maxlines
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set rotate at size (chainable). Must be called before the first log message
|
||||||
|
// is written.
|
||||||
|
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
|
||||||
|
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
|
||||||
|
w.maxsize = maxsize
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set rotate daily (chainable). Must be called before the first log message is
|
||||||
|
// written.
|
||||||
|
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
|
||||||
|
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
|
||||||
|
w.daily = daily
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set max backup files. Must be called before the first log message
|
||||||
|
// is written.
|
||||||
|
func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter {
|
||||||
|
w.maxbackup = maxbackup
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRotate changes whether or not the old logs are kept. (chainable) Must be
|
||||||
|
// called before the first log message is written. If rotate is false, the
|
||||||
|
// files are overwritten; otherwise, they are rotated to another file before the
|
||||||
|
// new log is opened.
|
||||||
|
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
|
||||||
|
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
|
||||||
|
w.rotate = rotate
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
|
||||||
|
// output XML record log messages instead of line-based ones.
|
||||||
|
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||||
|
return NewFileLogWriter(fname, rotate).SetFormat(
|
||||||
|
` <record level="%L">
|
||||||
|
<timestamp>%D %T</timestamp>
|
||||||
|
<source>%S</source>
|
||||||
|
<message>%M</message>
|
||||||
|
</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
|
||||||
|
}
|
484
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
generated
vendored
Normal file
484
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
generated
vendored
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||||
|
|
||||||
|
// Package log4go provides level-based and highly configurable logging.
|
||||||
|
//
|
||||||
|
// Enhanced Logging
|
||||||
|
//
|
||||||
|
// This is inspired by the logging functionality in Java. Essentially, you create a Logger
|
||||||
|
// object and create output filters for it. You can send whatever you want to the Logger,
|
||||||
|
// and it will filter that based on your settings and send it to the outputs. This way, you
|
||||||
|
// can put as much debug code in your program as you want, and when you're done you can filter
|
||||||
|
// out the mundane messages so only the important ones show up.
|
||||||
|
//
|
||||||
|
// Utility functions are provided to make life easier. Here is some example code to get started:
|
||||||
|
//
|
||||||
|
// log := log4go.NewLogger()
|
||||||
|
// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
|
||||||
|
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||||
|
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||||
|
//
|
||||||
|
// The first two lines can be combined with the utility NewDefaultLogger:
|
||||||
|
//
|
||||||
|
// log := log4go.NewDefaultLogger(log4go.DEBUG)
|
||||||
|
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||||
|
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||||
|
//
|
||||||
|
// Usage notes:
|
||||||
|
// - The ConsoleLogWriter does not display the source of the message to standard
|
||||||
|
// output, but the FileLogWriter does.
|
||||||
|
// - The utility functions (Info, Debug, Warn, etc) derive their source from the
|
||||||
|
// calling function, and this incurs extra overhead.
|
||||||
|
//
|
||||||
|
// Changes from 2.0:
|
||||||
|
// - The external interface has remained mostly stable, but a lot of the
|
||||||
|
// internals have been changed, so if you depended on any of this or created
|
||||||
|
// your own LogWriter, then you will probably have to update your code. In
|
||||||
|
// particular, Logger is now a map and ConsoleLogWriter is now a channel
|
||||||
|
// behind-the-scenes, and the LogWrite method no longer has return values.
|
||||||
|
//
|
||||||
|
// Future work: (please let me know if you think I should work on any of these particularly)
|
||||||
|
// - Log file rotation
|
||||||
|
// - Logging configuration files ala log4j
|
||||||
|
// - Have the ability to remove filters?
|
||||||
|
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
|
||||||
|
// for another method of logging
|
||||||
|
// - Add an XML filter type
|
||||||
|
package log4go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version information
|
||||||
|
const (
|
||||||
|
L4G_VERSION = "log4go-v3.0.1"
|
||||||
|
L4G_MAJOR = 3
|
||||||
|
L4G_MINOR = 0
|
||||||
|
L4G_BUILD = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
/****** Constants ******/
|
||||||
|
|
||||||
|
// These are the integer logging levels used by the logger
|
||||||
|
type Level int
|
||||||
|
|
||||||
|
const (
|
||||||
|
FINEST Level = iota
|
||||||
|
FINE
|
||||||
|
DEBUG
|
||||||
|
TRACE
|
||||||
|
INFO
|
||||||
|
WARNING
|
||||||
|
ERROR
|
||||||
|
CRITICAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logging level strings
|
||||||
|
var (
|
||||||
|
levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (l Level) String() string {
|
||||||
|
if l < 0 || int(l) > len(levelStrings) {
|
||||||
|
return "UNKNOWN"
|
||||||
|
}
|
||||||
|
return levelStrings[int(l)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/****** Variables ******/
|
||||||
|
var (
|
||||||
|
// LogBufferLength specifies how many log messages a particular log4go
|
||||||
|
// logger can buffer at a time before writing them.
|
||||||
|
LogBufferLength = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
/****** LogRecord ******/
|
||||||
|
|
||||||
|
// A LogRecord contains all of the pertinent information for each message
|
||||||
|
type LogRecord struct {
|
||||||
|
Level Level // The log level
|
||||||
|
Created time.Time // The time at which the log message was created (nanoseconds)
|
||||||
|
Source string // The message source
|
||||||
|
Message string // The log message
|
||||||
|
}
|
||||||
|
|
||||||
|
/****** LogWriter ******/
|
||||||
|
|
||||||
|
// This is an interface for anything that should be able to write logs
|
||||||
|
type LogWriter interface {
|
||||||
|
// This will be called to log a LogRecord message.
|
||||||
|
LogWrite(rec *LogRecord)
|
||||||
|
|
||||||
|
// This should clean up anything lingering about the LogWriter, as it is called before
|
||||||
|
// the LogWriter is removed. LogWrite should not be called after Close.
|
||||||
|
Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/****** Logger ******/
|
||||||
|
|
||||||
|
// A Filter represents the log level below which no log records are written to
|
||||||
|
// the associated LogWriter.
|
||||||
|
type Filter struct {
|
||||||
|
Level Level
|
||||||
|
LogWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Logger represents a collection of Filters through which log messages are
|
||||||
|
// written.
|
||||||
|
type Logger map[string]*Filter
|
||||||
|
|
||||||
|
// Create a new logger.
|
||||||
|
//
|
||||||
|
// DEPRECATED: Use make(Logger) instead.
|
||||||
|
func NewLogger() Logger {
|
||||||
|
os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
|
||||||
|
return make(Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||||
|
// or above lvl to standard output.
|
||||||
|
//
|
||||||
|
// DEPRECATED: use NewDefaultLogger instead.
|
||||||
|
func NewConsoleLogger(lvl Level) Logger {
|
||||||
|
os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
|
||||||
|
return Logger{
|
||||||
|
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||||
|
// or above lvl to standard output.
|
||||||
|
func NewDefaultLogger(lvl Level) Logger {
|
||||||
|
return Logger{
|
||||||
|
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes all log writers in preparation for exiting the program or a
|
||||||
|
// reconfiguration of logging. Calling this is not really imperative, unless
|
||||||
|
// you want to guarantee that all log messages are written. Close removes
|
||||||
|
// all filters (and thus all LogWriters) from the logger.
|
||||||
|
func (log Logger) Close() {
|
||||||
|
// Close all open loggers
|
||||||
|
for name, filt := range log {
|
||||||
|
filt.Close()
|
||||||
|
delete(log, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new LogWriter to the Logger which will only log messages at lvl or
|
||||||
|
// higher. This function should not be called from multiple goroutines.
|
||||||
|
// Returns the logger for chaining.
|
||||||
|
func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
|
||||||
|
log[name] = &Filter{lvl, writer}
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
/******* Logging *******/
|
||||||
|
// Send a formatted log message internally
|
||||||
|
func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
|
||||||
|
skip := true
|
||||||
|
|
||||||
|
// Determine if any logging will be done
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl >= filt.Level {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine caller func
|
||||||
|
pc, _, lineno, ok := runtime.Caller(2)
|
||||||
|
src := ""
|
||||||
|
if ok {
|
||||||
|
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := format
|
||||||
|
if len(args) > 0 {
|
||||||
|
msg = fmt.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the log record
|
||||||
|
rec := &LogRecord{
|
||||||
|
Level: lvl,
|
||||||
|
Created: time.Now(),
|
||||||
|
Source: src,
|
||||||
|
Message: msg,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the logs
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl < filt.Level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filt.LogWrite(rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a closure log message internally
|
||||||
|
func (log Logger) intLogc(lvl Level, closure func() string) {
|
||||||
|
skip := true
|
||||||
|
|
||||||
|
// Determine if any logging will be done
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl >= filt.Level {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine caller func
|
||||||
|
pc, _, lineno, ok := runtime.Caller(2)
|
||||||
|
src := ""
|
||||||
|
if ok {
|
||||||
|
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the log record
|
||||||
|
rec := &LogRecord{
|
||||||
|
Level: lvl,
|
||||||
|
Created: time.Now(),
|
||||||
|
Source: src,
|
||||||
|
Message: closure(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the logs
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl < filt.Level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filt.LogWrite(rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a log message with manual level, source, and message.
|
||||||
|
func (log Logger) Log(lvl Level, source, message string) {
|
||||||
|
skip := true
|
||||||
|
|
||||||
|
// Determine if any logging will be done
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl >= filt.Level {
|
||||||
|
skip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the log record
|
||||||
|
rec := &LogRecord{
|
||||||
|
Level: lvl,
|
||||||
|
Created: time.Now(),
|
||||||
|
Source: source,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the logs
|
||||||
|
for _, filt := range log {
|
||||||
|
if lvl < filt.Level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filt.LogWrite(rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logf logs a formatted log message at the given log level, using the caller as
|
||||||
|
// its source.
|
||||||
|
func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
|
||||||
|
log.intLogf(lvl, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logc logs a string returned by the closure at the given log level, using the caller as
|
||||||
|
// its source. If no log message would be written, the closure is never called.
|
||||||
|
func (log Logger) Logc(lvl Level, closure func() string) {
|
||||||
|
log.intLogc(lvl, closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finest logs a message at the finest log level.
|
||||||
|
// See Debug for an explanation of the arguments.
|
||||||
|
func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
|
||||||
|
const (
|
||||||
|
lvl = FINEST
|
||||||
|
)
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
log.intLogf(lvl, first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
log.intLogc(lvl, first)
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fine logs a message at the fine log level.
|
||||||
|
// See Debug for an explanation of the arguments.
|
||||||
|
func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
|
||||||
|
const (
|
||||||
|
lvl = FINE
|
||||||
|
)
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
log.intLogf(lvl, first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
log.intLogc(lvl, first)
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug is a utility method for debug log messages.
|
||||||
|
// The behavior of Debug depends on the first argument:
|
||||||
|
// - arg0 is a string
|
||||||
|
// When given a string as the first argument, this behaves like Logf but with
|
||||||
|
// the DEBUG log level: the first argument is interpreted as a format for the
|
||||||
|
// latter arguments.
|
||||||
|
// - arg0 is a func()string
|
||||||
|
// When given a closure of type func()string, this logs the string returned by
|
||||||
|
// the closure iff it will be logged. The closure runs at most one time.
|
||||||
|
// - arg0 is interface{}
|
||||||
|
// When given anything else, the log message will be each of the arguments
|
||||||
|
// formatted with %v and separated by spaces (ala Sprint).
|
||||||
|
func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
|
||||||
|
const (
|
||||||
|
lvl = DEBUG
|
||||||
|
)
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
log.intLogf(lvl, first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
log.intLogc(lvl, first)
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace logs a message at the trace log level.
|
||||||
|
// See Debug for an explanation of the arguments.
|
||||||
|
func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
|
||||||
|
const (
|
||||||
|
lvl = TRACE
|
||||||
|
)
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
log.intLogf(lvl, first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
log.intLogc(lvl, first)
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a message at the info log level.
|
||||||
|
// See Debug for an explanation of the arguments.
|
||||||
|
func (log Logger) Info(arg0 interface{}, args ...interface{}) {
|
||||||
|
const (
|
||||||
|
lvl = INFO
|
||||||
|
)
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
log.intLogf(lvl, first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
log.intLogc(lvl, first)
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a message at the warning log level and returns the formatted error.
|
||||||
|
// At the warning level and higher, there is no performance benefit if the
|
||||||
|
// message is not actually logged, because all formats are processed and all
|
||||||
|
// closures are executed to format the error message.
|
||||||
|
// See Debug for further explanation of the arguments.
|
||||||
|
func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
|
||||||
|
const (
|
||||||
|
lvl = WARNING
|
||||||
|
)
|
||||||
|
var msg string
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
msg = fmt.Sprintf(first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
msg = first()
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
log.intLogf(lvl, msg)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at the error log level and returns the formatted error,
|
||||||
|
// See Warn for an explanation of the performance and Debug for an explanation
|
||||||
|
// of the parameters.
|
||||||
|
func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
|
||||||
|
const (
|
||||||
|
lvl = ERROR
|
||||||
|
)
|
||||||
|
var msg string
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
msg = fmt.Sprintf(first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
msg = first()
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
log.intLogf(lvl, msg)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Critical logs a message at the critical log level and returns the formatted error,
|
||||||
|
// See Warn for an explanation of the performance and Debug for an explanation
|
||||||
|
// of the parameters.
|
||||||
|
func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
|
||||||
|
const (
|
||||||
|
lvl = CRITICAL
|
||||||
|
)
|
||||||
|
var msg string
|
||||||
|
switch first := arg0.(type) {
|
||||||
|
case string:
|
||||||
|
// Use the string as a format string
|
||||||
|
msg = fmt.Sprintf(first, args...)
|
||||||
|
case func() string:
|
||||||
|
// Log the closure (no other arguments used)
|
||||||
|
msg = first()
|
||||||
|
default:
|
||||||
|
// Build a format string so that it will be similar to Sprint
|
||||||
|
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||||
|
}
|
||||||
|
log.intLogf(lvl, msg)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user