forked from lug/matterbridge
		
	Compare commits
	
		
			40 Commits
		
	
	
		
			v0.10.2-de
			...
			v0.12.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7a16146304 | ||
| 
						 | 
					3d3809a21b | ||
| 
						 | 
					29465397dd | ||
| 
						 | 
					d300bb1735 | ||
| 
						 | 
					2e703472f1 | ||
| 
						 | 
					8fede90b9e | ||
| 
						 | 
					d128f157c4 | ||
| 
						 | 
					4fcedabfd0 | ||
| 
						 | 
					246c8e4f74 | ||
| 
						 | 
					4d2207aba7 | ||
| 
						 | 
					17b8b86d68 | ||
| 
						 | 
					fdb57230a3 | ||
| 
						 | 
					7469732bbc | ||
| 
						 | 
					d1dd6c3440 | ||
| 
						 | 
					02612c0061 | ||
| 
						 | 
					a4db63a773 | ||
| 
						 | 
					035c2b906a | ||
| 
						 | 
					6ea8be5749 | ||
| 
						 | 
					36024d5439 | ||
| 
						 | 
					8d52c98373 | ||
| 
						 | 
					b4a4eb0057 | ||
| 
						 | 
					b469c8ddbd | ||
| 
						 | 
					eee0036c7f | ||
| 
						 | 
					89c66b9430 | ||
| 
						 | 
					bd38319d83 | ||
| 
						 | 
					33dffd5ea8 | ||
| 
						 | 
					57176dadd4 | ||
| 
						 | 
					dd449a8705 | ||
| 
						 | 
					587ad9f41d | ||
| 
						 | 
					a16ad8bf3b | ||
| 
						 | 
					1e0490bd36 | ||
| 
						 | 
					8afc641f0c | ||
| 
						 | 
					2e4d58cb92 | ||
| 
						 | 
					02d7e2db65 | ||
| 
						 | 
					f935c573e9 | ||
| 
						 | 
					4a25e66c00 | ||
| 
						 | 
					95f4e3448e | ||
| 
						 | 
					eacb1c1771 | ||
| 
						 | 
					07fd825349 | ||
| 
						 | 
					be15cc8a36 | 
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
								
							@@ -28,7 +28,7 @@ Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, R
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Requirements
 | 
					# Requirements
 | 
				
			||||||
Accounts to one of the supported bridges
 | 
					Accounts to one of the supported bridges
 | 
				
			||||||
* [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.7.x
 | 
					* [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.9.x
 | 
				
			||||||
* [IRC](http://www.mirc.com/servers.html)
 | 
					* [IRC](http://www.mirc.com/servers.html)
 | 
				
			||||||
* [XMPP](https://jabber.org)
 | 
					* [XMPP](https://jabber.org)
 | 
				
			||||||
* [Gitter](https://gitter.im)
 | 
					* [Gitter](https://gitter.im)
 | 
				
			||||||
@@ -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 stable release [v0.12.0](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,16 +122,20 @@ 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:
 | 
				
			||||||
  -conf string
 | 
					  -conf string
 | 
				
			||||||
        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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,20 +30,20 @@ type Bridge struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	EVENT_JOIN_LEAVE      = "join_leave"
 | 
						EVENT_JOIN_LEAVE      = "join_leave"
 | 
				
			||||||
	EVENT_FAILURE         = "failure"
 | 
						EVENT_FAILURE         = "failure"
 | 
				
			||||||
 | 
						EVENT_REJOIN_CHANNELS = "rejoin_channels"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Message struct {
 | 
					type Message struct {
 | 
				
			||||||
@@ -25,9 +26,21 @@ 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
 | 
				
			||||||
 | 
						EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
				
			||||||
 | 
						EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
				
			||||||
	IconURL                string // mattermost, slack
 | 
						IconURL                string // mattermost, slack
 | 
				
			||||||
	IgnoreNicks            string // all protocols
 | 
						IgnoreNicks            string // all protocols
 | 
				
			||||||
	Jid                    string // xmpp
 | 
						Jid                    string // xmpp
 | 
				
			||||||
@@ -66,6 +79,7 @@ type Bridge struct {
 | 
				
			|||||||
	Account     string
 | 
						Account     string
 | 
				
			||||||
	Channel     string
 | 
						Channel     string
 | 
				
			||||||
	Options     ChannelOptions
 | 
						Options     ChannelOptions
 | 
				
			||||||
 | 
						SameChannel bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Gateway struct {
 | 
					type Gateway struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	log "github.com/Sirupsen/logrus"
 | 
						log "github.com/Sirupsen/logrus"
 | 
				
			||||||
	"github.com/bwmarrin/discordgo"
 | 
						"github.com/bwmarrin/discordgo"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -51,6 +52,7 @@ func (b *bdiscord) Connect() error {
 | 
				
			|||||||
	flog.Info("Connection succeeded")
 | 
						flog.Info("Connection succeeded")
 | 
				
			||||||
	b.c.AddHandler(b.messageCreate)
 | 
						b.c.AddHandler(b.messageCreate)
 | 
				
			||||||
	b.c.AddHandler(b.memberUpdate)
 | 
						b.c.AddHandler(b.memberUpdate)
 | 
				
			||||||
 | 
						b.c.AddHandler(b.messageUpdate)
 | 
				
			||||||
	err = b.c.Open()
 | 
						err = b.c.Open()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		flog.Debugf("%#v", err)
 | 
							flog.Debugf("%#v", err)
 | 
				
			||||||
@@ -103,6 +105,18 @@ func (b *bdiscord) Send(msg config.Message) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
 | 
				
			||||||
 | 
						if b.Config.EditDisable {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// only when message is actually edited
 | 
				
			||||||
 | 
						if m.Message.EditedTimestamp != "" {
 | 
				
			||||||
 | 
							flog.Debugf("Sending edit message")
 | 
				
			||||||
 | 
							m.Content = m.Content + b.Config.EditSuffix
 | 
				
			||||||
 | 
							b.messageCreate(s, (*discordgo.MessageCreate)(m))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 | 
					func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 | 
				
			||||||
	// not relay our own messages
 | 
						// not relay our own messages
 | 
				
			||||||
	if m.Author.Username == b.Nick {
 | 
						if m.Author.Username == b.Nick {
 | 
				
			||||||
@@ -125,6 +139,7 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
 | 
				
			|||||||
	if len(m.MentionRoles) > 0 {
 | 
						if len(m.MentionRoles) > 0 {
 | 
				
			||||||
		m.Message.Content = b.replaceRoleMentions(m.Message.Content)
 | 
							m.Message.Content = b.replaceRoleMentions(m.Message.Content)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						m.Message.Content = b.stripCustomoji(m.Message.Content)
 | 
				
			||||||
	b.Remote <- config.Message{Username: username, Text: m.ContentWithMentionsReplaced(), Channel: channelName,
 | 
						b.Remote <- config.Message{Username: username, Text: m.ContentWithMentionsReplaced(), Channel: channelName,
 | 
				
			||||||
		Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"}
 | 
							Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -195,3 +210,9 @@ func (b *bdiscord) replaceRoleMentions(text string) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return text
 | 
						return text
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *bdiscord) stripCustomoji(text string) string {
 | 
				
			||||||
 | 
						// <:doge:302803592035958784>
 | 
				
			||||||
 | 
						re := regexp.MustCompile("<(:.*?:)[0-9]+>")
 | 
				
			||||||
 | 
						return re.ReplaceAllString(text, `$1`)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -109,7 +109,6 @@ func (b *Birc) Send(msg config.Message) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if strings.HasPrefix(msg.Text, "!") {
 | 
						if strings.HasPrefix(msg.Text, "!") {
 | 
				
			||||||
		b.Command(&msg)
 | 
							b.Command(&msg)
 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, text := range strings.Split(msg.Text, "\n") {
 | 
						for _, text := range strings.Split(msg.Text, "\n") {
 | 
				
			||||||
		if len(b.Local) < b.Config.MessageQueue {
 | 
							if len(b.Local) < b.Config.MessageQueue {
 | 
				
			||||||
@@ -167,14 +166,19 @@ func (b *Birc) handleNewConnection(event *irc.Event) {
 | 
				
			|||||||
	i.AddCallback("JOIN", b.handleJoinPart)
 | 
						i.AddCallback("JOIN", b.handleJoinPart)
 | 
				
			||||||
	i.AddCallback("PART", b.handleJoinPart)
 | 
						i.AddCallback("PART", b.handleJoinPart)
 | 
				
			||||||
	i.AddCallback("QUIT", b.handleJoinPart)
 | 
						i.AddCallback("QUIT", b.handleJoinPart)
 | 
				
			||||||
 | 
						i.AddCallback("KICK", b.handleJoinPart)
 | 
				
			||||||
	i.AddCallback("*", b.handleOther)
 | 
						i.AddCallback("*", b.handleOther)
 | 
				
			||||||
	// we are now fully connected
 | 
						// we are now fully connected
 | 
				
			||||||
	b.connected <- struct{}{}
 | 
						b.connected <- struct{}{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Birc) handleJoinPart(event *irc.Event) {
 | 
					func (b *Birc) handleJoinPart(event *irc.Event) {
 | 
				
			||||||
	flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
					 | 
				
			||||||
	channel := event.Arguments[0]
 | 
						channel := event.Arguments[0]
 | 
				
			||||||
 | 
						if event.Code == "KICK" {
 | 
				
			||||||
 | 
							flog.Infof("Got kicked from %s by %s", channel, event.Nick)
 | 
				
			||||||
 | 
							b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if event.Code == "QUIT" {
 | 
						if event.Code == "QUIT" {
 | 
				
			||||||
		if event.Nick == b.Nick && strings.Contains(event.Raw, "Ping timeout") {
 | 
							if event.Nick == b.Nick && strings.Contains(event.Raw, "Ping timeout") {
 | 
				
			||||||
			flog.Infof("%s reconnecting ..", b.Account)
 | 
								flog.Infof("%s reconnecting ..", b.Account)
 | 
				
			||||||
@@ -182,6 +186,7 @@ func (b *Birc) handleJoinPart(event *irc.Event) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
				
			||||||
	b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
						b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
				
			||||||
	flog.Debugf("handle %#v", event)
 | 
						flog.Debugf("handle %#v", event)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,7 @@ func (b *Bmattermost) Connect() error {
 | 
				
			|||||||
		flog.Info("Connection succeeded")
 | 
							flog.Info("Connection succeeded")
 | 
				
			||||||
		b.TeamId = b.mc.GetTeamId()
 | 
							b.TeamId = b.mc.GetTeamId()
 | 
				
			||||||
		go b.mc.WsReceiver()
 | 
							go b.mc.WsReceiver()
 | 
				
			||||||
 | 
							go b.mc.StatusLoop()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	go b.handleMatter()
 | 
						go b.handleMatter()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@@ -96,15 +97,11 @@ 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}
 | 
				
			||||||
 | 
							matterMessage.IconURL = msg.Avatar
 | 
				
			||||||
		matterMessage.Channel = channel
 | 
							matterMessage.Channel = channel
 | 
				
			||||||
		matterMessage.UserName = nick
 | 
							matterMessage.UserName = nick
 | 
				
			||||||
		matterMessage.Type = ""
 | 
							matterMessage.Type = ""
 | 
				
			||||||
@@ -136,14 +133,26 @@ func (b *Bmattermost) handleMatter() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
					func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
				
			||||||
	for message := range b.mc.MessageChan {
 | 
						for message := range b.mc.MessageChan {
 | 
				
			||||||
 | 
							flog.Debugf("%#v", message.Raw.Data)
 | 
				
			||||||
 | 
							if message.Type == "system_join_leave" ||
 | 
				
			||||||
 | 
								message.Type == "system_join_channel" ||
 | 
				
			||||||
 | 
								message.Type == "system_leave_channel" {
 | 
				
			||||||
 | 
								flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
 | 
				
			||||||
 | 
								b.Remote <- config.Message{Username: "system", Text: message.Text, Channel: message.Channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		// do not post our own messages back to irc
 | 
							// do not post our own messages back to irc
 | 
				
			||||||
		// only listen to message from our team
 | 
							// only listen to message from our team
 | 
				
			||||||
		if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
 | 
							if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") &&
 | 
				
			||||||
 | 
								b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
 | 
				
			||||||
			flog.Debugf("Receiving from matterclient %#v", message)
 | 
								flog.Debugf("Receiving from matterclient %#v", message)
 | 
				
			||||||
			m := &MMMessage{}
 | 
								m := &MMMessage{}
 | 
				
			||||||
			m.Username = message.Username
 | 
								m.Username = message.Username
 | 
				
			||||||
			m.Channel = message.Channel
 | 
								m.Channel = message.Channel
 | 
				
			||||||
			m.Text = message.Text
 | 
								m.Text = message.Text
 | 
				
			||||||
 | 
								if message.Raw.Event == "post_edited" && !b.Config.EditDisable {
 | 
				
			||||||
 | 
									m.Text = message.Text + b.Config.EditSuffix
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if len(message.Post.FileIds) > 0 {
 | 
								if len(message.Post.FileIds) > 0 {
 | 
				
			||||||
				for _, link := range b.mc.GetPublicLinks(message.Post.FileIds) {
 | 
									for _, link := range b.mc.GetPublicLinks(message.Post.FileIds) {
 | 
				
			||||||
					m.Text = m.Text + "\n" + link
 | 
										m.Text = m.Text + "\n" + link
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,11 +73,17 @@ 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 {
 | 
				
			||||||
 | 
								if err.Error() != "name_taken" {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -195,6 +201,11 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
				
			|||||||
			// ignore first message
 | 
								// ignore first message
 | 
				
			||||||
			if count > 0 {
 | 
								if count > 0 {
 | 
				
			||||||
				flog.Debugf("Receiving from slackclient %#v", ev)
 | 
									flog.Debugf("Receiving from slackclient %#v", ev)
 | 
				
			||||||
 | 
									if !b.Config.EditDisable && ev.SubMessage != nil {
 | 
				
			||||||
 | 
										flog.Debugf("SubMessage %#v", ev.SubMessage)
 | 
				
			||||||
 | 
										ev.User = ev.SubMessage.User
 | 
				
			||||||
 | 
										ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				// use our own func because rtm.GetChannelInfo doesn't work for private channels
 | 
									// use our own func because rtm.GetChannelInfo doesn't work for private channels
 | 
				
			||||||
				channel, err := b.getChannelByID(ev.Channel)
 | 
									channel, err := b.getChannelByID(ev.Channel)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,28 +80,32 @@ 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 == "" {
 | 
					 | 
				
			||||||
					username = update.ChannelPost.From.UserName
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			}
 | 
							if update.EditedChannelPost != nil && !b.Config.EditDisable {
 | 
				
			||||||
			text = update.ChannelPost.Text
 | 
								message = update.EditedChannelPost
 | 
				
			||||||
			channel = strconv.FormatInt(update.ChannelPost.Chat.ID, 10)
 | 
								message.Text = message.Text + b.Config.EditSuffix
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// 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 update.EditedMessage != nil && !b.Config.EditDisable {
 | 
				
			||||||
 | 
								message = update.EditedMessage
 | 
				
			||||||
 | 
								message.Text = message.Text + b.Config.EditSuffix
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if message.From != nil {
 | 
				
			||||||
 | 
								username = message.From.FirstName
 | 
				
			||||||
			if username == "" {
 | 
								if username == "" {
 | 
				
			||||||
					username = update.Message.From.UserName
 | 
									username = message.From.UserName
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								text = message.Text
 | 
				
			||||||
 | 
								channel = strconv.FormatInt(message.Chat.ID, 10)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			text = update.Message.Text
 | 
					
 | 
				
			||||||
			channel = strconv.FormatInt(update.Message.Chat.ID, 10)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if username == "" {
 | 
							if username == "" {
 | 
				
			||||||
			username = "unknown"
 | 
								username = "unknown"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										42
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,45 @@
 | 
				
			|||||||
 | 
					# v0.12.0
 | 
				
			||||||
 | 
					## Changes
 | 
				
			||||||
 | 
					* general: edited messages are now being sent by default on discord/mattermost/telegram/slack. See "New Features"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					* general: add support for edited messages. 
 | 
				
			||||||
 | 
					  Add new keyword EditDisable (false/true), default false. Which means by default edited messages will be sent to other bridges.
 | 
				
			||||||
 | 
					  Add new keyword EditSuffix , default "". You can change this eg to "(edited)", this will be appended to every edit message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					* discord: Strip custom emoji metadata (discord). Closes #148
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v0.11.0
 | 
				
			||||||
 | 
					## 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
 | 
				
			||||||
 | 
					* telegram:  Support edited messages (telegram). See #141
 | 
				
			||||||
 | 
					* mattermost: Add support for showing/hiding join/leave messages from mattermost. Closes #147
 | 
				
			||||||
 | 
					* mattermost: Reconnect on session removal/timeout (mattermost)
 | 
				
			||||||
 | 
					* irc:  Rejoin channel when kicked (irc).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					* mattermost: Remove space after nick (mattermost). Closes #142
 | 
				
			||||||
 | 
					* mattermost: Modify iconurl correctly (mattermost).
 | 
				
			||||||
 | 
					* irc: Fix join/leave regression (irc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
						log.Infof("Starting gateway: %s", cfg.Name)
 | 
				
			||||||
 | 
						gw.Names[cfg.Name] = true
 | 
				
			||||||
func (gw *Gateway) Start() error {
 | 
						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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -88,6 +103,15 @@ func (gw *Gateway) handleReceive() {
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if msg.Event == config.EVENT_REJOIN_CHANNELS {
 | 
				
			||||||
 | 
									for _, br := range gw.Bridges {
 | 
				
			||||||
 | 
										if msg.Account == br.Account {
 | 
				
			||||||
 | 
											br.Joined = make(map[string]bool)
 | 
				
			||||||
 | 
											br.JoinChannels()
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if !gw.ignoreMessage(&msg) {
 | 
								if !gw.ignoreMessage(&msg) {
 | 
				
			||||||
				msg.Timestamp = time.Now()
 | 
									msg.Timestamp = time.Now()
 | 
				
			||||||
				for _, br := range gw.Bridges {
 | 
									for _, br := range gw.Bridges {
 | 
				
			||||||
@@ -109,45 +133,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.ChannelsOut = m
 | 
							gw.Channels[ID].GID[gw.Name] = true
 | 
				
			||||||
	m = nil
 | 
							gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
 | 
				
			||||||
	m = make(map[string][]string)
 | 
					 | 
				
			||||||
	for _, br := range gw.MyConfig.In {
 | 
					 | 
				
			||||||
		m[br.Account] = append(m[br.Account], br.Channel)
 | 
					 | 
				
			||||||
		options[br.Account+br.Channel] = br.Options
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gw.ChannelsIn = m
 | 
					
 | 
				
			||||||
	for _, br := range gw.MyConfig.InOut {
 | 
						for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) {
 | 
				
			||||||
		gw.ChannelsIn[br.Account] = append(gw.ChannelsIn[br.Account], br.Channel)
 | 
							ID := br.Channel + br.Account
 | 
				
			||||||
		gw.ChannelsOut[br.Account] = append(gw.ChannelsOut[br.Account], br.Channel)
 | 
							_, ok := gw.Channels[ID]
 | 
				
			||||||
		options[br.Account+br.Channel] = br.Options
 | 
							if !ok {
 | 
				
			||||||
 | 
								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.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 +186,20 @@ 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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	originchannel := msg.Channel
 | 
						// broadcast to every out channel (irc QUIT)
 | 
				
			||||||
	channels := gw.DestChannelFunc(&msg, dest.Account)
 | 
						if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
 | 
				
			||||||
	for _, channel := range channels {
 | 
					 | 
				
			||||||
		// do not send the message to the bridge we come from if also the channel is the same
 | 
					 | 
				
			||||||
		if msg.Account == dest.Account && channel == originchannel {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		msg.Channel = channel
 | 
					 | 
				
			||||||
		if msg.Channel == "" {
 | 
					 | 
				
			||||||
		log.Debug("empty channel")
 | 
							log.Debug("empty channel")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel)
 | 
						originchannel := msg.Channel
 | 
				
			||||||
 | 
						for _, channel := range gw.DestChannelFunc(&msg, *dest) {
 | 
				
			||||||
 | 
							// do not send to ourself
 | 
				
			||||||
 | 
							if channel.ID == getChannelID(msg) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
				
			||||||
 | 
							msg.Channel = channel.Name
 | 
				
			||||||
 | 
							gw.modifyAvatar(&msg, dest)
 | 
				
			||||||
		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 +226,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 +238,40 @@ 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 (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
 | 
				
			||||||
 | 
						iconurl := gw.Config.General.IconURL
 | 
				
			||||||
 | 
						if iconurl == "" {
 | 
				
			||||||
 | 
							iconurl = dest.Config.IconURL
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
 | 
				
			||||||
 | 
						if msg.Avatar == "" {
 | 
				
			||||||
 | 
							msg.Avatar = iconurl
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return gw.Start()
 | 
							gwconfigs = append(gwconfigs, gwconfig)
 | 
				
			||||||
}
 | 
						}
 | 
				
			||||||
 | 
						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.12.0"
 | 
				
			||||||
	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)
 | 
				
			||||||
 | 
						if strings.Contains(version, "-dev") {
 | 
				
			||||||
 | 
							log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	cfg := config.NewConfig(*flagConfig)
 | 
						cfg := config.NewConfig(*flagConfig)
 | 
				
			||||||
	for _, gw := range cfg.SameChannelGateway {
 | 
					 | 
				
			||||||
		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)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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)
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatalf("Starting gateway failed: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	err := g.Start()
 | 
						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)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
						log.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
				
			||||||
	select {}
 | 
						select {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -114,7 +115,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -157,7 +159,8 @@ IgnoreNicks="spammer1 spammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -197,7 +200,7 @@ IconURL="http://youricon.png"
 | 
				
			|||||||
#OPTIONAL
 | 
					#OPTIONAL
 | 
				
			||||||
useAPI=false
 | 
					useAPI=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#The mattermost hostname. 
 | 
					#The mattermost hostname. (do not prefix it with http or https)
 | 
				
			||||||
#REQUIRED (when useAPI=true)
 | 
					#REQUIRED (when useAPI=true)
 | 
				
			||||||
Server="yourmattermostserver.domain" 
 | 
					Server="yourmattermostserver.domain" 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -238,6 +241,14 @@ NicksPerRow=4
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
PrefixMessagesWithNick=false
 | 
					PrefixMessagesWithNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Disable sending of edits to other bridges
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					EditDisable=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Message to be appended to every edited message
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					EditSuffix=" (edited)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Nicks you want to ignore. 
 | 
					#Nicks you want to ignore. 
 | 
				
			||||||
#Messages from those users will not be sent to other bridges.
 | 
					#Messages from those users will not be sent to other bridges.
 | 
				
			||||||
#OPTIONAL
 | 
					#OPTIONAL
 | 
				
			||||||
@@ -250,7 +261,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -282,7 +294,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -342,6 +355,14 @@ NickFormatter="plain"
 | 
				
			|||||||
#OPTIONAL (default 4)
 | 
					#OPTIONAL (default 4)
 | 
				
			||||||
NicksPerRow=4
 | 
					NicksPerRow=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Disable sending of edits to other bridges
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					EditDisable=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Message to be appended to every edited message
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					EditSuffix=" (edited)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Whether to prefix messages from other bridges to mattermost with RemoteNickFormat
 | 
					#Whether to prefix messages from other bridges to mattermost with RemoteNickFormat
 | 
				
			||||||
#Useful if username overrides for incoming webhooks isn't enabled on the 
 | 
					#Useful if username overrides for incoming webhooks isn't enabled on the 
 | 
				
			||||||
#slack server. If you set PrefixMessagesWithNick to true, each message 
 | 
					#slack server. If you set PrefixMessagesWithNick to true, each message 
 | 
				
			||||||
@@ -362,7 +383,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -385,6 +407,14 @@ Token="Yourtokenhere"
 | 
				
			|||||||
#REQUIRED
 | 
					#REQUIRED
 | 
				
			||||||
Server="yourservername"
 | 
					Server="yourservername"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Disable sending of edits to other bridges
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					EditDisable=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Message to be appended to every edited message
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					EditSuffix=" (edited)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Nicks you want to ignore. 
 | 
					#Nicks you want to ignore. 
 | 
				
			||||||
#Messages from those users will not be sent to other bridges.
 | 
					#Messages from those users will not be sent to other bridges.
 | 
				
			||||||
#OPTIONAL
 | 
					#OPTIONAL
 | 
				
			||||||
@@ -397,7 +427,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -420,6 +451,14 @@ Token="Yourtokenhere"
 | 
				
			|||||||
#See https://core.telegram.org/bots/api#html-style
 | 
					#See https://core.telegram.org/bots/api#html-style
 | 
				
			||||||
MessageFormat=""
 | 
					MessageFormat=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Disable sending of edits to other bridges
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					EditDisable=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Message to be appended to every edited message
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					EditSuffix=" (edited)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Nicks you want to ignore. 
 | 
					#Nicks you want to ignore. 
 | 
				
			||||||
#Messages from those users will not be sent to other bridges.
 | 
					#Messages from those users will not be sent to other bridges.
 | 
				
			||||||
#OPTIONAL
 | 
					#OPTIONAL
 | 
				
			||||||
@@ -432,7 +471,8 @@ IgnoreNicks="spammer1 spammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -489,7 +529,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -532,7 +573,8 @@ IgnoreNicks="spammer1 spammer2"
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
 | 
					#Enable to show users joins/parts from other bridges 
 | 
				
			||||||
 | 
					#Only works hiding/show messages from irc and mattermost bridge for now
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -587,7 +629,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 +701,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"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
[mattermost]
 | 
					[mattermost]
 | 
				
			||||||
    [mattermost.work]
 | 
					    [mattermost.work]
 | 
				
			||||||
    useAPI=true
 | 
					    useAPI=true
 | 
				
			||||||
 | 
					    #do not prefix it wit http:// or https://
 | 
				
			||||||
    Server="yourmattermostserver.domain" 
 | 
					    Server="yourmattermostserver.domain" 
 | 
				
			||||||
    Team="yourteam"
 | 
					    Team="yourteam"
 | 
				
			||||||
    Login="yourlogin"
 | 
					    Login="yourlogin"
 | 
				
			||||||
@@ -30,3 +31,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" },
 | 
				
			||||||
 | 
					#]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,11 @@ import (
 | 
				
			|||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/cookiejar"
 | 
						"net/http/cookiejar"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -34,6 +36,7 @@ type Message struct {
 | 
				
			|||||||
	Channel  string
 | 
						Channel  string
 | 
				
			||||||
	Username string
 | 
						Username string
 | 
				
			||||||
	Text     string
 | 
						Text     string
 | 
				
			||||||
 | 
						Type     string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Team struct {
 | 
					type Team struct {
 | 
				
			||||||
@@ -60,6 +63,7 @@ type MMClient struct {
 | 
				
			|||||||
	WsConnected   bool
 | 
						WsConnected   bool
 | 
				
			||||||
	WsSequence    int64
 | 
						WsSequence    int64
 | 
				
			||||||
	WsPingChan    chan *model.WebSocketResponse
 | 
						WsPingChan    chan *model.WebSocketResponse
 | 
				
			||||||
 | 
						ServerVersion string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(login, pass, team, server string) *MMClient {
 | 
					func New(login, pass, team, server string) *MMClient {
 | 
				
			||||||
@@ -102,8 +106,27 @@ func (m *MMClient) Login() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	// login to mattermost
 | 
						// login to mattermost
 | 
				
			||||||
	m.Client = model.NewClient(uriScheme + m.Credentials.Server)
 | 
						m.Client = model.NewClient(uriScheme + m.Credentials.Server)
 | 
				
			||||||
	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
 | 
						m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, Proxy: http.ProxyFromEnvironment}
 | 
				
			||||||
	m.Client.HttpClient.Timeout = time.Second * 10
 | 
						m.Client.HttpClient.Timeout = time.Second * 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							d := b.Duration()
 | 
				
			||||||
 | 
							// bogus call to get the serverversion
 | 
				
			||||||
 | 
							m.Client.GetClientProperties()
 | 
				
			||||||
 | 
							if firstConnection && !supportedVersion(m.Client.ServerVersion) {
 | 
				
			||||||
 | 
								return fmt.Errorf("unsupported mattermost version: %s", m.Client.ServerVersion)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							m.ServerVersion = m.Client.ServerVersion
 | 
				
			||||||
 | 
							if m.ServerVersion == "" {
 | 
				
			||||||
 | 
								m.log.Debugf("Server not up yet, reconnecting in %s", d)
 | 
				
			||||||
 | 
								time.Sleep(d)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								m.log.Infof("Found version %s", m.ServerVersion)
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var myinfo *model.Result
 | 
						var myinfo *model.Result
 | 
				
			||||||
	var appErr *model.AppError
 | 
						var appErr *model.AppError
 | 
				
			||||||
	var logmsg = "trying login"
 | 
						var logmsg = "trying login"
 | 
				
			||||||
@@ -159,11 +182,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)
 | 
				
			||||||
@@ -177,6 +200,7 @@ func (m *MMClient) Login() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	b.Reset()
 | 
						b.Reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.log.Debug("WsClient: connected")
 | 
				
			||||||
	m.WsSequence = 1
 | 
						m.WsSequence = 1
 | 
				
			||||||
	m.WsPingChan = make(chan *model.WebSocketResponse)
 | 
						m.WsPingChan = make(chan *model.WebSocketResponse)
 | 
				
			||||||
	// only start to parse WS messages when login is completely done
 | 
						// only start to parse WS messages when login is completely done
 | 
				
			||||||
@@ -238,7 +262,7 @@ func (m *MMClient) WsReceiver() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) parseMessage(rmsg *Message) {
 | 
					func (m *MMClient) parseMessage(rmsg *Message) {
 | 
				
			||||||
	switch rmsg.Raw.Event {
 | 
						switch rmsg.Raw.Event {
 | 
				
			||||||
	case model.WEBSOCKET_EVENT_POSTED:
 | 
						case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED:
 | 
				
			||||||
		m.parseActionPost(rmsg)
 | 
							m.parseActionPost(rmsg)
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			case model.ACTION_USER_REMOVED:
 | 
								case model.ACTION_USER_REMOVED:
 | 
				
			||||||
@@ -266,7 +290,18 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	rmsg.Username = m.GetUser(data.UserId).Username
 | 
						rmsg.Username = m.GetUser(data.UserId).Username
 | 
				
			||||||
	rmsg.Channel = m.GetChannelName(data.ChannelId)
 | 
						rmsg.Channel = m.GetChannelName(data.ChannelId)
 | 
				
			||||||
	rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string))
 | 
						rmsg.Type = data.Type
 | 
				
			||||||
 | 
						teamid, _ := rmsg.Raw.Data["team_id"].(string)
 | 
				
			||||||
 | 
						// edit messsages have no team_id for some reason
 | 
				
			||||||
 | 
						if teamid == "" {
 | 
				
			||||||
 | 
							// we can find the team_id from the channelid
 | 
				
			||||||
 | 
							result, _ := m.Client.GetChannel(data.ChannelId, "")
 | 
				
			||||||
 | 
							teamid = result.Data.(*model.ChannelData).Channel.TeamId
 | 
				
			||||||
 | 
							rmsg.Raw.Data["team_id"] = teamid
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if teamid != "" {
 | 
				
			||||||
 | 
							rmsg.Team = m.GetTeamName(teamid)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// direct message
 | 
						// direct message
 | 
				
			||||||
	if rmsg.Raw.Data["channel_type"] == "D" {
 | 
						if rmsg.Raw.Data["channel_type"] == "D" {
 | 
				
			||||||
		rmsg.Channel = m.GetUser(data.UserId).Username
 | 
							rmsg.Channel = m.GetUser(data.UserId).Username
 | 
				
			||||||
@@ -292,7 +327,12 @@ func (m *MMClient) UpdateChannels() error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.New(err.DetailedError)
 | 
							return errors.New(err.DetailedError)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mmchannels2, err := m.Client.GetMoreChannels("")
 | 
						var mmchannels2 *model.Result
 | 
				
			||||||
 | 
						if m.mmVersion() >= 3.8 {
 | 
				
			||||||
 | 
							mmchannels2, err = m.Client.GetMoreChannelsPage(0, 5000)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							mmchannels2, err = m.Client.GetMoreChannels("")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.New(err.DetailedError)
 | 
							return errors.New(err.DetailedError)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -427,6 +467,14 @@ func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
					func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
				
			||||||
	m.log.Debugf("posting lastview %#v", channelId)
 | 
						m.log.Debugf("posting lastview %#v", channelId)
 | 
				
			||||||
 | 
						if m.mmVersion() >= 3.8 {
 | 
				
			||||||
 | 
							view := model.ChannelView{ChannelId: channelId}
 | 
				
			||||||
 | 
							res, _ := m.Client.ViewChannel(view)
 | 
				
			||||||
 | 
							if res == false {
 | 
				
			||||||
 | 
								m.log.Errorf("ChannelView update for %s failed", channelId)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	_, err := m.Client.UpdateLastViewedAt(channelId, true)
 | 
						_, err := m.Client.UpdateLastViewedAt(channelId, true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		m.log.Error(err)
 | 
							m.log.Error(err)
 | 
				
			||||||
@@ -628,6 +676,7 @@ func (m *MMClient) StatusLoop() {
 | 
				
			|||||||
				m.Logout()
 | 
									m.Logout()
 | 
				
			||||||
				m.WsQuit = false
 | 
									m.WsQuit = false
 | 
				
			||||||
				m.Login()
 | 
									m.Login()
 | 
				
			||||||
 | 
									go m.WsReceiver()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		time.Sleep(time.Second * 60)
 | 
							time.Sleep(time.Second * 60)
 | 
				
			||||||
@@ -659,7 +708,11 @@ func (m *MMClient) initUser() error {
 | 
				
			|||||||
			return errors.New(err.DetailedError)
 | 
								return errors.New(err.DetailedError)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		t.Channels = mmchannels.Data.(*model.ChannelList)
 | 
							t.Channels = mmchannels.Data.(*model.ChannelList)
 | 
				
			||||||
 | 
							if m.mmVersion() >= 3.8 {
 | 
				
			||||||
 | 
								mmchannels, err = m.Client.GetMoreChannelsPage(0, 5000)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			mmchannels, err = m.Client.GetMoreChannels("")
 | 
								mmchannels, err = m.Client.GetMoreChannels("")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return errors.New(err.DetailedError)
 | 
								return errors.New(err.DetailedError)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -687,3 +740,19 @@ func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) err
 | 
				
			|||||||
	m.WsClient.WriteJSON(req)
 | 
						m.WsClient.WriteJSON(req)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MMClient) mmVersion() float64 {
 | 
				
			||||||
 | 
						v, _ := strconv.ParseFloat(m.ServerVersion[0:3], 64)
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func supportedVersion(version string) bool {
 | 
				
			||||||
 | 
						if strings.HasPrefix(version, "3.5.0") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(version, "3.6.0") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(version, "3.7.0") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(version, "3.8.0") ||
 | 
				
			||||||
 | 
							strings.HasPrefix(version, "3.9.0") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										231
									
								
								vendor/github.com/BurntSushi/toml/lex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										231
									
								
								vendor/github.com/BurntSushi/toml/lex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -30,10 +30,13 @@ const (
 | 
				
			|||||||
	itemArrayTableEnd
 | 
						itemArrayTableEnd
 | 
				
			||||||
	itemKeyStart
 | 
						itemKeyStart
 | 
				
			||||||
	itemCommentStart
 | 
						itemCommentStart
 | 
				
			||||||
 | 
						itemInlineTableStart
 | 
				
			||||||
 | 
						itemInlineTableEnd
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	eof              = 0
 | 
						eof              = 0
 | 
				
			||||||
 | 
						comma            = ','
 | 
				
			||||||
	tableStart       = '['
 | 
						tableStart       = '['
 | 
				
			||||||
	tableEnd         = ']'
 | 
						tableEnd         = ']'
 | 
				
			||||||
	arrayTableStart  = '['
 | 
						arrayTableStart  = '['
 | 
				
			||||||
@@ -42,12 +45,13 @@ const (
 | 
				
			|||||||
	keySep           = '='
 | 
						keySep           = '='
 | 
				
			||||||
	arrayStart       = '['
 | 
						arrayStart       = '['
 | 
				
			||||||
	arrayEnd         = ']'
 | 
						arrayEnd         = ']'
 | 
				
			||||||
	arrayValTerm    = ','
 | 
					 | 
				
			||||||
	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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								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,21 +98,24 @@ 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()
 | 
				
			||||||
	defer entry.Logger.mu.Unlock()
 | 
							_, err = entry.Logger.Out.Write(serialized)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err = io.Copy(entry.Logger.Out, reader)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 | 
								fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							entry.Logger.mu.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 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
 | 
				
			||||||
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
 | 
						// panic() to use in Entry#Panic(), we avoid the allocation by checking
 | 
				
			||||||
@@ -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"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								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) {
 | 
				
			||||||
 | 
						case *os.File:
 | 
				
			||||||
 | 
							_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
 | 
				
			||||||
		return err == 0
 | 
							return err == 0
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								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) {
 | 
				
			||||||
 | 
						case *os.File:
 | 
				
			||||||
 | 
							_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
 | 
				
			||||||
		return err == nil
 | 
							return err == nil
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								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) {
 | 
				
			||||||
 | 
						case *os.File:
 | 
				
			||||||
		var st uint32
 | 
							var st uint32
 | 
				
			||||||
	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
 | 
							r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
 | 
				
			||||||
		return r != 0 && e == 0
 | 
							return r != 0 && e == 0
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								vendor/github.com/Sirupsen/logrus/text_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								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()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -23,6 +23,8 @@ import (
 | 
				
			|||||||
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)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								vendor/github.com/gorilla/schema/cache.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								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,7 +178,7 @@ 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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -184,6 +189,8 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
 | 
				
			|||||||
		name:     field.Name,
 | 
							name:     field.Name,
 | 
				
			||||||
		ss:       isSlice && isStruct,
 | 
							ss:       isSlice && isStruct,
 | 
				
			||||||
		alias:    alias,
 | 
							alias:    alias,
 | 
				
			||||||
 | 
							anon:     field.Anonymous,
 | 
				
			||||||
 | 
							required: options.Contains("required"),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -216,6 +223,8 @@ type fieldInfo struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										612
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										612
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,14 +13,24 @@ 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
 | 
				
			||||||
@@ -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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Message writer fields.
 | 
					 | 
				
			||||||
	writeErr       error
 | 
					 | 
				
			||||||
	writeBuf      []byte    // frame is constructed in this buffer.
 | 
						writeBuf      []byte    // frame is constructed in this buffer.
 | 
				
			||||||
	writePos       int    // end of data in writeBuf.
 | 
					 | 
				
			||||||
	writeFrameType int    // type of the current frame.
 | 
					 | 
				
			||||||
	writeSeq       int    // incremented to invalidate message writers.
 | 
					 | 
				
			||||||
	writeDeadline time.Time
 | 
						writeDeadline time.Time
 | 
				
			||||||
 | 
						writer        io.WriteCloser // the current writer returned to the application
 | 
				
			||||||
	isWriting     bool           // for best-effort concurrent write detection
 | 
						isWriting     bool           // for best-effort concurrent write detection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writeErrMu sync.Mutex
 | 
				
			||||||
 | 
						writeErr   error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enableWriteCompression bool
 | 
				
			||||||
 | 
						compressionLevel       int
 | 
				
			||||||
 | 
						newCompressionWriter   func(io.WriteCloser, int) io.WriteCloser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var br *bufio.Reader
 | 
				
			||||||
 | 
						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 br == nil {
 | 
				
			||||||
		if readBufferSize == 0 {
 | 
							if readBufferSize == 0 {
 | 
				
			||||||
			readBufferSize = defaultReadBufferSize
 | 
								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 {
 | 
							if writeBufferSize == 0 {
 | 
				
			||||||
			writeBufferSize = defaultWriteBufferSize
 | 
								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
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if c.writeFrameType != noFrame {
 | 
					 | 
				
			||||||
		if err := c.flushFrame(true, nil); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !isControl(messageType) && !isData(messageType) {
 | 
						mw := &messageWriter{
 | 
				
			||||||
		return nil, errBadWriteOpCode
 | 
							c:         c,
 | 
				
			||||||
 | 
							frameType: messageType,
 | 
				
			||||||
 | 
							pos:       maxFrameHeaderSize,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						c.writer = mw
 | 
				
			||||||
	c.writeFrameType = messageType
 | 
						if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
 | 
				
			||||||
	return messageWriter{c, c.writeSeq}, nil
 | 
							w := c.newCompressionWriter(c.writer, c.compressionLevel)
 | 
				
			||||||
 | 
							mw.compress = true
 | 
				
			||||||
 | 
							c.writer = w
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c.writer, 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))
 | 
				
			||||||
			closeText = string(payload[2:])
 | 
								if !isValidReceivedCloseCode(closeCode) {
 | 
				
			||||||
 | 
									return noFrame, c.handleProtocolError("invalid close code")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/websocket/examples/autobahn/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								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,10 +109,22 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if writeMessage {
 | 
							if writeMessage {
 | 
				
			||||||
 | 
								if !writePrepared {
 | 
				
			||||||
				err = conn.WriteMessage(mt, b)
 | 
									err = conn.WriteMessage(mt, b)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Println("WriteMessage:", err)
 | 
										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)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@@ -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()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										54
									
								
								vendor/github.com/gorilla/websocket/examples/chat/hub.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								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 {
 | 
				
			||||||
 | 
						return &Hub{
 | 
				
			||||||
		broadcast:  make(chan []byte),
 | 
							broadcast:  make(chan []byte),
 | 
				
			||||||
	register:    make(chan *connection),
 | 
							register:   make(chan *Client),
 | 
				
			||||||
	unregister:  make(chan *connection),
 | 
							unregister: make(chan *Client),
 | 
				
			||||||
	connections: make(map[*connection]bool),
 | 
							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)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,7 +12,6 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"text/template"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/websocket"
 | 
						"github.com/gorilla/websocket"
 | 
				
			||||||
@@ -21,7 +20,6 @@ import (
 | 
				
			|||||||
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
											
										
									
								
							
							
								
								
									
										20
									
								
								vendor/github.com/mattermost/platform/model/command_response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,7 +20,7 @@ type CommandResponse struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										175
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -49,10 +49,64 @@ 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 {
 | 
				
			||||||
@@ -67,7 +121,6 @@ type ServiceSettings struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -76,6 +129,7 @@ type ServiceSettings struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -92,6 +146,12 @@ type ServiceSettings struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -34,7 +34,7 @@ type IncomingWebhookRequest struct {
 | 
				
			|||||||
	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"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,6 +12,8 @@ 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
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										76
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,6 +7,7 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode/utf8"
 | 
						"unicode/utf8"
 | 
				
			||||||
@@ -15,6 +16,13 @@ import (
 | 
				
			|||||||
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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										137
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										137
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,8 +7,10 @@ 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"
 | 
				
			||||||
@@ -18,9 +20,23 @@ 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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -41,7 +41,7 @@ type AppError struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										126
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/pattlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/pattlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package log4go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
 | 
				
			||||||
 | 
						FORMAT_SHORT   = "[%t %d] [%L] %M"
 | 
				
			||||||
 | 
						FORMAT_ABBREV  = "[%L] %M"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type formatCacheType struct {
 | 
				
			||||||
 | 
						LastUpdateSeconds    int64
 | 
				
			||||||
 | 
						shortTime, shortDate string
 | 
				
			||||||
 | 
						longTime, longDate   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var formatCache = &formatCacheType{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Known format codes:
 | 
				
			||||||
 | 
					// %T - Time (15:04:05 MST)
 | 
				
			||||||
 | 
					// %t - Time (15:04)
 | 
				
			||||||
 | 
					// %D - Date (2006/01/02)
 | 
				
			||||||
 | 
					// %d - Date (01/02/06)
 | 
				
			||||||
 | 
					// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
 | 
				
			||||||
 | 
					// %S - Source
 | 
				
			||||||
 | 
					// %M - Message
 | 
				
			||||||
 | 
					// Ignores unknown formats
 | 
				
			||||||
 | 
					// Recommended: "[%D %T] [%L] (%S) %M"
 | 
				
			||||||
 | 
					func FormatLogRecord(format string, rec *LogRecord) string {
 | 
				
			||||||
 | 
						if rec == nil {
 | 
				
			||||||
 | 
							return "<nil>"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(format) == 0 {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := bytes.NewBuffer(make([]byte, 0, 64))
 | 
				
			||||||
 | 
						secs := rec.Created.UnixNano() / 1e9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cache := *formatCache
 | 
				
			||||||
 | 
						if cache.LastUpdateSeconds != secs {
 | 
				
			||||||
 | 
							month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
 | 
				
			||||||
 | 
							hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
 | 
				
			||||||
 | 
							zone, _ := rec.Created.Zone()
 | 
				
			||||||
 | 
							updated := &formatCacheType{
 | 
				
			||||||
 | 
								LastUpdateSeconds: secs,
 | 
				
			||||||
 | 
								shortTime:         fmt.Sprintf("%02d:%02d", hour, minute),
 | 
				
			||||||
 | 
								shortDate:         fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
 | 
				
			||||||
 | 
								longTime:          fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
 | 
				
			||||||
 | 
								longDate:          fmt.Sprintf("%04d/%02d/%02d", year, month, day),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cache = *updated
 | 
				
			||||||
 | 
							formatCache = updated
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Split the string into pieces by % signs
 | 
				
			||||||
 | 
						pieces := bytes.Split([]byte(format), []byte{'%'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Iterate over the pieces, replacing known formats
 | 
				
			||||||
 | 
						for i, piece := range pieces {
 | 
				
			||||||
 | 
							if i > 0 && len(piece) > 0 {
 | 
				
			||||||
 | 
								switch piece[0] {
 | 
				
			||||||
 | 
								case 'T':
 | 
				
			||||||
 | 
									out.WriteString(cache.longTime)
 | 
				
			||||||
 | 
								case 't':
 | 
				
			||||||
 | 
									out.WriteString(cache.shortTime)
 | 
				
			||||||
 | 
								case 'D':
 | 
				
			||||||
 | 
									out.WriteString(cache.longDate)
 | 
				
			||||||
 | 
								case 'd':
 | 
				
			||||||
 | 
									out.WriteString(cache.shortDate)
 | 
				
			||||||
 | 
								case 'L':
 | 
				
			||||||
 | 
									out.WriteString(levelStrings[rec.Level])
 | 
				
			||||||
 | 
								case 'S':
 | 
				
			||||||
 | 
									out.WriteString(rec.Source)
 | 
				
			||||||
 | 
								case 's':
 | 
				
			||||||
 | 
									slice := strings.Split(rec.Source, "/")
 | 
				
			||||||
 | 
									out.WriteString(slice[len(slice)-1])
 | 
				
			||||||
 | 
								case 'M':
 | 
				
			||||||
 | 
									out.WriteString(rec.Message)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(piece) > 1 {
 | 
				
			||||||
 | 
									out.Write(piece[1:])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if len(piece) > 0 {
 | 
				
			||||||
 | 
								out.Write(piece)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out.WriteByte('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the standard writer that prints to standard output.
 | 
				
			||||||
 | 
					type FormatLogWriter chan *LogRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This creates a new FormatLogWriter
 | 
				
			||||||
 | 
					func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
 | 
				
			||||||
 | 
						records := make(FormatLogWriter, LogBufferLength)
 | 
				
			||||||
 | 
						go records.run(out, format)
 | 
				
			||||||
 | 
						return records
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w FormatLogWriter) run(out io.Writer, format string) {
 | 
				
			||||||
 | 
						for rec := range w {
 | 
				
			||||||
 | 
							fmt.Fprint(out, FormatLogRecord(format, rec))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the FormatLogWriter's output method.  This will block if the output
 | 
				
			||||||
 | 
					// buffer is full.
 | 
				
			||||||
 | 
					func (w FormatLogWriter) LogWrite(rec *LogRecord) {
 | 
				
			||||||
 | 
						w <- rec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Close stops the logger from sending messages to standard output.  Attempts to
 | 
				
			||||||
 | 
					// send log messages to this logger after a Close have undefined behavior.
 | 
				
			||||||
 | 
					func (w FormatLogWriter) Close() {
 | 
				
			||||||
 | 
						close(w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/socklog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/socklog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package log4go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This log writer sends output to a socket
 | 
				
			||||||
 | 
					type SocketLogWriter chan *LogRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the SocketLogWriter's output method
 | 
				
			||||||
 | 
					func (w SocketLogWriter) LogWrite(rec *LogRecord) {
 | 
				
			||||||
 | 
						w <- rec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w SocketLogWriter) Close() {
 | 
				
			||||||
 | 
						close(w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
 | 
				
			||||||
 | 
						sock, err := net.Dial(proto, hostport)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer func() {
 | 
				
			||||||
 | 
								if sock != nil && proto == "tcp" {
 | 
				
			||||||
 | 
									sock.Close()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for rec := range w {
 | 
				
			||||||
 | 
								// Marshall into JSON
 | 
				
			||||||
 | 
								js, err := json.Marshal(rec)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_, err = sock.Write(js)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return w
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/termlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/termlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package log4go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var stdout io.Writer = os.Stdout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the standard writer that prints to standard output.
 | 
				
			||||||
 | 
					type ConsoleLogWriter struct {
 | 
				
			||||||
 | 
						format string
 | 
				
			||||||
 | 
						w      chan *LogRecord
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This creates a new ConsoleLogWriter
 | 
				
			||||||
 | 
					func NewConsoleLogWriter() *ConsoleLogWriter {
 | 
				
			||||||
 | 
						consoleWriter := &ConsoleLogWriter{
 | 
				
			||||||
 | 
							format: "[%T %D] [%L] (%S) %M",
 | 
				
			||||||
 | 
							w:      make(chan *LogRecord, LogBufferLength),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go consoleWriter.run(stdout)
 | 
				
			||||||
 | 
						return consoleWriter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c *ConsoleLogWriter) SetFormat(format string) {
 | 
				
			||||||
 | 
						c.format = format
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c *ConsoleLogWriter) run(out io.Writer) {
 | 
				
			||||||
 | 
						for rec := range c.w {
 | 
				
			||||||
 | 
							fmt.Fprint(out, FormatLogRecord(c.format, rec))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the ConsoleLogWriter's output method.  This will block if the output
 | 
				
			||||||
 | 
					// buffer is full.
 | 
				
			||||||
 | 
					func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
 | 
				
			||||||
 | 
						c.w <- rec
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Close stops the logger from sending messages to standard output.  Attempts to
 | 
				
			||||||
 | 
					// send log messages to this logger after a Close have undefined behavior.
 | 
				
			||||||
 | 
					func (c *ConsoleLogWriter) Close() {
 | 
				
			||||||
 | 
						close(c.w)
 | 
				
			||||||
 | 
						time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										278
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/wrapper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/wrapper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package log4go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						Global Logger
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						Global = NewDefaultLogger(DEBUG)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).LoadConfiguration
 | 
				
			||||||
 | 
					func LoadConfiguration(filename string) {
 | 
				
			||||||
 | 
						Global.LoadConfiguration(filename)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).AddFilter
 | 
				
			||||||
 | 
					func AddFilter(name string, lvl Level, writer LogWriter) {
 | 
				
			||||||
 | 
						Global.AddFilter(name, lvl, writer)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Close (closes and removes all logwriters)
 | 
				
			||||||
 | 
					func Close() {
 | 
				
			||||||
 | 
						Global.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Crash(args ...interface{}) {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic(args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Logs the given message and crashes the program
 | 
				
			||||||
 | 
					func Crashf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						Global.intLogf(CRITICAL, format, args...)
 | 
				
			||||||
 | 
						Global.Close() // so that hopefully the messages get logged
 | 
				
			||||||
 | 
						panic(fmt.Sprintf(format, args...))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Exit(args ...interface{}) {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Global.Close() // so that hopefully the messages get logged
 | 
				
			||||||
 | 
						os.Exit(0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Exitf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						Global.intLogf(ERROR, format, args...)
 | 
				
			||||||
 | 
						Global.Close() // so that hopefully the messages get logged
 | 
				
			||||||
 | 
						os.Exit(0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Stderr(args ...interface{}) {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Stderrf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						Global.intLogf(ERROR, format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Stdout(args ...interface{}) {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compatibility with `log`
 | 
				
			||||||
 | 
					func Stdoutf(format string, args ...interface{}) {
 | 
				
			||||||
 | 
						Global.intLogf(INFO, format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send a log message manually
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Log
 | 
				
			||||||
 | 
					func Log(lvl Level, source, message string) {
 | 
				
			||||||
 | 
						Global.Log(lvl, source, message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send a formatted log message easily
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Logf
 | 
				
			||||||
 | 
					func Logf(lvl Level, format string, args ...interface{}) {
 | 
				
			||||||
 | 
						Global.intLogf(lvl, format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send a closure log message
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Logc
 | 
				
			||||||
 | 
					func Logc(lvl Level, closure func() string) {
 | 
				
			||||||
 | 
						Global.intLogc(lvl, closure)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for finest log messages (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Finest
 | 
				
			||||||
 | 
					func Finest(arg0 interface{}, args ...interface{}) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = FINEST
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							Global.intLogc(lvl, first)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for fine log messages (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Fine
 | 
				
			||||||
 | 
					func Fine(arg0 interface{}, args ...interface{}) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = FINE
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							Global.intLogc(lvl, first)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for debug log messages
 | 
				
			||||||
 | 
					// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
 | 
				
			||||||
 | 
					// 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.
 | 
				
			||||||
 | 
					// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Debug
 | 
				
			||||||
 | 
					func Debug(arg0 interface{}, args ...interface{}) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = DEBUG
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							Global.intLogc(lvl, first)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for trace log messages (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Trace
 | 
				
			||||||
 | 
					func Trace(arg0 interface{}, args ...interface{}) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = TRACE
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							Global.intLogc(lvl, first)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for info log messages (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Info
 | 
				
			||||||
 | 
					func Info(arg0 interface{}, args ...interface{}) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = INFO
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							Global.intLogc(lvl, first)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// These functions will execute a closure exactly once, to build the error message for the return
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Warn
 | 
				
			||||||
 | 
					func Warn(arg0 interface{}, args ...interface{}) error {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = WARNING
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprintf(first, args...))
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							str := first()
 | 
				
			||||||
 | 
							Global.intLogf(lvl, "%s", str)
 | 
				
			||||||
 | 
							return errors.New(str)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// These functions will execute a closure exactly once, to build the error message for the return
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Error
 | 
				
			||||||
 | 
					func Error(arg0 interface{}, args ...interface{}) error {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = ERROR
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprintf(first, args...))
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							str := first()
 | 
				
			||||||
 | 
							Global.intLogf(lvl, "%s", str)
 | 
				
			||||||
 | 
							return errors.New(str)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
 | 
				
			||||||
 | 
					// These functions will execute a closure exactly once, to build the error message for the return
 | 
				
			||||||
 | 
					// Wrapper for (*Logger).Critical
 | 
				
			||||||
 | 
					func Critical(arg0 interface{}, args ...interface{}) error {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							lvl = CRITICAL
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch first := arg0.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							// Use the string as a format string
 | 
				
			||||||
 | 
							Global.intLogf(lvl, first, args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprintf(first, args...))
 | 
				
			||||||
 | 
						case func() string:
 | 
				
			||||||
 | 
							// Log the closure (no other arguments used)
 | 
				
			||||||
 | 
							str := first()
 | 
				
			||||||
 | 
							Global.intLogf(lvl, "%s", str)
 | 
				
			||||||
 | 
							return errors.New(str)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Build a format string so that it will be similar to Sprint
 | 
				
			||||||
 | 
							Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
 | 
				
			||||||
 | 
							return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										897
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										897
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/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/>.
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user