forked from lug/matterbridge
		
	Compare commits
	
		
			20 Commits
		
	
	
		
			v0.10.2-de
			...
			v0.11.0-be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 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
									
									
									
									
									
								
							| @@ -42,7 +42,7 @@ Accounts to one of the supported bridges | |||||||
| # Installing | # Installing | ||||||
| ## Binaries | ## Binaries | ||||||
| Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/) | Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/) | ||||||
| * Latest release [v0.10.1](https://github.com/42wim/matterbridge/releases/latest) | * Latest release [v0.10.3](https://github.com/42wim/matterbridge/releases/latest) | ||||||
|  |  | ||||||
| ## Building | ## Building | ||||||
| Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH) | Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH) | ||||||
| @@ -122,9 +122,11 @@ RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> " | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| # Running | # Running | ||||||
| 1) Copy the matterbridge.toml.sample to matterbridge.toml in the same directory as the matterbridge binary.   | 1) Copy the matterbridge.toml.sample to matterbridge.toml  | ||||||
| 2) Edit matterbridge.toml with the settings for your environment.  | 2) Edit matterbridge.toml with the settings for your environment.  | ||||||
| 3) Now you can run matterbridge.  (```./matterbridge```) | 3) Now you can run matterbridge.  (```./matterbridge```)    | ||||||
|  |  | ||||||
|  | (Matterbridge will only look for the config file in your current directory, if it isn't there specify -conf "/path/toyour/matterbridge.toml") | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| Usage of ./matterbridge: | Usage of ./matterbridge: | ||||||
| @@ -132,6 +134,8 @@ Usage of ./matterbridge: | |||||||
|         config file (default "matterbridge.toml") |         config file (default "matterbridge.toml") | ||||||
|   -debug |   -debug | ||||||
|         enable debug |         enable debug | ||||||
|  |   -gops | ||||||
|  |         enable gops agent | ||||||
|   -version |   -version | ||||||
|         show version |         show version | ||||||
| ``` | ``` | ||||||
| @@ -165,6 +169,7 @@ Matterbridge wouldn't exist without these libraries: | |||||||
| * discord - https://github.com/bwmarrin/discordgo | * discord - https://github.com/bwmarrin/discordgo | ||||||
| * echo - https://github.com/labstack/echo | * echo - https://github.com/labstack/echo | ||||||
| * gitter - https://github.com/sromku/go-gitter | * gitter - https://github.com/sromku/go-gitter | ||||||
|  | * gops - https://github.com/google/gops | ||||||
| * irc - https://github.com/thoj/go-ircevent | * irc - https://github.com/thoj/go-ircevent | ||||||
| * mattermost - https://github.com/mattermost/platform | * mattermost - https://github.com/mattermost/platform | ||||||
| * matrix - https://github.com/matrix-org/gomatrix | * matrix - https://github.com/matrix-org/gomatrix | ||||||
|   | |||||||
| @@ -27,23 +27,23 @@ type Bridger interface { | |||||||
| type Bridge struct { | type Bridge struct { | ||||||
| 	Config config.Protocol | 	Config config.Protocol | ||||||
| 	Bridger | 	Bridger | ||||||
| 	Name        string | 	Name     string | ||||||
| 	Account     string | 	Account  string | ||||||
| 	Protocol    string | 	Protocol string | ||||||
| 	ChannelsIn  map[string]config.ChannelOptions | 	Channels map[string]config.ChannelInfo | ||||||
| 	ChannelsOut map[string]config.ChannelOptions | 	Joined   map[string]bool | ||||||
| } | } | ||||||
|  |  | ||||||
| func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge { | func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge { | ||||||
| 	b := new(Bridge) | 	b := new(Bridge) | ||||||
| 	b.ChannelsIn = make(map[string]config.ChannelOptions) | 	b.Channels = make(map[string]config.ChannelInfo) | ||||||
| 	b.ChannelsOut = make(map[string]config.ChannelOptions) |  | ||||||
| 	accInfo := strings.Split(bridge.Account, ".") | 	accInfo := strings.Split(bridge.Account, ".") | ||||||
| 	protocol := accInfo[0] | 	protocol := accInfo[0] | ||||||
| 	name := accInfo[1] | 	name := accInfo[1] | ||||||
| 	b.Name = name | 	b.Name = name | ||||||
| 	b.Protocol = protocol | 	b.Protocol = protocol | ||||||
| 	b.Account = bridge.Account | 	b.Account = bridge.Account | ||||||
|  | 	b.Joined = make(map[string]bool) | ||||||
|  |  | ||||||
| 	// override config from environment | 	// override config from environment | ||||||
| 	config.OverrideCfgFromEnv(cfg, protocol, name) | 	config.OverrideCfgFromEnv(cfg, protocol, name) | ||||||
| @@ -83,33 +83,28 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bridge) JoinChannels() error { | func (b *Bridge) JoinChannels() error { | ||||||
| 	exists := make(map[string]bool) | 	err := b.joinChannels(b.Channels, b.Joined) | ||||||
| 	err := b.joinChannels(b.ChannelsIn, exists) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	err = b.joinChannels(b.ChannelsOut, exists) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bridge) joinChannels(cMap map[string]config.ChannelOptions, exists map[string]bool) error { | func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error { | ||||||
| 	mychannel := "" | 	mychannel := "" | ||||||
| 	for channel, info := range cMap { | 	for ID, channel := range channels { | ||||||
| 		if !exists[channel] { | 		if !exists[ID] { | ||||||
| 			mychannel = channel | 			mychannel = channel.Name | ||||||
| 			log.Infof("%s: joining %s", b.Account, channel) | 			log.Infof("%s: joining %s (%s)", b.Account, channel.Name, ID) | ||||||
| 			if b.Protocol == "irc" && info.Key != "" { | 			if b.Protocol == "irc" && channel.Options.Key != "" { | ||||||
| 				log.Debugf("using key %s for channel %s", info.Key, channel) | 				log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name) | ||||||
| 				mychannel = mychannel + " " + info.Key | 				mychannel = mychannel + " " + channel.Options.Key | ||||||
| 			} | 			} | ||||||
| 			err := b.JoinChannel(mychannel) | 			err := b.JoinChannel(channel.Name) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 			exists[channel] = true | 			exists[ID] = true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
|   | |||||||
| @@ -10,8 +10,9 @@ 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,6 +26,16 @@ type Message struct { | |||||||
| 	Timestamp time.Time | 	Timestamp time.Time | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type ChannelInfo struct { | ||||||
|  | 	Name        string | ||||||
|  | 	Account     string | ||||||
|  | 	Direction   string | ||||||
|  | 	ID          string | ||||||
|  | 	GID         map[string]bool | ||||||
|  | 	SameChannel map[string]bool | ||||||
|  | 	Options     ChannelOptions | ||||||
|  | } | ||||||
|  |  | ||||||
| type Protocol struct { | type Protocol struct { | ||||||
| 	BindAddress            string // mattermost, slack | 	BindAddress            string // mattermost, slack | ||||||
| 	Buffer                 int    // api | 	Buffer                 int    // api | ||||||
| @@ -63,9 +74,10 @@ type ChannelOptions struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type Bridge struct { | type Bridge struct { | ||||||
| 	Account string | 	Account     string | ||||||
| 	Channel string | 	Channel     string | ||||||
| 	Options ChannelOptions | 	Options     ChannelOptions | ||||||
|  | 	SameChannel bool | ||||||
| } | } | ||||||
|  |  | ||||||
| type Gateway struct { | type Gateway struct { | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ func (b *Birc) Connect() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Birc) Disconnect() error { | func (b *Birc) Disconnect() error { | ||||||
| 	b.i.Disconnect() | 	//b.i.Disconnect() | ||||||
| 	close(b.Local) | 	close(b.Local) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -167,14 +167,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 +187,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,6 +133,14 @@ 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" && b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId { | ||||||
|   | |||||||
| @@ -73,6 +73,10 @@ func (b *Bslack) Disconnect() error { | |||||||
| func (b *Bslack) JoinChannel(channel string) error { | func (b *Bslack) JoinChannel(channel string) error { | ||||||
| 	// we can only join channels using the API | 	// we can only join channels using the API | ||||||
| 	if b.Config.UseAPI { | 	if b.Config.UseAPI { | ||||||
|  | 		if strings.HasPrefix(b.Config.Token, "xoxb") { | ||||||
|  | 			// TODO check if bot has already joined channel | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
| 		_, err := b.sc.JoinChannel(channel) | 		_, err := b.sc.JoinChannel(channel) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
|   | |||||||
| @@ -80,28 +80,30 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | |||||||
| 	text := "" | 	text := "" | ||||||
| 	channel := "" | 	channel := "" | ||||||
| 	for update := range updates { | 	for update := range updates { | ||||||
|  | 		var message *tgbotapi.Message | ||||||
| 		// handle channels | 		// handle channels | ||||||
| 		if update.ChannelPost != nil { | 		if update.ChannelPost != nil { | ||||||
| 			if update.ChannelPost.From != nil { | 			message = update.ChannelPost | ||||||
| 				username = update.ChannelPost.From.FirstName | 		} | ||||||
| 				if username == "" { | 		if update.EditedChannelPost != nil { | ||||||
| 					username = update.ChannelPost.From.UserName | 			message = update.EditedChannelPost | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			text = update.ChannelPost.Text |  | ||||||
| 			channel = strconv.FormatInt(update.ChannelPost.Chat.ID, 10) |  | ||||||
| 		} | 		} | ||||||
| 		// handle groups | 		// handle groups | ||||||
| 		if update.Message != nil { | 		if update.Message != nil { | ||||||
| 			if update.Message.From != nil { | 			message = update.Message | ||||||
| 				username = update.Message.From.FirstName |  | ||||||
| 				if username == "" { |  | ||||||
| 					username = update.Message.From.UserName |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			text = update.Message.Text |  | ||||||
| 			channel = strconv.FormatInt(update.Message.Chat.ID, 10) |  | ||||||
| 		} | 		} | ||||||
|  | 		if update.EditedMessage != nil { | ||||||
|  | 			message = update.EditedMessage | ||||||
|  | 		} | ||||||
|  | 		if message.From != nil { | ||||||
|  | 			username = message.From.FirstName | ||||||
|  | 			if username == "" { | ||||||
|  | 				username = message.From.UserName | ||||||
|  | 			} | ||||||
|  | 			text = message.Text | ||||||
|  | 			channel = strconv.FormatInt(message.Chat.ID, 10) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if username == "" { | 		if username == "" { | ||||||
| 			username = "unknown" | 			username = "unknown" | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,33 @@ | |||||||
|  | # v0.11.0-dev | ||||||
|  | ## New features | ||||||
|  | * general: reusing the same account on multiple gateways now also reuses the connection. | ||||||
|  |   This is particuarly useful for irc. See #87 | ||||||
|  | * general: the Name is now REQUIRED and needs to be UNIQUE for each gateway configuration | ||||||
|  | * 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") | ||||||
| func (gw *Gateway) Start() error { | 	} | ||||||
|  | 	log.Infof("Starting gateway: %s", cfg.Name) | ||||||
|  | 	gw.Names[cfg.Name] = true | ||||||
|  | 	gw.Name = cfg.Name | ||||||
|  | 	gw.MyConfig = cfg | ||||||
| 	gw.mapChannels() | 	gw.mapChannels() | ||||||
| 	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) { | 	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) { | ||||||
| 		err := gw.AddBridge(&br) | 		err := gw.AddBridge(&br) | ||||||
| @@ -73,6 +76,18 @@ func (gw *Gateway) Start() error { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) { | ||||||
|  | 	for ID, channel := range gw.Channels { | ||||||
|  | 		if br.Account == channel.Account { | ||||||
|  | 			br.Channels[ID] = *channel | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (gw *Gateway) Start() error { | ||||||
| 	go gw.handleReceive() | 	go gw.handleReceive() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -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.Channels[ID].GID[gw.Name] = true | ||||||
|  | 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | ||||||
| 	} | 	} | ||||||
| 	gw.ChannelsOut = m |  | ||||||
| 	m = nil | 	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) { | ||||||
| 	m = make(map[string][]string) | 		ID := br.Channel + br.Account | ||||||
| 	for _, br := range gw.MyConfig.In { | 		_, ok := gw.Channels[ID] | ||||||
| 		m[br.Account] = append(m[br.Account], br.Channel) | 		if !ok { | ||||||
| 		options[br.Account+br.Channel] = br.Options | 			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account, | ||||||
|  | 				GID: make(map[string]bool), SameChannel: make(map[string]bool)} | ||||||
|  | 			channel.GID[gw.Name] = true | ||||||
|  | 			channel.SameChannel[gw.Name] = br.SameChannel | ||||||
|  | 			gw.Channels[channel.ID] = channel | ||||||
|  | 		} | ||||||
|  | 		gw.Channels[ID].GID[gw.Name] = true | ||||||
|  | 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | ||||||
| 	} | 	} | ||||||
| 	gw.ChannelsIn = m |  | ||||||
| 	for _, br := range gw.MyConfig.InOut { |  | ||||||
| 		gw.ChannelsIn[br.Account] = append(gw.ChannelsIn[br.Account], br.Channel) |  | ||||||
| 		gw.ChannelsOut[br.Account] = append(gw.ChannelsOut[br.Account], br.Channel) |  | ||||||
| 		options[br.Account+br.Channel] = br.Options |  | ||||||
| 	} |  | ||||||
| 	gw.ChannelOptions = options |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string { | func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo { | ||||||
| 	channels := gw.ChannelsIn[msg.Account] | 	var channels []config.ChannelInfo | ||||||
| 	// broadcast to every out channel (irc QUIT) | 	for _, channel := range gw.Channels { | ||||||
| 	if msg.Event == config.EVENT_JOIN_LEAVE && msg.Channel == "" { | 		if _, ok := gw.Channels[getChannelID(*msg)]; !ok { | ||||||
| 		return gw.ChannelsOut[dest] | 			continue | ||||||
| 	} | 		} | ||||||
| 	for _, channel := range channels { | 		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(*msg, channel) { | ||||||
| 		if channel == msg.Channel { | 			channels = append(channels, *channel) | ||||||
| 			return gw.ChannelsOut[dest] |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return []string{} | 	return channels | ||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | ||||||
| @@ -155,19 +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 | ||||||
| 	} | 	} | ||||||
|  | 	// broadcast to every out channel (irc QUIT) | ||||||
|  | 	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE { | ||||||
|  | 		log.Debug("empty channel") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	originchannel := msg.Channel | 	originchannel := msg.Channel | ||||||
| 	channels := gw.DestChannelFunc(&msg, dest.Account) | 	for _, channel := range gw.DestChannelFunc(&msg, *dest) { | ||||||
| 	for _, channel := range channels { | 		// do not send to ourself | ||||||
| 		// do not send the message to the bridge we come from if also the channel is the same | 		if channel.ID == getChannelID(msg) { | ||||||
| 		if msg.Account == dest.Account && channel == originchannel { |  | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		msg.Channel = channel | 		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name) | ||||||
| 		if msg.Channel == "" { | 		msg.Channel = channel.Name | ||||||
| 			log.Debug("empty channel") | 		gw.modifyAvatar(&msg, dest) | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel) |  | ||||||
| 		gw.modifyUsername(&msg, dest) | 		gw.modifyUsername(&msg, dest) | ||||||
| 		// for api we need originchannel as channel | 		// for api we need originchannel as channel | ||||||
| 		if dest.Protocol == "api" { | 		if dest.Protocol == "api" { | ||||||
| @@ -194,21 +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}) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  | 		gwconfigs = append(gwconfigs, gwconfig) | ||||||
| 	} | 	} | ||||||
| 	return gw.Start() | 	return gwconfigs | ||||||
| } |  | ||||||
|  |  | ||||||
| func (sgw *SameChannelGateway) validChannel(channel string) bool { |  | ||||||
| 	for _, c := range sgw.Channels { |  | ||||||
| 		if c == channel { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (sgw *SameChannelGateway) getDestChannel(msg *config.Message, dest string) []string { |  | ||||||
| 	if sgw.validChannel(msg.Channel) { |  | ||||||
| 		return []string{msg.Channel} |  | ||||||
| 	} |  | ||||||
| 	return []string{} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,10 +8,11 @@ import ( | |||||||
| 	"github.com/42wim/matterbridge/gateway/samechannel" | 	"github.com/42wim/matterbridge/gateway/samechannel" | ||||||
| 	log "github.com/Sirupsen/logrus" | 	log "github.com/Sirupsen/logrus" | ||||||
| 	"github.com/google/gops/agent" | 	"github.com/google/gops/agent" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	version = "0.10.2-dev" | 	version = "0.11.0-dev" | ||||||
| 	githash string | 	githash string | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -39,31 +40,26 @@ func main() { | |||||||
| 		log.SetLevel(log.DebugLevel) | 		log.SetLevel(log.DebugLevel) | ||||||
| 	} | 	} | ||||||
| 	log.Printf("Running version %s %s", version, githash) | 	log.Printf("Running version %s %s", version, githash) | ||||||
| 	cfg := config.NewConfig(*flagConfig) | 	if strings.Contains(version, "-dev") { | ||||||
| 	for _, gw := range cfg.SameChannelGateway { | 		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.") | ||||||
| 		if !gw.Enable { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		log.Printf("Starting samechannel gateway %#v", gw.Name) |  | ||||||
| 		g := samechannelgateway.New(cfg, &gw) |  | ||||||
| 		err := g.Start() |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatalf("Starting gateway failed %#v", err) |  | ||||||
| 		} |  | ||||||
| 		log.Printf("Started samechannel gateway %#v", gw.Name) |  | ||||||
| 	} | 	} | ||||||
|  | 	cfg := config.NewConfig(*flagConfig) | ||||||
|  |  | ||||||
| 	for _, gw := range cfg.Gateway { | 	g := gateway.New(cfg) | ||||||
|  | 	sgw := samechannelgateway.New(cfg) | ||||||
|  | 	gwconfigs := sgw.GetConfig() | ||||||
|  | 	for _, gw := range append(gwconfigs, cfg.Gateway...) { | ||||||
| 		if !gw.Enable { | 		if !gw.Enable { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		log.Printf("Starting gateway %#v", gw.Name) | 		err := g.AddConfig(&gw) | ||||||
| 		g := gateway.New(cfg, &gw) |  | ||||||
| 		err := g.Start() |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatalf("Starting gateway failed %#v", err) | 			log.Fatalf("Starting gateway failed: %s", err) | ||||||
| 		} | 		} | ||||||
| 		log.Printf("Started gateway %#v", gw.Name) | 	} | ||||||
|  | 	err := g.Start() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Starting gateway failed: %s", err) | ||||||
| 	} | 	} | ||||||
| 	log.Printf("Gateway(s) started succesfully. Now relaying messages") | 	log.Printf("Gateway(s) started succesfully. Now relaying messages") | ||||||
| 	select {} | 	select {} | ||||||
|   | |||||||
| @@ -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 | ||||||
|  |  | ||||||
| @@ -250,7 +253,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 +286,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 | ||||||
|  |  | ||||||
| @@ -362,7 +367,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 | ||||||
|  |  | ||||||
| @@ -397,7 +403,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 | ||||||
|  |  | ||||||
| @@ -432,7 +439,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 +497,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 +541,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 +597,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 +669,7 @@ enable=true | |||||||
| #channel testing on slack and vice versa. (and for the channel testing2 and testing3) | #channel testing on slack and vice versa. (and for the channel testing2 and testing3) | ||||||
|  |  | ||||||
| [[samechannelgateway]] | [[samechannelgateway]] | ||||||
|  |    name="samechannel1" | ||||||
|    enable = false |    enable = false | ||||||
|    accounts = [ "mattermost.work","slack.hobby" ] |    accounts = [ "mattermost.work","slack.hobby" ] | ||||||
|    channels = [ "testing","testing2","testing3"] |    channels = [ "testing","testing2","testing3"] | ||||||
|   | |||||||
| @@ -30,3 +30,12 @@ enable=true | |||||||
|     [[gateway.out]] |     [[gateway.out]] | ||||||
|     account="mattermost.work" |     account="mattermost.work" | ||||||
|     channel="off-topic" |     channel="off-topic" | ||||||
|  |  | ||||||
|  | #simpler config possible since v0.10.2 | ||||||
|  | #[[gateway]] | ||||||
|  | #name="gateway2" | ||||||
|  | #enable=true | ||||||
|  | #inout = [ | ||||||
|  | #    { account="irc.freenode", channel="#testing", options={key="channelkey"}}, | ||||||
|  | #    { account="mattermost.work", channel="off-topic" }, | ||||||
|  | #] | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ type Message struct { | |||||||
| 	Channel  string | 	Channel  string | ||||||
| 	Username string | 	Username string | ||||||
| 	Text     string | 	Text     string | ||||||
|  | 	Type     string | ||||||
| } | } | ||||||
|  |  | ||||||
| type Team struct { | type Team struct { | ||||||
| @@ -159,11 +160,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 +178,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 | ||||||
| @@ -266,6 +268,7 @@ 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.Type = data.Type | ||||||
| 	rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string)) | 	rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string)) | ||||||
| 	// direct message | 	// direct message | ||||||
| 	if rmsg.Raw.Data["channel_type"] == "D" { | 	if rmsg.Raw.Data["channel_type"] == "D" { | ||||||
| @@ -628,6 +631,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) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/BurntSushi/toml/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/BurntSushi/toml/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,7 +4,7 @@ files via reflection. There is also support for delaying decoding with | |||||||
| the Primitive type, and querying the set of keys in a TOML document with the | the Primitive type, and querying the set of keys in a TOML document with the | ||||||
| MetaData type. | MetaData type. | ||||||
|  |  | ||||||
| The specification implemented: https://github.com/mojombo/toml | The specification implemented: https://github.com/toml-lang/toml | ||||||
|  |  | ||||||
| The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify | The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify | ||||||
| whether a file is a valid TOML document. It can also be used to print the | whether a file is a valid TOML document. It can also be used to print the | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/BurntSushi/toml/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/BurntSushi/toml/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -241,7 +241,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { | |||||||
| func (enc *Encoder) eTable(key Key, rv reflect.Value) { | func (enc *Encoder) eTable(key Key, rv reflect.Value) { | ||||||
| 	panicIfInvalidKey(key) | 	panicIfInvalidKey(key) | ||||||
| 	if len(key) == 1 { | 	if len(key) == 1 { | ||||||
| 		// Output an extra new line between top-level tables. | 		// Output an extra newline between top-level tables. | ||||||
| 		// (The newline isn't written if nothing else has been written though.) | 		// (The newline isn't written if nothing else has been written though.) | ||||||
| 		enc.newline() | 		enc.newline() | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										259
									
								
								vendor/github.com/BurntSushi/toml/lex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										259
									
								
								vendor/github.com/BurntSushi/toml/lex.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -30,24 +30,28 @@ const ( | |||||||
| 	itemArrayTableEnd | 	itemArrayTableEnd | ||||||
| 	itemKeyStart | 	itemKeyStart | ||||||
| 	itemCommentStart | 	itemCommentStart | ||||||
|  | 	itemInlineTableStart | ||||||
|  | 	itemInlineTableEnd | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	eof             = 0 | 	eof              = 0 | ||||||
| 	tableStart      = '[' | 	comma            = ',' | ||||||
| 	tableEnd        = ']' | 	tableStart       = '[' | ||||||
| 	arrayTableStart = '[' | 	tableEnd         = ']' | ||||||
| 	arrayTableEnd   = ']' | 	arrayTableStart  = '[' | ||||||
| 	tableSep        = '.' | 	arrayTableEnd    = ']' | ||||||
| 	keySep          = '=' | 	tableSep         = '.' | ||||||
| 	arrayStart      = '[' | 	keySep           = '=' | ||||||
| 	arrayEnd        = ']' | 	arrayStart       = '[' | ||||||
| 	arrayValTerm    = ',' | 	arrayEnd         = ']' | ||||||
| 	commentStart    = '#' | 	commentStart     = '#' | ||||||
| 	stringStart     = '"' | 	stringStart      = '"' | ||||||
| 	stringEnd       = '"' | 	stringEnd        = '"' | ||||||
| 	rawStringStart  = '\'' | 	rawStringStart   = '\'' | ||||||
| 	rawStringEnd    = '\'' | 	rawStringEnd     = '\'' | ||||||
|  | 	inlineTableStart = '{' | ||||||
|  | 	inlineTableEnd   = '}' | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type stateFn func(lx *lexer) stateFn | type stateFn func(lx *lexer) stateFn | ||||||
| @@ -56,11 +60,18 @@ type lexer struct { | |||||||
| 	input string | 	input string | ||||||
| 	start int | 	start int | ||||||
| 	pos   int | 	pos   int | ||||||
| 	width int |  | ||||||
| 	line  int | 	line  int | ||||||
| 	state stateFn | 	state stateFn | ||||||
| 	items chan item | 	items chan item | ||||||
|  |  | ||||||
|  | 	// Allow for backing up up to three runes. | ||||||
|  | 	// This is necessary because TOML contains 3-rune tokens (""" and '''). | ||||||
|  | 	prevWidths [3]int | ||||||
|  | 	nprev      int // how many of prevWidths are in use | ||||||
|  | 	// If we emit an eof, we can still back up, but it is not OK to call | ||||||
|  | 	// next again. | ||||||
|  | 	atEOF bool | ||||||
|  |  | ||||||
| 	// A stack of state functions used to maintain context. | 	// A stack of state functions used to maintain context. | ||||||
| 	// The idea is to reuse parts of the state machine in various places. | 	// The idea is to reuse parts of the state machine in various places. | ||||||
| 	// For example, values can appear at the top level or within arbitrarily | 	// For example, values can appear at the top level or within arbitrarily | ||||||
| @@ -88,7 +99,7 @@ func (lx *lexer) nextItem() item { | |||||||
|  |  | ||||||
| func lex(input string) *lexer { | func lex(input string) *lexer { | ||||||
| 	lx := &lexer{ | 	lx := &lexer{ | ||||||
| 		input: input + "\n", | 		input: input, | ||||||
| 		state: lexTop, | 		state: lexTop, | ||||||
| 		line:  1, | 		line:  1, | ||||||
| 		items: make(chan item, 10), | 		items: make(chan item, 10), | ||||||
| @@ -103,7 +114,7 @@ func (lx *lexer) push(state stateFn) { | |||||||
|  |  | ||||||
| func (lx *lexer) pop() stateFn { | func (lx *lexer) pop() stateFn { | ||||||
| 	if len(lx.stack) == 0 { | 	if len(lx.stack) == 0 { | ||||||
| 		return lx.errorf("BUG in lexer: no states to pop.") | 		return lx.errorf("BUG in lexer: no states to pop") | ||||||
| 	} | 	} | ||||||
| 	last := lx.stack[len(lx.stack)-1] | 	last := lx.stack[len(lx.stack)-1] | ||||||
| 	lx.stack = lx.stack[0 : len(lx.stack)-1] | 	lx.stack = lx.stack[0 : len(lx.stack)-1] | ||||||
| @@ -125,16 +136,25 @@ func (lx *lexer) emitTrim(typ itemType) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (lx *lexer) next() (r rune) { | func (lx *lexer) next() (r rune) { | ||||||
|  | 	if lx.atEOF { | ||||||
|  | 		panic("next called after EOF") | ||||||
|  | 	} | ||||||
| 	if lx.pos >= len(lx.input) { | 	if lx.pos >= len(lx.input) { | ||||||
| 		lx.width = 0 | 		lx.atEOF = true | ||||||
| 		return eof | 		return eof | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if lx.input[lx.pos] == '\n' { | 	if lx.input[lx.pos] == '\n' { | ||||||
| 		lx.line++ | 		lx.line++ | ||||||
| 	} | 	} | ||||||
| 	r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:]) | 	lx.prevWidths[2] = lx.prevWidths[1] | ||||||
| 	lx.pos += lx.width | 	lx.prevWidths[1] = lx.prevWidths[0] | ||||||
|  | 	if lx.nprev < 3 { | ||||||
|  | 		lx.nprev++ | ||||||
|  | 	} | ||||||
|  | 	r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) | ||||||
|  | 	lx.prevWidths[0] = w | ||||||
|  | 	lx.pos += w | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -143,9 +163,20 @@ func (lx *lexer) ignore() { | |||||||
| 	lx.start = lx.pos | 	lx.start = lx.pos | ||||||
| } | } | ||||||
|  |  | ||||||
| // backup steps back one rune. Can be called only once per call of next. | // backup steps back one rune. Can be called only twice between calls to next. | ||||||
| func (lx *lexer) backup() { | func (lx *lexer) backup() { | ||||||
| 	lx.pos -= lx.width | 	if lx.atEOF { | ||||||
|  | 		lx.atEOF = false | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if lx.nprev < 1 { | ||||||
|  | 		panic("backed up too far") | ||||||
|  | 	} | ||||||
|  | 	w := lx.prevWidths[0] | ||||||
|  | 	lx.prevWidths[0] = lx.prevWidths[1] | ||||||
|  | 	lx.prevWidths[1] = lx.prevWidths[2] | ||||||
|  | 	lx.nprev-- | ||||||
|  | 	lx.pos -= w | ||||||
| 	if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { | 	if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { | ||||||
| 		lx.line-- | 		lx.line-- | ||||||
| 	} | 	} | ||||||
| @@ -182,7 +213,7 @@ func (lx *lexer) skip(pred func(rune) bool) { | |||||||
|  |  | ||||||
| // errorf stops all lexing by emitting an error and returning `nil`. | // errorf stops all lexing by emitting an error and returning `nil`. | ||||||
| // Note that any value that is a character is escaped if it's a special | // Note that any value that is a character is escaped if it's a special | ||||||
| // character (new lines, tabs, etc.). | // character (newlines, tabs, etc.). | ||||||
| func (lx *lexer) errorf(format string, values ...interface{}) stateFn { | func (lx *lexer) errorf(format string, values ...interface{}) stateFn { | ||||||
| 	lx.items <- item{ | 	lx.items <- item{ | ||||||
| 		itemError, | 		itemError, | ||||||
| @@ -198,7 +229,6 @@ func lexTop(lx *lexer) stateFn { | |||||||
| 	if isWhitespace(r) || isNL(r) { | 	if isWhitespace(r) || isNL(r) { | ||||||
| 		return lexSkip(lx, lexTop) | 		return lexSkip(lx, lexTop) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch r { | 	switch r { | ||||||
| 	case commentStart: | 	case commentStart: | ||||||
| 		lx.push(lexTop) | 		lx.push(lexTop) | ||||||
| @@ -207,7 +237,7 @@ func lexTop(lx *lexer) stateFn { | |||||||
| 		return lexTableStart | 		return lexTableStart | ||||||
| 	case eof: | 	case eof: | ||||||
| 		if lx.pos > lx.start { | 		if lx.pos > lx.start { | ||||||
| 			return lx.errorf("Unexpected EOF.") | 			return lx.errorf("unexpected EOF") | ||||||
| 		} | 		} | ||||||
| 		lx.emit(itemEOF) | 		lx.emit(itemEOF) | ||||||
| 		return nil | 		return nil | ||||||
| @@ -222,12 +252,12 @@ func lexTop(lx *lexer) stateFn { | |||||||
|  |  | ||||||
| // lexTopEnd is entered whenever a top-level item has been consumed. (A value | // lexTopEnd is entered whenever a top-level item has been consumed. (A value | ||||||
| // or a table.) It must see only whitespace, and will turn back to lexTop | // or a table.) It must see only whitespace, and will turn back to lexTop | ||||||
| // upon a new line. If it sees EOF, it will quit the lexer successfully. | // upon a newline. If it sees EOF, it will quit the lexer successfully. | ||||||
| func lexTopEnd(lx *lexer) stateFn { | func lexTopEnd(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
| 	case r == commentStart: | 	case r == commentStart: | ||||||
| 		// a comment will read to a new line for us. | 		// a comment will read to a newline for us. | ||||||
| 		lx.push(lexTop) | 		lx.push(lexTop) | ||||||
| 		return lexCommentStart | 		return lexCommentStart | ||||||
| 	case isWhitespace(r): | 	case isWhitespace(r): | ||||||
| @@ -236,11 +266,11 @@ func lexTopEnd(lx *lexer) stateFn { | |||||||
| 		lx.ignore() | 		lx.ignore() | ||||||
| 		return lexTop | 		return lexTop | ||||||
| 	case r == eof: | 	case r == eof: | ||||||
| 		lx.ignore() | 		lx.emit(itemEOF) | ||||||
| 		return lexTop | 		return nil | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Expected a top-level item to end with a new line, "+ | 	return lx.errorf("expected a top-level item to end with a newline, "+ | ||||||
| 		"comment or EOF, but got %q instead.", r) | 		"comment, or EOF, but got %q instead", r) | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexTable lexes the beginning of a table. Namely, it makes sure that | // lexTable lexes the beginning of a table. Namely, it makes sure that | ||||||
| @@ -267,8 +297,8 @@ func lexTableEnd(lx *lexer) stateFn { | |||||||
|  |  | ||||||
| func lexArrayTableEnd(lx *lexer) stateFn { | func lexArrayTableEnd(lx *lexer) stateFn { | ||||||
| 	if r := lx.next(); r != arrayTableEnd { | 	if r := lx.next(); r != arrayTableEnd { | ||||||
| 		return lx.errorf("Expected end of table array name delimiter %q, "+ | 		return lx.errorf("expected end of table array name delimiter %q, "+ | ||||||
| 			"but got %q instead.", arrayTableEnd, r) | 			"but got %q instead", arrayTableEnd, r) | ||||||
| 	} | 	} | ||||||
| 	lx.emit(itemArrayTableEnd) | 	lx.emit(itemArrayTableEnd) | ||||||
| 	return lexTopEnd | 	return lexTopEnd | ||||||
| @@ -278,11 +308,11 @@ func lexTableNameStart(lx *lexer) stateFn { | |||||||
| 	lx.skip(isWhitespace) | 	lx.skip(isWhitespace) | ||||||
| 	switch r := lx.peek(); { | 	switch r := lx.peek(); { | ||||||
| 	case r == tableEnd || r == eof: | 	case r == tableEnd || r == eof: | ||||||
| 		return lx.errorf("Unexpected end of table name. (Table names cannot " + | 		return lx.errorf("unexpected end of table name " + | ||||||
| 			"be empty.)") | 			"(table names cannot be empty)") | ||||||
| 	case r == tableSep: | 	case r == tableSep: | ||||||
| 		return lx.errorf("Unexpected table separator. (Table names cannot " + | 		return lx.errorf("unexpected table separator " + | ||||||
| 			"be empty.)") | 			"(table names cannot be empty)") | ||||||
| 	case r == stringStart || r == rawStringStart: | 	case r == stringStart || r == rawStringStart: | ||||||
| 		lx.ignore() | 		lx.ignore() | ||||||
| 		lx.push(lexTableNameEnd) | 		lx.push(lexTableNameEnd) | ||||||
| @@ -317,8 +347,8 @@ func lexTableNameEnd(lx *lexer) stateFn { | |||||||
| 	case r == tableEnd: | 	case r == tableEnd: | ||||||
| 		return lx.pop() | 		return lx.pop() | ||||||
| 	default: | 	default: | ||||||
| 		return lx.errorf("Expected '.' or ']' to end table name, but got %q "+ | 		return lx.errorf("expected '.' or ']' to end table name, "+ | ||||||
| 			"instead.", r) | 			"but got %q instead", r) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -328,7 +358,7 @@ func lexKeyStart(lx *lexer) stateFn { | |||||||
| 	r := lx.peek() | 	r := lx.peek() | ||||||
| 	switch { | 	switch { | ||||||
| 	case r == keySep: | 	case r == keySep: | ||||||
| 		return lx.errorf("Unexpected key separator %q.", keySep) | 		return lx.errorf("unexpected key separator %q", keySep) | ||||||
| 	case isWhitespace(r) || isNL(r): | 	case isWhitespace(r) || isNL(r): | ||||||
| 		lx.next() | 		lx.next() | ||||||
| 		return lexSkip(lx, lexKeyStart) | 		return lexSkip(lx, lexKeyStart) | ||||||
| @@ -359,7 +389,7 @@ func lexBareKey(lx *lexer) stateFn { | |||||||
| 		lx.emit(itemText) | 		lx.emit(itemText) | ||||||
| 		return lexKeyEnd | 		return lexKeyEnd | ||||||
| 	default: | 	default: | ||||||
| 		return lx.errorf("Bare keys cannot contain %q.", r) | 		return lx.errorf("bare keys cannot contain %q", r) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -372,7 +402,7 @@ func lexKeyEnd(lx *lexer) stateFn { | |||||||
| 	case isWhitespace(r): | 	case isWhitespace(r): | ||||||
| 		return lexSkip(lx, lexKeyEnd) | 		return lexSkip(lx, lexKeyEnd) | ||||||
| 	default: | 	default: | ||||||
| 		return lx.errorf("Expected key separator %q, but got %q instead.", | 		return lx.errorf("expected key separator %q, but got %q instead", | ||||||
| 			keySep, r) | 			keySep, r) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -381,9 +411,8 @@ func lexKeyEnd(lx *lexer) stateFn { | |||||||
| // lexValue will ignore whitespace. | // lexValue will ignore whitespace. | ||||||
| // After a value is lexed, the last state on the next is popped and returned. | // After a value is lexed, the last state on the next is popped and returned. | ||||||
| func lexValue(lx *lexer) stateFn { | func lexValue(lx *lexer) stateFn { | ||||||
| 	// We allow whitespace to precede a value, but NOT new lines. | 	// We allow whitespace to precede a value, but NOT newlines. | ||||||
| 	// In array syntax, the array states are responsible for ignoring new | 	// In array syntax, the array states are responsible for ignoring newlines. | ||||||
| 	// lines. |  | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
| 	case isWhitespace(r): | 	case isWhitespace(r): | ||||||
| @@ -397,6 +426,10 @@ func lexValue(lx *lexer) stateFn { | |||||||
| 		lx.ignore() | 		lx.ignore() | ||||||
| 		lx.emit(itemArray) | 		lx.emit(itemArray) | ||||||
| 		return lexArrayValue | 		return lexArrayValue | ||||||
|  | 	case inlineTableStart: | ||||||
|  | 		lx.ignore() | ||||||
|  | 		lx.emit(itemInlineTableStart) | ||||||
|  | 		return lexInlineTableValue | ||||||
| 	case stringStart: | 	case stringStart: | ||||||
| 		if lx.accept(stringStart) { | 		if lx.accept(stringStart) { | ||||||
| 			if lx.accept(stringStart) { | 			if lx.accept(stringStart) { | ||||||
| @@ -420,7 +453,7 @@ func lexValue(lx *lexer) stateFn { | |||||||
| 	case '+', '-': | 	case '+', '-': | ||||||
| 		return lexNumberStart | 		return lexNumberStart | ||||||
| 	case '.': // special error case, be kind to users | 	case '.': // special error case, be kind to users | ||||||
| 		return lx.errorf("Floats must start with a digit, not '.'.") | 		return lx.errorf("floats must start with a digit, not '.'") | ||||||
| 	} | 	} | ||||||
| 	if unicode.IsLetter(r) { | 	if unicode.IsLetter(r) { | ||||||
| 		// Be permissive here; lexBool will give a nice error if the | 		// Be permissive here; lexBool will give a nice error if the | ||||||
| @@ -430,11 +463,11 @@ func lexValue(lx *lexer) stateFn { | |||||||
| 		lx.backup() | 		lx.backup() | ||||||
| 		return lexBool | 		return lexBool | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Expected value but found %q instead.", r) | 	return lx.errorf("expected value but found %q instead", r) | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexArrayValue consumes one value in an array. It assumes that '[' or ',' | // lexArrayValue consumes one value in an array. It assumes that '[' or ',' | ||||||
| // have already been consumed. All whitespace and new lines are ignored. | // have already been consumed. All whitespace and newlines are ignored. | ||||||
| func lexArrayValue(lx *lexer) stateFn { | func lexArrayValue(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
| @@ -443,10 +476,11 @@ func lexArrayValue(lx *lexer) stateFn { | |||||||
| 	case r == commentStart: | 	case r == commentStart: | ||||||
| 		lx.push(lexArrayValue) | 		lx.push(lexArrayValue) | ||||||
| 		return lexCommentStart | 		return lexCommentStart | ||||||
| 	case r == arrayValTerm: | 	case r == comma: | ||||||
| 		return lx.errorf("Unexpected array value terminator %q.", | 		return lx.errorf("unexpected comma") | ||||||
| 			arrayValTerm) |  | ||||||
| 	case r == arrayEnd: | 	case r == arrayEnd: | ||||||
|  | 		// NOTE(caleb): The spec isn't clear about whether you can have | ||||||
|  | 		// a trailing comma or not, so we'll allow it. | ||||||
| 		return lexArrayEnd | 		return lexArrayEnd | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -455,8 +489,9 @@ func lexArrayValue(lx *lexer) stateFn { | |||||||
| 	return lexValue | 	return lexValue | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexArrayValueEnd consumes the cruft between values of an array. Namely, | // lexArrayValueEnd consumes everything between the end of an array value and | ||||||
| // it ignores whitespace and expects either a ',' or a ']'. | // the next value (or the end of the array): it ignores whitespace and newlines | ||||||
|  | // and expects either a ',' or a ']'. | ||||||
| func lexArrayValueEnd(lx *lexer) stateFn { | func lexArrayValueEnd(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
| @@ -465,31 +500,88 @@ func lexArrayValueEnd(lx *lexer) stateFn { | |||||||
| 	case r == commentStart: | 	case r == commentStart: | ||||||
| 		lx.push(lexArrayValueEnd) | 		lx.push(lexArrayValueEnd) | ||||||
| 		return lexCommentStart | 		return lexCommentStart | ||||||
| 	case r == arrayValTerm: | 	case r == comma: | ||||||
| 		lx.ignore() | 		lx.ignore() | ||||||
| 		return lexArrayValue // move on to the next value | 		return lexArrayValue // move on to the next value | ||||||
| 	case r == arrayEnd: | 	case r == arrayEnd: | ||||||
| 		return lexArrayEnd | 		return lexArrayEnd | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Expected an array value terminator %q or an array "+ | 	return lx.errorf( | ||||||
| 		"terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r) | 		"expected a comma or array terminator %q, but got %q instead", | ||||||
|  | 		arrayEnd, r, | ||||||
|  | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexArrayEnd finishes the lexing of an array. It assumes that a ']' has | // lexArrayEnd finishes the lexing of an array. | ||||||
| // just been consumed. | // It assumes that a ']' has just been consumed. | ||||||
| func lexArrayEnd(lx *lexer) stateFn { | func lexArrayEnd(lx *lexer) stateFn { | ||||||
| 	lx.ignore() | 	lx.ignore() | ||||||
| 	lx.emit(itemArrayEnd) | 	lx.emit(itemArrayEnd) | ||||||
| 	return lx.pop() | 	return lx.pop() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // lexInlineTableValue consumes one key/value pair in an inline table. | ||||||
|  | // It assumes that '{' or ',' have already been consumed. Whitespace is ignored. | ||||||
|  | func lexInlineTableValue(lx *lexer) stateFn { | ||||||
|  | 	r := lx.next() | ||||||
|  | 	switch { | ||||||
|  | 	case isWhitespace(r): | ||||||
|  | 		return lexSkip(lx, lexInlineTableValue) | ||||||
|  | 	case isNL(r): | ||||||
|  | 		return lx.errorf("newlines not allowed within inline tables") | ||||||
|  | 	case r == commentStart: | ||||||
|  | 		lx.push(lexInlineTableValue) | ||||||
|  | 		return lexCommentStart | ||||||
|  | 	case r == comma: | ||||||
|  | 		return lx.errorf("unexpected comma") | ||||||
|  | 	case r == inlineTableEnd: | ||||||
|  | 		return lexInlineTableEnd | ||||||
|  | 	} | ||||||
|  | 	lx.backup() | ||||||
|  | 	lx.push(lexInlineTableValueEnd) | ||||||
|  | 	return lexKeyStart | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // lexInlineTableValueEnd consumes everything between the end of an inline table | ||||||
|  | // key/value pair and the next pair (or the end of the table): | ||||||
|  | // it ignores whitespace and expects either a ',' or a '}'. | ||||||
|  | func lexInlineTableValueEnd(lx *lexer) stateFn { | ||||||
|  | 	r := lx.next() | ||||||
|  | 	switch { | ||||||
|  | 	case isWhitespace(r): | ||||||
|  | 		return lexSkip(lx, lexInlineTableValueEnd) | ||||||
|  | 	case isNL(r): | ||||||
|  | 		return lx.errorf("newlines not allowed within inline tables") | ||||||
|  | 	case r == commentStart: | ||||||
|  | 		lx.push(lexInlineTableValueEnd) | ||||||
|  | 		return lexCommentStart | ||||||
|  | 	case r == comma: | ||||||
|  | 		lx.ignore() | ||||||
|  | 		return lexInlineTableValue | ||||||
|  | 	case r == inlineTableEnd: | ||||||
|  | 		return lexInlineTableEnd | ||||||
|  | 	} | ||||||
|  | 	return lx.errorf("expected a comma or an inline table terminator %q, "+ | ||||||
|  | 		"but got %q instead", inlineTableEnd, r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // lexInlineTableEnd finishes the lexing of an inline table. | ||||||
|  | // It assumes that a '}' has just been consumed. | ||||||
|  | func lexInlineTableEnd(lx *lexer) stateFn { | ||||||
|  | 	lx.ignore() | ||||||
|  | 	lx.emit(itemInlineTableEnd) | ||||||
|  | 	return lx.pop() | ||||||
|  | } | ||||||
|  |  | ||||||
| // lexString consumes the inner contents of a string. It assumes that the | // lexString consumes the inner contents of a string. It assumes that the | ||||||
| // beginning '"' has already been consumed and ignored. | // beginning '"' has already been consumed and ignored. | ||||||
| func lexString(lx *lexer) stateFn { | func lexString(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
|  | 	case r == eof: | ||||||
|  | 		return lx.errorf("unexpected EOF") | ||||||
| 	case isNL(r): | 	case isNL(r): | ||||||
| 		return lx.errorf("Strings cannot contain new lines.") | 		return lx.errorf("strings cannot contain newlines") | ||||||
| 	case r == '\\': | 	case r == '\\': | ||||||
| 		lx.push(lexString) | 		lx.push(lexString) | ||||||
| 		return lexStringEscape | 		return lexStringEscape | ||||||
| @@ -506,11 +598,12 @@ func lexString(lx *lexer) stateFn { | |||||||
| // lexMultilineString consumes the inner contents of a string. It assumes that | // lexMultilineString consumes the inner contents of a string. It assumes that | ||||||
| // the beginning '"""' has already been consumed and ignored. | // the beginning '"""' has already been consumed and ignored. | ||||||
| func lexMultilineString(lx *lexer) stateFn { | func lexMultilineString(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	switch lx.next() { | ||||||
| 	switch { | 	case eof: | ||||||
| 	case r == '\\': | 		return lx.errorf("unexpected EOF") | ||||||
|  | 	case '\\': | ||||||
| 		return lexMultilineStringEscape | 		return lexMultilineStringEscape | ||||||
| 	case r == stringEnd: | 	case stringEnd: | ||||||
| 		if lx.accept(stringEnd) { | 		if lx.accept(stringEnd) { | ||||||
| 			if lx.accept(stringEnd) { | 			if lx.accept(stringEnd) { | ||||||
| 				lx.backup() | 				lx.backup() | ||||||
| @@ -534,8 +627,10 @@ func lexMultilineString(lx *lexer) stateFn { | |||||||
| func lexRawString(lx *lexer) stateFn { | func lexRawString(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	switch { | 	switch { | ||||||
|  | 	case r == eof: | ||||||
|  | 		return lx.errorf("unexpected EOF") | ||||||
| 	case isNL(r): | 	case isNL(r): | ||||||
| 		return lx.errorf("Strings cannot contain new lines.") | 		return lx.errorf("strings cannot contain newlines") | ||||||
| 	case r == rawStringEnd: | 	case r == rawStringEnd: | ||||||
| 		lx.backup() | 		lx.backup() | ||||||
| 		lx.emit(itemRawString) | 		lx.emit(itemRawString) | ||||||
| @@ -547,12 +642,13 @@ func lexRawString(lx *lexer) stateFn { | |||||||
| } | } | ||||||
|  |  | ||||||
| // lexMultilineRawString consumes a raw string. Nothing can be escaped in such | // lexMultilineRawString consumes a raw string. Nothing can be escaped in such | ||||||
| // a string. It assumes that the beginning "'" has already been consumed and | // a string. It assumes that the beginning "'''" has already been consumed and | ||||||
| // ignored. | // ignored. | ||||||
| func lexMultilineRawString(lx *lexer) stateFn { | func lexMultilineRawString(lx *lexer) stateFn { | ||||||
| 	r := lx.next() | 	switch lx.next() { | ||||||
| 	switch { | 	case eof: | ||||||
| 	case r == rawStringEnd: | 		return lx.errorf("unexpected EOF") | ||||||
|  | 	case rawStringEnd: | ||||||
| 		if lx.accept(rawStringEnd) { | 		if lx.accept(rawStringEnd) { | ||||||
| 			if lx.accept(rawStringEnd) { | 			if lx.accept(rawStringEnd) { | ||||||
| 				lx.backup() | 				lx.backup() | ||||||
| @@ -605,10 +701,9 @@ func lexStringEscape(lx *lexer) stateFn { | |||||||
| 	case 'U': | 	case 'U': | ||||||
| 		return lexLongUnicodeEscape | 		return lexLongUnicodeEscape | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Invalid escape character %q. Only the following "+ | 	return lx.errorf("invalid escape character %q; only the following "+ | ||||||
| 		"escape characters are allowed: "+ | 		"escape characters are allowed: "+ | ||||||
| 		"\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, "+ | 		`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) | ||||||
| 		"\\uXXXX and \\UXXXXXXXX.", r) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func lexShortUnicodeEscape(lx *lexer) stateFn { | func lexShortUnicodeEscape(lx *lexer) stateFn { | ||||||
| @@ -616,8 +711,8 @@ func lexShortUnicodeEscape(lx *lexer) stateFn { | |||||||
| 	for i := 0; i < 4; i++ { | 	for i := 0; i < 4; i++ { | ||||||
| 		r = lx.next() | 		r = lx.next() | ||||||
| 		if !isHexadecimal(r) { | 		if !isHexadecimal(r) { | ||||||
| 			return lx.errorf("Expected four hexadecimal digits after '\\u', "+ | 			return lx.errorf(`expected four hexadecimal digits after '\u', `+ | ||||||
| 				"but got '%s' instead.", lx.current()) | 				"but got %q instead", lx.current()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return lx.pop() | 	return lx.pop() | ||||||
| @@ -628,8 +723,8 @@ func lexLongUnicodeEscape(lx *lexer) stateFn { | |||||||
| 	for i := 0; i < 8; i++ { | 	for i := 0; i < 8; i++ { | ||||||
| 		r = lx.next() | 		r = lx.next() | ||||||
| 		if !isHexadecimal(r) { | 		if !isHexadecimal(r) { | ||||||
| 			return lx.errorf("Expected eight hexadecimal digits after '\\U', "+ | 			return lx.errorf(`expected eight hexadecimal digits after '\U', `+ | ||||||
| 				"but got '%s' instead.", lx.current()) | 				"but got %q instead", lx.current()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return lx.pop() | 	return lx.pop() | ||||||
| @@ -647,9 +742,9 @@ func lexNumberOrDateStart(lx *lexer) stateFn { | |||||||
| 	case 'e', 'E': | 	case 'e', 'E': | ||||||
| 		return lexFloat | 		return lexFloat | ||||||
| 	case '.': | 	case '.': | ||||||
| 		return lx.errorf("Floats must start with a digit, not '.'.") | 		return lx.errorf("floats must start with a digit, not '.'") | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Expected a digit but got %q.", r) | 	return lx.errorf("expected a digit but got %q", r) | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexNumberOrDate consumes either an integer, float or datetime. | // lexNumberOrDate consumes either an integer, float or datetime. | ||||||
| @@ -697,9 +792,9 @@ func lexNumberStart(lx *lexer) stateFn { | |||||||
| 	r := lx.next() | 	r := lx.next() | ||||||
| 	if !isDigit(r) { | 	if !isDigit(r) { | ||||||
| 		if r == '.' { | 		if r == '.' { | ||||||
| 			return lx.errorf("Floats must start with a digit, not '.'.") | 			return lx.errorf("floats must start with a digit, not '.'") | ||||||
| 		} | 		} | ||||||
| 		return lx.errorf("Expected a digit but got %q.", r) | 		return lx.errorf("expected a digit but got %q", r) | ||||||
| 	} | 	} | ||||||
| 	return lexNumber | 	return lexNumber | ||||||
| } | } | ||||||
| @@ -757,7 +852,7 @@ func lexBool(lx *lexer) stateFn { | |||||||
| 		lx.emit(itemBool) | 		lx.emit(itemBool) | ||||||
| 		return lx.pop() | 		return lx.pop() | ||||||
| 	} | 	} | ||||||
| 	return lx.errorf("Expected value but found %q instead.", s) | 	return lx.errorf("expected value but found %q instead", s) | ||||||
| } | } | ||||||
|  |  | ||||||
| // lexCommentStart begins the lexing of a comment. It will emit | // lexCommentStart begins the lexing of a comment. It will emit | ||||||
| @@ -769,7 +864,7 @@ func lexCommentStart(lx *lexer) stateFn { | |||||||
| } | } | ||||||
|  |  | ||||||
| // lexComment lexes an entire comment. It assumes that '#' has been consumed. | // lexComment lexes an entire comment. It assumes that '#' has been consumed. | ||||||
| // It will consume *up to* the first new line character, and pass control | // It will consume *up to* the first newline character, and pass control | ||||||
| // back to the last state on the stack. | // back to the last state on the stack. | ||||||
| func lexComment(lx *lexer) stateFn { | func lexComment(lx *lexer) stateFn { | ||||||
| 	r := lx.peek() | 	r := lx.peek() | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								vendor/github.com/BurntSushi/toml/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/BurntSushi/toml/parse.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -269,6 +269,41 @@ func (p *parser) value(it item) (interface{}, tomlType) { | |||||||
| 			types = append(types, typ) | 			types = append(types, typ) | ||||||
| 		} | 		} | ||||||
| 		return array, p.typeOfArray(types) | 		return array, p.typeOfArray(types) | ||||||
|  | 	case itemInlineTableStart: | ||||||
|  | 		var ( | ||||||
|  | 			hash         = make(map[string]interface{}) | ||||||
|  | 			outerContext = p.context | ||||||
|  | 			outerKey     = p.currentKey | ||||||
|  | 		) | ||||||
|  |  | ||||||
|  | 		p.context = append(p.context, p.currentKey) | ||||||
|  | 		p.currentKey = "" | ||||||
|  | 		for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { | ||||||
|  | 			if it.typ != itemKeyStart { | ||||||
|  | 				p.bug("Expected key start but instead found %q, around line %d", | ||||||
|  | 					it.val, p.approxLine) | ||||||
|  | 			} | ||||||
|  | 			if it.typ == itemCommentStart { | ||||||
|  | 				p.expect(itemText) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// retrieve key | ||||||
|  | 			k := p.next() | ||||||
|  | 			p.approxLine = k.line | ||||||
|  | 			kname := p.keyString(k) | ||||||
|  |  | ||||||
|  | 			// retrieve value | ||||||
|  | 			p.currentKey = kname | ||||||
|  | 			val, typ := p.value(p.next()) | ||||||
|  | 			// make sure we keep metadata up to date | ||||||
|  | 			p.setType(kname, typ) | ||||||
|  | 			p.ordered = append(p.ordered, p.context.add(p.currentKey)) | ||||||
|  | 			hash[kname] = val | ||||||
|  | 		} | ||||||
|  | 		p.context = outerContext | ||||||
|  | 		p.currentKey = outerKey | ||||||
|  | 		return hash, tomlHash | ||||||
| 	} | 	} | ||||||
| 	p.bug("Unexpected value type: %s", it.typ) | 	p.bug("Unexpected value type: %s", it.typ) | ||||||
| 	panic("unreachable") | 	panic("unreachable") | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								vendor/github.com/Sirupsen/logrus/alt_exit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/Sirupsen/logrus/alt_exit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | package logrus | ||||||
|  |  | ||||||
|  | // The following code was sourced and modified from the | ||||||
|  | // https://github.com/tebeka/atexit package governed by the following license: | ||||||
|  | // | ||||||
|  | // Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>. | ||||||
|  | // | ||||||
|  | // Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||||
|  | // this software and associated documentation files (the "Software"), to deal in | ||||||
|  | // the Software without restriction, including without limitation the rights to | ||||||
|  | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||||
|  | // the Software, and to permit persons to whom the Software is furnished to do so, | ||||||
|  | // subject to the following conditions: | ||||||
|  | // | ||||||
|  | // The above copyright notice and this permission notice shall be included in all | ||||||
|  | // copies or substantial portions of the Software. | ||||||
|  | // | ||||||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||||
|  | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||||
|  | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||||
|  | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||||
|  | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var handlers = []func(){} | ||||||
|  |  | ||||||
|  | func runHandler(handler func()) { | ||||||
|  | 	defer func() { | ||||||
|  | 		if err := recover(); err != nil { | ||||||
|  | 			fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	handler() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func runHandlers() { | ||||||
|  | 	for _, handler := range handlers { | ||||||
|  | 		runHandler(handler) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) | ||||||
|  | func Exit(code int) { | ||||||
|  | 	runHandlers() | ||||||
|  | 	os.Exit(code) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke | ||||||
|  | // all handlers. The handlers will also be invoked when any Fatal log entry is | ||||||
|  | // made. | ||||||
|  | // | ||||||
|  | // This method is useful when a caller wishes to use logrus to log a fatal | ||||||
|  | // message but also needs to gracefully shutdown. An example usecase could be | ||||||
|  | // closing database connections, or sending a alert that the application is | ||||||
|  | // closing. | ||||||
|  | func RegisterExitHandler(handler func()) { | ||||||
|  | 	handlers = append(handlers, handler) | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,11 +3,21 @@ package logrus | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" |  | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var bufferPool *sync.Pool | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	bufferPool = &sync.Pool{ | ||||||
|  | 		New: func() interface{} { | ||||||
|  | 			return new(bytes.Buffer) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Defines the key when adding errors using WithError. | // Defines the key when adding errors using WithError. | ||||||
| var ErrorKey = "error" | var ErrorKey = "error" | ||||||
|  |  | ||||||
| @@ -29,6 +39,9 @@ type Entry struct { | |||||||
|  |  | ||||||
| 	// Message passed to Debug, Info, Warn, Error, Fatal or Panic | 	// Message passed to Debug, Info, Warn, Error, Fatal or Panic | ||||||
| 	Message string | 	Message string | ||||||
|  |  | ||||||
|  | 	// When formatter is called in entry.log(), an Buffer may be set to entry | ||||||
|  | 	Buffer *bytes.Buffer | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewEntry(logger *Logger) *Entry { | func NewEntry(logger *Logger) *Entry { | ||||||
| @@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Returns a reader for the entry, which is a proxy to the formatter. |  | ||||||
| func (entry *Entry) Reader() (*bytes.Buffer, error) { |  | ||||||
| 	serialized, err := entry.Logger.Formatter.Format(entry) |  | ||||||
| 	return bytes.NewBuffer(serialized), err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Returns the string representation from the reader and ultimately the | // Returns the string representation from the reader and ultimately the | ||||||
| // formatter. | // formatter. | ||||||
| func (entry *Entry) String() (string, error) { | func (entry *Entry) String() (string, error) { | ||||||
| 	reader, err := entry.Reader() | 	serialized, err := entry.Logger.Formatter.Format(entry) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
|  | 	str := string(serialized) | ||||||
| 	return reader.String(), err | 	return str, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Add an error as single field (using the key defined in ErrorKey) to the Entry. | // Add an error as single field (using the key defined in ErrorKey) to the Entry. | ||||||
| @@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { | |||||||
| // This function is not declared with a pointer value because otherwise | // This function is not declared with a pointer value because otherwise | ||||||
| // race conditions will occur when using multiple goroutines | // race conditions will occur when using multiple goroutines | ||||||
| func (entry Entry) log(level Level, msg string) { | func (entry Entry) log(level Level, msg string) { | ||||||
|  | 	var buffer *bytes.Buffer | ||||||
| 	entry.Time = time.Now() | 	entry.Time = time.Now() | ||||||
| 	entry.Level = level | 	entry.Level = level | ||||||
| 	entry.Message = msg | 	entry.Message = msg | ||||||
| @@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) { | |||||||
| 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | ||||||
| 		entry.Logger.mu.Unlock() | 		entry.Logger.mu.Unlock() | ||||||
| 	} | 	} | ||||||
|  | 	buffer = bufferPool.Get().(*bytes.Buffer) | ||||||
| 	reader, err := entry.Reader() | 	buffer.Reset() | ||||||
|  | 	defer bufferPool.Put(buffer) | ||||||
|  | 	entry.Buffer = buffer | ||||||
|  | 	serialized, err := entry.Logger.Formatter.Format(&entry) | ||||||
|  | 	entry.Buffer = nil | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		entry.Logger.mu.Lock() | 		entry.Logger.mu.Lock() | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | ||||||
| 		entry.Logger.mu.Unlock() | 		entry.Logger.mu.Unlock() | ||||||
| 	} | 	} else { | ||||||
|  | 		entry.Logger.mu.Lock() | ||||||
| 	entry.Logger.mu.Lock() | 		_, err = entry.Logger.Out.Write(serialized) | ||||||
| 	defer entry.Logger.mu.Unlock() | 		if err != nil { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) | ||||||
| 	_, err = io.Copy(entry.Logger.Out, reader) | 		} | ||||||
| 	if err != nil { | 		entry.Logger.mu.Unlock() | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// To avoid Entry#log() returning a value that only would make sense for | 	// To avoid Entry#log() returning a value that only would make sense for | ||||||
| @@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) { | |||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.log(FatalLevel, fmt.Sprint(args...)) | 		entry.log(FatalLevel, fmt.Sprint(args...)) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (entry *Entry) Panic(args ...interface{}) { | func (entry *Entry) Panic(args ...interface{}) { | ||||||
| @@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { | |||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.Fatal(fmt.Sprintf(format, args...)) | 		entry.Fatal(fmt.Sprintf(format, args...)) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (entry *Entry) Panicf(format string, args ...interface{}) { | func (entry *Entry) Panicf(format string, args ...interface{}) { | ||||||
| @@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { | |||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.Fatal(entry.sprintlnn(args...)) | 		entry.Fatal(entry.sprintlnn(args...)) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (entry *Entry) Panicln(args ...interface{}) { | func (entry *Entry) Panicln(args ...interface{}) { | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/Sirupsen/logrus" | 	"github.com/Sirupsen/logrus" | ||||||
|  | 	// "os" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var log = logrus.New() | var log = logrus.New() | ||||||
| @@ -9,6 +10,14 @@ var log = logrus.New() | |||||||
| func init() { | func init() { | ||||||
| 	log.Formatter = new(logrus.JSONFormatter) | 	log.Formatter = new(logrus.JSONFormatter) | ||||||
| 	log.Formatter = new(logrus.TextFormatter) // default | 	log.Formatter = new(logrus.TextFormatter) // default | ||||||
|  |  | ||||||
|  | 	// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) | ||||||
|  | 	// if err == nil { | ||||||
|  | 	// 	log.Out = file | ||||||
|  | 	// } else { | ||||||
|  | 	// 	log.Info("Failed to log to file, using default stderr") | ||||||
|  | 	// } | ||||||
|  |  | ||||||
| 	log.Level = logrus.DebugLevel | 	log.Level = logrus.DebugLevel | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -31,18 +31,15 @@ type Formatter interface { | |||||||
| // It's not exported because it's still using Data in an opinionated way. It's to | // It's not exported because it's still using Data in an opinionated way. It's to | ||||||
| // avoid code duplication between the two default formatters. | // avoid code duplication between the two default formatters. | ||||||
| func prefixFieldClashes(data Fields) { | func prefixFieldClashes(data Fields) { | ||||||
| 	_, ok := data["time"] | 	if t, ok := data["time"]; ok { | ||||||
| 	if ok { | 		data["fields.time"] = t | ||||||
| 		data["fields.time"] = data["time"] |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, ok = data["msg"] | 	if m, ok := data["msg"]; ok { | ||||||
| 	if ok { | 		data["fields.msg"] = m | ||||||
| 		data["fields.msg"] = data["msg"] |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, ok = data["level"] | 	if l, ok := data["level"]; ok { | ||||||
| 	if ok { | 		data["fields.level"] = l | ||||||
| 		data["fields.level"] = data["level"] |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										61
									
								
								vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,61 +0,0 @@ | |||||||
| package logstash |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"github.com/Sirupsen/logrus" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Formatter generates json in logstash format. |  | ||||||
| // Logstash site: http://logstash.net/ |  | ||||||
| type LogstashFormatter struct { |  | ||||||
| 	Type string // if not empty use for logstash type field. |  | ||||||
|  |  | ||||||
| 	// TimestampFormat sets the format used for timestamps. |  | ||||||
| 	TimestampFormat string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { |  | ||||||
| 	fields := make(logrus.Fields) |  | ||||||
| 	for k, v := range entry.Data { |  | ||||||
| 		fields[k] = v |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	fields["@version"] = 1 |  | ||||||
|  |  | ||||||
| 	if f.TimestampFormat == "" { |  | ||||||
| 		f.TimestampFormat = logrus.DefaultTimestampFormat |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	fields["@timestamp"] = entry.Time.Format(f.TimestampFormat) |  | ||||||
|  |  | ||||||
| 	// set message field |  | ||||||
| 	v, ok := entry.Data["message"] |  | ||||||
| 	if ok { |  | ||||||
| 		fields["fields.message"] = v |  | ||||||
| 	} |  | ||||||
| 	fields["message"] = entry.Message |  | ||||||
|  |  | ||||||
| 	// set level field |  | ||||||
| 	v, ok = entry.Data["level"] |  | ||||||
| 	if ok { |  | ||||||
| 		fields["fields.level"] = v |  | ||||||
| 	} |  | ||||||
| 	fields["level"] = entry.Level.String() |  | ||||||
|  |  | ||||||
| 	// set type field |  | ||||||
| 	if f.Type != "" { |  | ||||||
| 		v, ok = entry.Data["type"] |  | ||||||
| 		if ok { |  | ||||||
| 			fields["fields.type"] = v |  | ||||||
| 		} |  | ||||||
| 		fields["type"] = f.Type |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	serialized, err := json.Marshal(fields) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) |  | ||||||
| 	} |  | ||||||
| 	return append(serialized, '\n'), nil |  | ||||||
| } |  | ||||||
							
								
								
									
										39
									
								
								vendor/github.com/Sirupsen/logrus/json_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/Sirupsen/logrus/json_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,9 +5,40 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type fieldKey string | ||||||
|  | type FieldMap map[fieldKey]string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	FieldKeyMsg   = "msg" | ||||||
|  | 	FieldKeyLevel = "level" | ||||||
|  | 	FieldKeyTime  = "time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (f FieldMap) resolve(key fieldKey) string { | ||||||
|  | 	if k, ok := f[key]; ok { | ||||||
|  | 		return k | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return string(key) | ||||||
|  | } | ||||||
|  |  | ||||||
| type JSONFormatter struct { | type JSONFormatter struct { | ||||||
| 	// TimestampFormat sets the format used for marshaling timestamps. | 	// TimestampFormat sets the format used for marshaling timestamps. | ||||||
| 	TimestampFormat string | 	TimestampFormat string | ||||||
|  |  | ||||||
|  | 	// DisableTimestamp allows disabling automatic timestamps in output | ||||||
|  | 	DisableTimestamp bool | ||||||
|  |  | ||||||
|  | 	// FieldMap allows users to customize the names of keys for various fields. | ||||||
|  | 	// As an example: | ||||||
|  | 	// formatter := &JSONFormatter{ | ||||||
|  | 	//   	FieldMap: FieldMap{ | ||||||
|  | 	// 		 FieldKeyTime: "@timestamp", | ||||||
|  | 	// 		 FieldKeyLevel: "@level", | ||||||
|  | 	// 		 FieldKeyLevel: "@message", | ||||||
|  | 	//    }, | ||||||
|  | 	// } | ||||||
|  | 	FieldMap FieldMap | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
| @@ -29,9 +60,11 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | |||||||
| 		timestampFormat = DefaultTimestampFormat | 		timestampFormat = DefaultTimestampFormat | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	data["time"] = entry.Time.Format(timestampFormat) | 	if !f.DisableTimestamp { | ||||||
| 	data["msg"] = entry.Message | 		data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) | ||||||
| 	data["level"] = entry.Level.String() | 	} | ||||||
|  | 	data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message | ||||||
|  | 	data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() | ||||||
|  |  | ||||||
| 	serialized, err := json.Marshal(data) | 	serialized, err := json.Marshal(data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										162
									
								
								vendor/github.com/Sirupsen/logrus/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/Sirupsen/logrus/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -26,8 +26,31 @@ type Logger struct { | |||||||
| 	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be | 	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be | ||||||
| 	// logged. `logrus.Debug` is useful in | 	// logged. `logrus.Debug` is useful in | ||||||
| 	Level Level | 	Level Level | ||||||
| 	// Used to sync writing to the log. | 	// Used to sync writing to the log. Locking is enabled by Default | ||||||
| 	mu sync.Mutex | 	mu MutexWrap | ||||||
|  | 	// Reusable empty entry | ||||||
|  | 	entryPool sync.Pool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MutexWrap struct { | ||||||
|  | 	lock     sync.Mutex | ||||||
|  | 	disabled bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mw *MutexWrap) Lock() { | ||||||
|  | 	if !mw.disabled { | ||||||
|  | 		mw.lock.Lock() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mw *MutexWrap) Unlock() { | ||||||
|  | 	if !mw.disabled { | ||||||
|  | 		mw.lock.Unlock() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (mw *MutexWrap) Disable() { | ||||||
|  | 	mw.disabled = true | ||||||
| } | } | ||||||
|  |  | ||||||
| // Creates a new logger. Configuration should be set by changing `Formatter`, | // Creates a new logger. Configuration should be set by changing `Formatter`, | ||||||
| @@ -51,162 +74,235 @@ func New() *Logger { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Adds a field to the log entry, note that you it doesn't log until you call | func (logger *Logger) newEntry() *Entry { | ||||||
|  | 	entry, ok := logger.entryPool.Get().(*Entry) | ||||||
|  | 	if ok { | ||||||
|  | 		return entry | ||||||
|  | 	} | ||||||
|  | 	return NewEntry(logger) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (logger *Logger) releaseEntry(entry *Entry) { | ||||||
|  | 	logger.entryPool.Put(entry) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Adds a field to the log entry, note that it doesn't log until you call | ||||||
| // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. | // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. | ||||||
| // If you want multiple fields, use `WithFields`. | // If you want multiple fields, use `WithFields`. | ||||||
| func (logger *Logger) WithField(key string, value interface{}) *Entry { | func (logger *Logger) WithField(key string, value interface{}) *Entry { | ||||||
| 	return NewEntry(logger).WithField(key, value) | 	entry := logger.newEntry() | ||||||
|  | 	defer logger.releaseEntry(entry) | ||||||
|  | 	return entry.WithField(key, value) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Adds a struct of fields to the log entry. All it does is call `WithField` for | // Adds a struct of fields to the log entry. All it does is call `WithField` for | ||||||
| // each `Field`. | // each `Field`. | ||||||
| func (logger *Logger) WithFields(fields Fields) *Entry { | func (logger *Logger) WithFields(fields Fields) *Entry { | ||||||
| 	return NewEntry(logger).WithFields(fields) | 	entry := logger.newEntry() | ||||||
|  | 	defer logger.releaseEntry(entry) | ||||||
|  | 	return entry.WithFields(fields) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Add an error as single field to the log entry.  All it does is call | // Add an error as single field to the log entry.  All it does is call | ||||||
| // `WithError` for the given `error`. | // `WithError` for the given `error`. | ||||||
| func (logger *Logger) WithError(err error) *Entry { | func (logger *Logger) WithError(err error) *Entry { | ||||||
| 	return NewEntry(logger).WithError(err) | 	entry := logger.newEntry() | ||||||
|  | 	defer logger.releaseEntry(entry) | ||||||
|  | 	return entry.WithError(err) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Debugf(format string, args ...interface{}) { | func (logger *Logger) Debugf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	if logger.Level >= DebugLevel { | ||||||
| 		NewEntry(logger).Debugf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Debugf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Infof(format string, args ...interface{}) { | func (logger *Logger) Infof(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	if logger.Level >= InfoLevel { | ||||||
| 		NewEntry(logger).Infof(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Infof(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Printf(format string, args ...interface{}) { | func (logger *Logger) Printf(format string, args ...interface{}) { | ||||||
| 	NewEntry(logger).Printf(format, args...) | 	entry := logger.newEntry() | ||||||
|  | 	entry.Printf(format, args...) | ||||||
|  | 	logger.releaseEntry(entry) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warnf(format string, args ...interface{}) { | func (logger *Logger) Warnf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warningf(format string, args ...interface{}) { | func (logger *Logger) Warningf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Errorf(format string, args ...interface{}) { | func (logger *Logger) Errorf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	if logger.Level >= ErrorLevel { | ||||||
| 		NewEntry(logger).Errorf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Errorf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Fatalf(format string, args ...interface{}) { | func (logger *Logger) Fatalf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	if logger.Level >= FatalLevel { | ||||||
| 		NewEntry(logger).Fatalf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Fatalf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Panicf(format string, args ...interface{}) { | func (logger *Logger) Panicf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	if logger.Level >= PanicLevel { | ||||||
| 		NewEntry(logger).Panicf(format, args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Panicf(format, args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Debug(args ...interface{}) { | func (logger *Logger) Debug(args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	if logger.Level >= DebugLevel { | ||||||
| 		NewEntry(logger).Debug(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Debug(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Info(args ...interface{}) { | func (logger *Logger) Info(args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	if logger.Level >= InfoLevel { | ||||||
| 		NewEntry(logger).Info(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Info(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Print(args ...interface{}) { | func (logger *Logger) Print(args ...interface{}) { | ||||||
| 	NewEntry(logger).Info(args...) | 	entry := logger.newEntry() | ||||||
|  | 	entry.Info(args...) | ||||||
|  | 	logger.releaseEntry(entry) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warn(args ...interface{}) { | func (logger *Logger) Warn(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warn(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warn(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warning(args ...interface{}) { | func (logger *Logger) Warning(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warn(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warn(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Error(args ...interface{}) { | func (logger *Logger) Error(args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	if logger.Level >= ErrorLevel { | ||||||
| 		NewEntry(logger).Error(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Error(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Fatal(args ...interface{}) { | func (logger *Logger) Fatal(args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	if logger.Level >= FatalLevel { | ||||||
| 		NewEntry(logger).Fatal(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Fatal(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Panic(args ...interface{}) { | func (logger *Logger) Panic(args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	if logger.Level >= PanicLevel { | ||||||
| 		NewEntry(logger).Panic(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Panic(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Debugln(args ...interface{}) { | func (logger *Logger) Debugln(args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	if logger.Level >= DebugLevel { | ||||||
| 		NewEntry(logger).Debugln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Debugln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Infoln(args ...interface{}) { | func (logger *Logger) Infoln(args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	if logger.Level >= InfoLevel { | ||||||
| 		NewEntry(logger).Infoln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Infoln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Println(args ...interface{}) { | func (logger *Logger) Println(args ...interface{}) { | ||||||
| 	NewEntry(logger).Println(args...) | 	entry := logger.newEntry() | ||||||
|  | 	entry.Println(args...) | ||||||
|  | 	logger.releaseEntry(entry) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warnln(args ...interface{}) { | func (logger *Logger) Warnln(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Warningln(args ...interface{}) { | func (logger *Logger) Warningln(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Errorln(args ...interface{}) { | func (logger *Logger) Errorln(args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	if logger.Level >= ErrorLevel { | ||||||
| 		NewEntry(logger).Errorln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Errorln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Fatalln(args ...interface{}) { | func (logger *Logger) Fatalln(args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	if logger.Level >= FatalLevel { | ||||||
| 		NewEntry(logger).Fatalln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Fatalln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) Panicln(args ...interface{}) { | func (logger *Logger) Panicln(args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	if logger.Level >= PanicLevel { | ||||||
| 		NewEntry(logger).Panicln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Panicln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | //When file is opened with appending mode, it's safe to | ||||||
|  | //write concurrently to a file (within 4k message on Linux). | ||||||
|  | //In these cases user can choose to disable the lock. | ||||||
|  | func (logger *Logger) SetNoLock() { | ||||||
|  | 	logger.mu.Disable() | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | // +build appengine | ||||||
|  |  | ||||||
|  | package logrus | ||||||
|  |  | ||||||
|  | import "io" | ||||||
|  |  | ||||||
|  | // IsTerminal returns true if stderr's file descriptor is a terminal. | ||||||
|  | func IsTerminal(f io.Writer) bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,4 +1,5 @@ | |||||||
| // +build darwin freebsd openbsd netbsd dragonfly | // +build darwin freebsd openbsd netbsd dragonfly | ||||||
|  | // +build !appengine | ||||||
|  |  | ||||||
| package logrus | package logrus | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/Sirupsen/logrus/terminal_linux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/Sirupsen/logrus/terminal_linux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,6 +3,8 @@ | |||||||
| // Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build !appengine | ||||||
|  |  | ||||||
| package logrus | package logrus | ||||||
|  |  | ||||||
| import "syscall" | import "syscall" | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,18 +4,25 @@ | |||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| // +build linux darwin freebsd openbsd netbsd dragonfly | // +build linux darwin freebsd openbsd netbsd dragonfly | ||||||
|  | // +build !appengine | ||||||
|  |  | ||||||
| package logrus | package logrus | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // IsTerminal returns true if stderr's file descriptor is a terminal. | // IsTerminal returns true if stderr's file descriptor is a terminal. | ||||||
| func IsTerminal() bool { | func IsTerminal(f io.Writer) bool { | ||||||
| 	fd := syscall.Stderr |  | ||||||
| 	var termios Termios | 	var termios Termios | ||||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | 	switch v := f.(type) { | ||||||
| 	return err == 0 | 	case *os.File: | ||||||
|  | 		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | ||||||
|  | 		return err == 0 | ||||||
|  | 	default: | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,15 +1,21 @@ | |||||||
| // +build solaris | // +build solaris,!appengine | ||||||
|  |  | ||||||
| package logrus | package logrus | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"golang.org/x/sys/unix" | 	"golang.org/x/sys/unix" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // IsTerminal returns true if the given file descriptor is a terminal. | // IsTerminal returns true if the given file descriptor is a terminal. | ||||||
| func IsTerminal() bool { | func IsTerminal(f io.Writer) bool { | ||||||
| 	_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) | 	switch v := f.(type) { | ||||||
| 	return err == nil | 	case *os.File: | ||||||
|  | 		_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA) | ||||||
|  | 		return err == nil | ||||||
|  | 	default: | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,11 +3,13 @@ | |||||||
| // Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| // +build windows | // +build windows,!appengine | ||||||
|  |  | ||||||
| package logrus | package logrus | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| ) | ) | ||||||
| @@ -19,9 +21,13 @@ var ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // IsTerminal returns true if stderr's file descriptor is a terminal. | // IsTerminal returns true if stderr's file descriptor is a terminal. | ||||||
| func IsTerminal() bool { | func IsTerminal(f io.Writer) bool { | ||||||
| 	fd := syscall.Stderr | 	switch v := f.(type) { | ||||||
| 	var st uint32 | 	case *os.File: | ||||||
| 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | 		var st uint32 | ||||||
| 	return r != 0 && e == 0 | 		r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) | ||||||
|  | 		return r != 0 && e == 0 | ||||||
|  | 	default: | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								vendor/github.com/Sirupsen/logrus/text_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/Sirupsen/logrus/text_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,9 +3,9 @@ package logrus | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"runtime" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -20,16 +20,10 @@ const ( | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	baseTimestamp time.Time | 	baseTimestamp time.Time | ||||||
| 	isTerminal    bool |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	baseTimestamp = time.Now() | 	baseTimestamp = time.Now() | ||||||
| 	isTerminal = IsTerminal() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func miniTS() int { |  | ||||||
| 	return int(time.Since(baseTimestamp) / time.Second) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type TextFormatter struct { | type TextFormatter struct { | ||||||
| @@ -54,10 +48,32 @@ type TextFormatter struct { | |||||||
| 	// that log extremely frequently and don't use the JSON formatter this may not | 	// that log extremely frequently and don't use the JSON formatter this may not | ||||||
| 	// be desired. | 	// be desired. | ||||||
| 	DisableSorting bool | 	DisableSorting bool | ||||||
|  |  | ||||||
|  | 	// QuoteEmptyFields will wrap empty fields in quotes if true | ||||||
|  | 	QuoteEmptyFields bool | ||||||
|  |  | ||||||
|  | 	// QuoteCharacter can be set to the override the default quoting character " | ||||||
|  | 	// with something else. For example: ', or `. | ||||||
|  | 	QuoteCharacter string | ||||||
|  |  | ||||||
|  | 	// Whether the logger's out is to a terminal | ||||||
|  | 	isTerminal bool | ||||||
|  |  | ||||||
|  | 	sync.Once | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *TextFormatter) init(entry *Entry) { | ||||||
|  | 	if len(f.QuoteCharacter) == 0 { | ||||||
|  | 		f.QuoteCharacter = "\"" | ||||||
|  | 	} | ||||||
|  | 	if entry.Logger != nil { | ||||||
|  | 		f.isTerminal = IsTerminal(entry.Logger.Out) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
| 	var keys []string = make([]string, 0, len(entry.Data)) | 	var b *bytes.Buffer | ||||||
|  | 	keys := make([]string, 0, len(entry.Data)) | ||||||
| 	for k := range entry.Data { | 	for k := range entry.Data { | ||||||
| 		keys = append(keys, k) | 		keys = append(keys, k) | ||||||
| 	} | 	} | ||||||
| @@ -65,13 +81,17 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | |||||||
| 	if !f.DisableSorting { | 	if !f.DisableSorting { | ||||||
| 		sort.Strings(keys) | 		sort.Strings(keys) | ||||||
| 	} | 	} | ||||||
|  | 	if entry.Buffer != nil { | ||||||
| 	b := &bytes.Buffer{} | 		b = entry.Buffer | ||||||
|  | 	} else { | ||||||
|  | 		b = &bytes.Buffer{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	prefixFieldClashes(entry.Data) | 	prefixFieldClashes(entry.Data) | ||||||
|  |  | ||||||
| 	isColorTerminal := isTerminal && (runtime.GOOS != "windows") | 	f.Do(func() { f.init(entry) }) | ||||||
| 	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors |  | ||||||
|  | 	isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors | ||||||
|  |  | ||||||
| 	timestampFormat := f.TimestampFormat | 	timestampFormat := f.TimestampFormat | ||||||
| 	if timestampFormat == "" { | 	if timestampFormat == "" { | ||||||
| @@ -111,51 +131,59 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin | |||||||
|  |  | ||||||
| 	levelText := strings.ToUpper(entry.Level.String())[0:4] | 	levelText := strings.ToUpper(entry.Level.String())[0:4] | ||||||
|  |  | ||||||
| 	if !f.FullTimestamp { | 	if f.DisableTimestamp { | ||||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) | ||||||
|  | 	} else if !f.FullTimestamp { | ||||||
|  | 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message) | ||||||
| 	} else { | 	} else { | ||||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) | 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) | ||||||
| 	} | 	} | ||||||
| 	for _, k := range keys { | 	for _, k := range keys { | ||||||
| 		v := entry.Data[k] | 		v := entry.Data[k] | ||||||
| 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) | 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) | ||||||
|  | 		f.appendValue(b, v) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func needsQuoting(text string) bool { | func (f *TextFormatter) needsQuoting(text string) bool { | ||||||
|  | 	if f.QuoteEmptyFields && len(text) == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
| 	for _, ch := range text { | 	for _, ch := range text { | ||||||
| 		if !((ch >= 'a' && ch <= 'z') || | 		if !((ch >= 'a' && ch <= 'z') || | ||||||
| 			(ch >= 'A' && ch <= 'Z') || | 			(ch >= 'A' && ch <= 'Z') || | ||||||
| 			(ch >= '0' && ch <= '9') || | 			(ch >= '0' && ch <= '9') || | ||||||
| 			ch == '-' || ch == '.') { | 			ch == '-' || ch == '.') { | ||||||
| 			return false | 			return true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return true | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | ||||||
|  |  | ||||||
| 	b.WriteString(key) | 	b.WriteString(key) | ||||||
| 	b.WriteByte('=') | 	b.WriteByte('=') | ||||||
|  | 	f.appendValue(b, value) | ||||||
|  | 	b.WriteByte(' ') | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { | ||||||
| 	switch value := value.(type) { | 	switch value := value.(type) { | ||||||
| 	case string: | 	case string: | ||||||
| 		if needsQuoting(value) { | 		if !f.needsQuoting(value) { | ||||||
| 			b.WriteString(value) | 			b.WriteString(value) | ||||||
| 		} else { | 		} else { | ||||||
| 			fmt.Fprintf(b, "%q", value) | 			fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) | ||||||
| 		} | 		} | ||||||
| 	case error: | 	case error: | ||||||
| 		errmsg := value.Error() | 		errmsg := value.Error() | ||||||
| 		if needsQuoting(errmsg) { | 		if !f.needsQuoting(errmsg) { | ||||||
| 			b.WriteString(errmsg) | 			b.WriteString(errmsg) | ||||||
| 		} else { | 		} else { | ||||||
| 			fmt.Fprintf(b, "%q", value) | 			fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) | ||||||
| 		} | 		} | ||||||
| 	default: | 	default: | ||||||
| 		fmt.Fprint(b, value) | 		fmt.Fprint(b, value) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.WriteByte(' ') |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								vendor/github.com/Sirupsen/logrus/writer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/Sirupsen/logrus/writer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,21 +7,52 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func (logger *Logger) Writer() *io.PipeWriter { | func (logger *Logger) Writer() *io.PipeWriter { | ||||||
|  | 	return logger.WriterLevel(InfoLevel) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { | ||||||
|  | 	return NewEntry(logger).WriterLevel(level) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (entry *Entry) Writer() *io.PipeWriter { | ||||||
|  | 	return entry.WriterLevel(InfoLevel) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { | ||||||
| 	reader, writer := io.Pipe() | 	reader, writer := io.Pipe() | ||||||
|  |  | ||||||
| 	go logger.writerScanner(reader) | 	var printFunc func(args ...interface{}) | ||||||
|  |  | ||||||
|  | 	switch level { | ||||||
|  | 	case DebugLevel: | ||||||
|  | 		printFunc = entry.Debug | ||||||
|  | 	case InfoLevel: | ||||||
|  | 		printFunc = entry.Info | ||||||
|  | 	case WarnLevel: | ||||||
|  | 		printFunc = entry.Warn | ||||||
|  | 	case ErrorLevel: | ||||||
|  | 		printFunc = entry.Error | ||||||
|  | 	case FatalLevel: | ||||||
|  | 		printFunc = entry.Fatal | ||||||
|  | 	case PanicLevel: | ||||||
|  | 		printFunc = entry.Panic | ||||||
|  | 	default: | ||||||
|  | 		printFunc = entry.Print | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go entry.writerScanner(reader, printFunc) | ||||||
| 	runtime.SetFinalizer(writer, writerFinalizer) | 	runtime.SetFinalizer(writer, writerFinalizer) | ||||||
|  |  | ||||||
| 	return writer | 	return writer | ||||||
| } | } | ||||||
|  |  | ||||||
| func (logger *Logger) writerScanner(reader *io.PipeReader) { | func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { | ||||||
| 	scanner := bufio.NewScanner(reader) | 	scanner := bufio.NewScanner(reader) | ||||||
| 	for scanner.Scan() { | 	for scanner.Scan() { | ||||||
| 		logger.Print(scanner.Text()) | 		printFunc(scanner.Text()) | ||||||
| 	} | 	} | ||||||
| 	if err := scanner.Err(); err != nil { | 	if err := scanner.Err(); err != nil { | ||||||
| 		logger.Errorf("Error while reading from Writer: %s", err) | 		entry.Errorf("Error while reading from Writer: %s", err) | ||||||
| 	} | 	} | ||||||
| 	reader.Close() | 	reader.Close() | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -21,8 +21,10 @@ import ( | |||||||
|  |  | ||||||
| // BotAPI allows you to interact with the Telegram Bot API. | // BotAPI allows you to interact with the Telegram Bot API. | ||||||
| type BotAPI struct { | type BotAPI struct { | ||||||
| 	Token  string       `json:"token"` | 	Token  string `json:"token"` | ||||||
| 	Debug  bool         `json:"debug"` | 	Debug  bool   `json:"debug"` | ||||||
|  | 	Buffer int    `json:"buffer"` | ||||||
|  |  | ||||||
| 	Self   User         `json:"-"` | 	Self   User         `json:"-"` | ||||||
| 	Client *http.Client `json:"-"` | 	Client *http.Client `json:"-"` | ||||||
| } | } | ||||||
| @@ -42,11 +44,12 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) { | |||||||
| 	bot := &BotAPI{ | 	bot := &BotAPI{ | ||||||
| 		Token:  token, | 		Token:  token, | ||||||
| 		Client: client, | 		Client: client, | ||||||
|  | 		Buffer: 100, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	self, err := bot.GetMe() | 	self, err := bot.GetMe() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return &BotAPI{}, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	bot.Self = self | 	bot.Self = self | ||||||
| @@ -68,6 +71,10 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, | |||||||
| 		return APIResponse{}, errors.New(ErrAPIForbidden) | 		return APIResponse{}, errors.New(ErrAPIForbidden) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if resp.StatusCode != http.StatusOK { | ||||||
|  | 		return APIResponse{}, errors.New(http.StatusText(resp.StatusCode)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	bytes, err := ioutil.ReadAll(resp.Body) | 	bytes, err := ioutil.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return APIResponse{}, err | 		return APIResponse{}, err | ||||||
| @@ -457,7 +464,7 @@ func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) { | |||||||
|  |  | ||||||
| // GetUpdatesChan starts and returns a channel for getting updates. | // GetUpdatesChan starts and returns a channel for getting updates. | ||||||
| func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { | func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { | ||||||
| 	ch := make(chan Update, 100) | 	ch := make(chan Update, bot.Buffer) | ||||||
|  |  | ||||||
| 	go func() { | 	go func() { | ||||||
| 		for { | 		for { | ||||||
| @@ -484,7 +491,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { | |||||||
|  |  | ||||||
| // ListenForWebhook registers a http handler for a webhook. | // ListenForWebhook registers a http handler for a webhook. | ||||||
| func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { | func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { | ||||||
| 	ch := make(chan Update, 100) | 	ch := make(chan Update, bot.Buffer) | ||||||
|  |  | ||||||
| 	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { | 	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		bytes, _ := ioutil.ReadAll(r.Body) | 		bytes, _ := ioutil.ReadAll(r.Body) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -768,8 +768,8 @@ type UpdateConfig struct { | |||||||
|  |  | ||||||
| // WebhookConfig contains information about a SetWebhook request. | // WebhookConfig contains information about a SetWebhook request. | ||||||
| type WebhookConfig struct { | type WebhookConfig struct { | ||||||
| 	URL         *url.URL | 	URL            *url.URL | ||||||
| 	Certificate interface{} | 	Certificate    interface{} | ||||||
| 	MaxConnections int | 	MaxConnections int | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -318,21 +318,6 @@ func NewWebhookWithCert(link string, file interface{}) WebhookConfig { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewWebhookWithCert creates a new webhook with a certificate and max_connections. |  | ||||||
| // |  | ||||||
| // link is the url you wish to get webhooks, |  | ||||||
| // file contains a string to a file, FileReader, or FileBytes. |  | ||||||
| // maxConnections defines maximum number of connections from telegram to your server |  | ||||||
| func NewWebhookWithCertAndMaxConnections(link string, file interface{}, maxConnections int) WebhookConfig { |  | ||||||
| 	u, _ := url.Parse(link) |  | ||||||
|  |  | ||||||
| 	return WebhookConfig{ |  | ||||||
| 		URL:         u, |  | ||||||
| 		Certificate: file, |  | ||||||
| 		MaxConnections: maxConnections, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewInlineQueryResultArticle creates a new inline query article. | // NewInlineQueryResultArticle creates a new inline query article. | ||||||
| func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle { | func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle { | ||||||
| 	return InlineQueryResultArticle{ | 	return InlineQueryResultArticle{ | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -194,7 +194,7 @@ func (m *Message) CommandArguments() string { | |||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return strings.SplitN(m.Text, " ", 2)[1] | 	return split[1] | ||||||
| } | } | ||||||
|  |  | ||||||
| // MessageEntity contains information about data in a Message. | // MessageEntity contains information about data in a Message. | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								vendor/github.com/gorilla/schema/cache.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/gorilla/schema/cache.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -138,7 +138,12 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo { | |||||||
| 				ft = ft.Elem() | 				ft = ft.Elem() | ||||||
| 			} | 			} | ||||||
| 			if ft.Kind() == reflect.Struct { | 			if ft.Kind() == reflect.Struct { | ||||||
|  | 				bef := len(info.fields) | ||||||
| 				c.create(ft, info) | 				c.create(ft, info) | ||||||
|  | 				for _, fi := range info.fields[bef:len(info.fields)] { | ||||||
|  | 					// exclude required check because duplicated to embedded field | ||||||
|  | 					fi.required = false | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		c.createField(field, info) | 		c.createField(field, info) | ||||||
| @@ -148,7 +153,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo { | |||||||
|  |  | ||||||
| // createField creates a fieldInfo for the given field. | // createField creates a fieldInfo for the given field. | ||||||
| func (c *cache) createField(field reflect.StructField, info *structInfo) { | func (c *cache) createField(field reflect.StructField, info *structInfo) { | ||||||
| 	alias := fieldAlias(field, c.tag) | 	alias, options := fieldAlias(field, c.tag) | ||||||
| 	if alias == "-" { | 	if alias == "-" { | ||||||
| 		// Ignore this field. | 		// Ignore this field. | ||||||
| 		return | 		return | ||||||
| @@ -173,17 +178,19 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if isStruct = ft.Kind() == reflect.Struct; !isStruct { | 	if isStruct = ft.Kind() == reflect.Struct; !isStruct { | ||||||
| 		if conv := c.conv[ft.Kind()]; conv == nil { | 		if conv := c.converter(ft); conv == nil { | ||||||
| 			// Type is not supported. | 			// Type is not supported. | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	info.fields = append(info.fields, &fieldInfo{ | 	info.fields = append(info.fields, &fieldInfo{ | ||||||
| 		typ:   field.Type, | 		typ:      field.Type, | ||||||
| 		name:  field.Name, | 		name:     field.Name, | ||||||
| 		ss:    isSlice && isStruct, | 		ss:       isSlice && isStruct, | ||||||
| 		alias: alias, | 		alias:    alias, | ||||||
|  | 		anon:     field.Anonymous, | ||||||
|  | 		required: options.Contains("required"), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -212,10 +219,12 @@ func (i *structInfo) get(alias string) *fieldInfo { | |||||||
| } | } | ||||||
|  |  | ||||||
| type fieldInfo struct { | type fieldInfo struct { | ||||||
| 	typ   reflect.Type | 	typ      reflect.Type | ||||||
| 	name  string // field name in the struct. | 	name     string // field name in the struct. | ||||||
| 	ss    bool   // true if this is a slice of structs. | 	ss       bool   // true if this is a slice of structs. | ||||||
| 	alias string | 	alias    string | ||||||
|  | 	anon     bool // is an embedded field | ||||||
|  | 	required bool // tag option | ||||||
| } | } | ||||||
|  |  | ||||||
| type pathPart struct { | type pathPart struct { | ||||||
| @@ -227,19 +236,33 @@ type pathPart struct { | |||||||
| // ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| // fieldAlias parses a field tag to get a field alias. | // fieldAlias parses a field tag to get a field alias. | ||||||
| func fieldAlias(field reflect.StructField, tagName string) string { | func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) { | ||||||
| 	var alias string |  | ||||||
| 	if tag := field.Tag.Get(tagName); tag != "" { | 	if tag := field.Tag.Get(tagName); tag != "" { | ||||||
| 		// For now tags only support the name but let's follow the | 		alias, options = parseTag(tag) | ||||||
| 		// comma convention from encoding/json and others. |  | ||||||
| 		if idx := strings.Index(tag, ","); idx == -1 { |  | ||||||
| 			alias = tag |  | ||||||
| 		} else { |  | ||||||
| 			alias = tag[:idx] |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	if alias == "" { | 	if alias == "" { | ||||||
| 		alias = field.Name | 		alias = field.Name | ||||||
| 	} | 	} | ||||||
| 	return alias | 	return alias, options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // tagOptions is the string following a comma in a struct field's tag, or | ||||||
|  | // the empty string. It does not include the leading comma. | ||||||
|  | type tagOptions []string | ||||||
|  |  | ||||||
|  | // parseTag splits a struct field's url tag into its name and comma-separated | ||||||
|  | // options. | ||||||
|  | func parseTag(tag string) (string, tagOptions) { | ||||||
|  | 	s := strings.Split(tag, ",") | ||||||
|  | 	return s[0], s[1:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Contains checks whether the tagOptions contains the specified option. | ||||||
|  | func (o tagOptions) Contains(option string) bool { | ||||||
|  | 	for _, s := range o { | ||||||
|  | 		if s == option { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								vendor/github.com/gorilla/schema/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/gorilla/schema/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -87,9 +87,60 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error { | |||||||
| 	if len(errors) > 0 { | 	if len(errors) > 0 { | ||||||
| 		return errors | 		return errors | ||||||
| 	} | 	} | ||||||
|  | 	return d.checkRequired(t, src, "") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // checkRequired checks whether requred field empty | ||||||
|  | // | ||||||
|  | // check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation | ||||||
|  | // | ||||||
|  | // src is the source map for decoding, we use it here to see if those required fields are included in src | ||||||
|  | func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix string) error { | ||||||
|  | 	struc := d.cache.get(t) | ||||||
|  | 	if struc == nil { | ||||||
|  | 		// unexpect, cache.get never return nil | ||||||
|  | 		return errors.New("cache fail") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, f := range struc.fields { | ||||||
|  | 		if f.typ.Kind() == reflect.Struct { | ||||||
|  | 			err := d.checkRequired(f.typ, src, prefix+f.alias+".") | ||||||
|  | 			if err != nil { | ||||||
|  | 				if !f.anon { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 				// check embedded parent field. | ||||||
|  | 				err2 := d.checkRequired(f.typ, src, prefix) | ||||||
|  | 				if err2 != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if f.required { | ||||||
|  | 			key := f.alias | ||||||
|  | 			if prefix != "" { | ||||||
|  | 				key = prefix + key | ||||||
|  | 			} | ||||||
|  | 			if isEmpty(f.typ, src[key]) { | ||||||
|  | 				return fmt.Errorf("%v is empty", key) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // isEmpty returns true if value is empty for specific type | ||||||
|  | func isEmpty(t reflect.Type, value []string) bool { | ||||||
|  | 	if len(value) == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	switch t.Kind() { | ||||||
|  | 	case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type: | ||||||
|  | 		return len(value[0]) == 0 | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
| // decode fills a struct field using a parsed path. | // decode fills a struct field using a parsed path. | ||||||
| func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error { | func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error { | ||||||
| 	// Get the field walking the struct fields by index. | 	// Get the field walking the struct fields by index. | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/gorilla/schema/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gorilla/schema/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,7 +12,7 @@ The basic usage is really simple. Given this struct: | |||||||
| 		Phone string | 		Phone string | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| ...we can fill it passing a map to the Load() function: | ...we can fill it passing a map to the Decode() function: | ||||||
|  |  | ||||||
| 	values := map[string][]string{ | 	values := map[string][]string{ | ||||||
| 		"Name":  {"John"}, | 		"Name":  {"John"}, | ||||||
|   | |||||||
							
								
								
									
										161
									
								
								vendor/github.com/gorilla/schema/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/gorilla/schema/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | package schema | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type encoderFunc func(reflect.Value) string | ||||||
|  |  | ||||||
|  | // Encoder encodes values from a struct into url.Values. | ||||||
|  | type Encoder struct { | ||||||
|  | 	cache  *cache | ||||||
|  | 	regenc map[reflect.Type]encoderFunc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewEncoder returns a new Encoder with defaults. | ||||||
|  | func NewEncoder() *Encoder { | ||||||
|  | 	return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Encode encodes a struct into map[string][]string. | ||||||
|  | // | ||||||
|  | // Intended for use with url.Values. | ||||||
|  | func (e *Encoder) Encode(src interface{}, dst map[string][]string) error { | ||||||
|  | 	v := reflect.ValueOf(src) | ||||||
|  |  | ||||||
|  | 	return e.encode(v, dst) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RegisterEncoder registers a converter for encoding a custom type. | ||||||
|  | func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) { | ||||||
|  | 	e.regenc[reflect.TypeOf(value)] = encoder | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetAliasTag changes the tag used to locate custom field aliases. | ||||||
|  | // The default tag is "schema". | ||||||
|  | func (e *Encoder) SetAliasTag(tag string) { | ||||||
|  | 	e.cache.tag = tag | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { | ||||||
|  | 	if v.Kind() == reflect.Ptr { | ||||||
|  | 		v = v.Elem() | ||||||
|  | 	} | ||||||
|  | 	if v.Kind() != reflect.Struct { | ||||||
|  | 		return errors.New("schema: interface must be a struct") | ||||||
|  | 	} | ||||||
|  | 	t := v.Type() | ||||||
|  |  | ||||||
|  | 	errors := MultiError{} | ||||||
|  |  | ||||||
|  | 	for i := 0; i < v.NumField(); i++ { | ||||||
|  | 		name, opts := fieldAlias(t.Field(i), e.cache.tag) | ||||||
|  | 		if name == "-" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if v.Field(i).Type().Kind() == reflect.Struct { | ||||||
|  | 			e.encode(v.Field(i), dst) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		encFunc := typeEncoder(v.Field(i).Type(), e.regenc) | ||||||
|  |  | ||||||
|  | 		// Encode non-slice types and custom implementations immediately. | ||||||
|  | 		if encFunc != nil { | ||||||
|  | 			value := encFunc(v.Field(i)) | ||||||
|  | 			if value == "" && opts.Contains("omitempty") { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			dst[name] = append(dst[name], value) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if v.Field(i).Type().Kind() == reflect.Slice { | ||||||
|  | 			encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if encFunc == nil { | ||||||
|  | 			errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i)) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Encode a slice. | ||||||
|  | 		if v.Field(i).Len() == 0 && opts.Contains("omitempty") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		dst[name] = []string{} | ||||||
|  | 		for j := 0; j < v.Field(i).Len(); j++ { | ||||||
|  | 			dst[name] = append(dst[name], encFunc(v.Field(i).Index(j))) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(errors) > 0 { | ||||||
|  | 		return errors | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc { | ||||||
|  | 	if f, ok := reg[t]; ok { | ||||||
|  | 		return f | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch t.Kind() { | ||||||
|  | 	case reflect.Bool: | ||||||
|  | 		return encodeBool | ||||||
|  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||||
|  | 		return encodeInt | ||||||
|  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||||||
|  | 		return encodeUint | ||||||
|  | 	case reflect.Float32: | ||||||
|  | 		return encodeFloat32 | ||||||
|  | 	case reflect.Float64: | ||||||
|  | 		return encodeFloat64 | ||||||
|  | 	case reflect.Ptr: | ||||||
|  | 		f := typeEncoder(t.Elem(), reg) | ||||||
|  | 		return func(v reflect.Value) string { | ||||||
|  | 			if v.IsNil() { | ||||||
|  | 				return "null" | ||||||
|  | 			} | ||||||
|  | 			return f(v.Elem()) | ||||||
|  | 		} | ||||||
|  | 	case reflect.String: | ||||||
|  | 		return encodeString | ||||||
|  | 	default: | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeBool(v reflect.Value) string { | ||||||
|  | 	return strconv.FormatBool(v.Bool()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeInt(v reflect.Value) string { | ||||||
|  | 	return strconv.FormatInt(int64(v.Int()), 10) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeUint(v reflect.Value) string { | ||||||
|  | 	return strconv.FormatUint(uint64(v.Uint()), 10) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeFloat(v reflect.Value, bits int) string { | ||||||
|  | 	return strconv.FormatFloat(v.Float(), 'f', 6, bits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeFloat32(v reflect.Value) string { | ||||||
|  | 	return encodeFloat(v, 32) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeFloat64(v reflect.Value) string { | ||||||
|  | 	return encodeFloat(v, 64) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func encodeString(v reflect.Value) string { | ||||||
|  | 	return v.String() | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								vendor/github.com/gorilla/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/gorilla/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -23,6 +23,8 @@ import ( | |||||||
| // invalid. | // invalid. | ||||||
| var ErrBadHandshake = errors.New("websocket: bad handshake") | var ErrBadHandshake = errors.New("websocket: bad handshake") | ||||||
|  |  | ||||||
|  | var errInvalidCompression = errors.New("websocket: invalid compression negotiation") | ||||||
|  |  | ||||||
| // NewClient creates a new client connection using the given net connection. | // NewClient creates a new client connection using the given net connection. | ||||||
| // The URL u specifies the host and request URI. Use requestHeader to specify | // The URL u specifies the host and request URI. Use requestHeader to specify | ||||||
| // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies | // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies | ||||||
| @@ -64,12 +66,24 @@ type Dialer struct { | |||||||
| 	// HandshakeTimeout specifies the duration for the handshake to complete. | 	// HandshakeTimeout specifies the duration for the handshake to complete. | ||||||
| 	HandshakeTimeout time.Duration | 	HandshakeTimeout time.Duration | ||||||
|  |  | ||||||
| 	// Input and output buffer sizes. If the buffer size is zero, then a | 	// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer | ||||||
| 	// default value of 4096 is used. | 	// size is zero, then a useful default size is used. The I/O buffer sizes | ||||||
|  | 	// do not limit the size of the messages that can be sent or received. | ||||||
| 	ReadBufferSize, WriteBufferSize int | 	ReadBufferSize, WriteBufferSize int | ||||||
|  |  | ||||||
| 	// Subprotocols specifies the client's requested subprotocols. | 	// Subprotocols specifies the client's requested subprotocols. | ||||||
| 	Subprotocols []string | 	Subprotocols []string | ||||||
|  |  | ||||||
|  | 	// EnableCompression specifies if the client should attempt to negotiate | ||||||
|  | 	// per message compression (RFC 7692). Setting this value to true does not | ||||||
|  | 	// guarantee that compression will be supported. Currently only "no context | ||||||
|  | 	// takeover" modes are supported. | ||||||
|  | 	EnableCompression bool | ||||||
|  |  | ||||||
|  | 	// Jar specifies the cookie jar. | ||||||
|  | 	// If Jar is nil, cookies are not sent in requests and ignored | ||||||
|  | 	// in responses. | ||||||
|  | 	Jar http.CookieJar | ||||||
| } | } | ||||||
|  |  | ||||||
| var errMalformedURL = errors.New("malformed ws or wss URL") | var errMalformedURL = errors.New("malformed ws or wss URL") | ||||||
| @@ -83,7 +97,6 @@ func parseURL(s string) (*url.URL, error) { | |||||||
| 	// | 	// | ||||||
| 	// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] | 	// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] | ||||||
| 	// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] | 	// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] | ||||||
|  |  | ||||||
| 	var u url.URL | 	var u url.URL | ||||||
| 	switch { | 	switch { | ||||||
| 	case strings.HasPrefix(s, "ws://"): | 	case strings.HasPrefix(s, "ws://"): | ||||||
| @@ -193,6 +206,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 		Host:       u.Host, | 		Host:       u.Host, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Set the cookies present in the cookie jar of the dialer | ||||||
|  | 	if d.Jar != nil { | ||||||
|  | 		for _, cookie := range d.Jar.Cookies(u) { | ||||||
|  | 			req.AddCookie(cookie) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Set the request headers using the capitalization for names and values in | 	// Set the request headers using the capitalization for names and values in | ||||||
| 	// RFC examples. Although the capitalization shouldn't matter, there are | 	// RFC examples. Although the capitalization shouldn't matter, there are | ||||||
| 	// servers that depend on it. The Header.Set method is not used because the | 	// servers that depend on it. The Header.Set method is not used because the | ||||||
| @@ -214,6 +234,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 			k == "Connection" || | 			k == "Connection" || | ||||||
| 			k == "Sec-Websocket-Key" || | 			k == "Sec-Websocket-Key" || | ||||||
| 			k == "Sec-Websocket-Version" || | 			k == "Sec-Websocket-Version" || | ||||||
|  | 			k == "Sec-Websocket-Extensions" || | ||||||
| 			(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): | 			(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): | ||||||
| 			return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) | 			return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) | ||||||
| 		default: | 		default: | ||||||
| @@ -221,6 +242,10 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if d.EnableCompression { | ||||||
|  | 		req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	hostPort, hostNoPort := hostPortNoPort(u) | 	hostPort, hostNoPort := hostPortNoPort(u) | ||||||
|  |  | ||||||
| 	var proxyURL *url.URL | 	var proxyURL *url.URL | ||||||
| @@ -298,12 +323,8 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.Scheme == "https" { | 	if u.Scheme == "https" { | ||||||
| 		cfg := d.TLSClientConfig | 		cfg := cloneTLSConfig(d.TLSClientConfig) | ||||||
| 		if cfg == nil { | 		if cfg.ServerName == "" { | ||||||
| 			cfg = &tls.Config{ServerName: hostNoPort} |  | ||||||
| 		} else if cfg.ServerName == "" { |  | ||||||
| 			shallowCopy := *cfg |  | ||||||
| 			cfg = &shallowCopy |  | ||||||
| 			cfg.ServerName = hostNoPort | 			cfg.ServerName = hostNoPort | ||||||
| 		} | 		} | ||||||
| 		tlsConn := tls.Client(netConn, cfg) | 		tlsConn := tls.Client(netConn, cfg) | ||||||
| @@ -328,6 +349,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if d.Jar != nil { | ||||||
|  | 		if rc := resp.Cookies(); len(rc) > 0 { | ||||||
|  | 			d.Jar.SetCookies(u, rc) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if resp.StatusCode != 101 || | 	if resp.StatusCode != 101 || | ||||||
| 		!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || | 		!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || | ||||||
| 		!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || | 		!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || | ||||||
| @@ -341,6 +369,20 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re | |||||||
| 		return nil, resp, ErrBadHandshake | 		return nil, resp, ErrBadHandshake | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	for _, ext := range parseExtensions(resp.Header) { | ||||||
|  | 		if ext[""] != "permessage-deflate" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		_, snct := ext["server_no_context_takeover"] | ||||||
|  | 		_, cnct := ext["client_no_context_takeover"] | ||||||
|  | 		if !snct || !cnct { | ||||||
|  | 			return nil, resp, errInvalidCompression | ||||||
|  | 		} | ||||||
|  | 		conn.newCompressionWriter = compressNoContextTakeover | ||||||
|  | 		conn.newDecompressionReader = decompressNoContextTakeover | ||||||
|  | 		break | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) | 	resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) | ||||||
| 	conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") | 	conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								vendor/github.com/gorilla/websocket/client_clone.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/gorilla/websocket/client_clone.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build go1.8 | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import "crypto/tls" | ||||||
|  |  | ||||||
|  | func cloneTLSConfig(cfg *tls.Config) *tls.Config { | ||||||
|  | 	if cfg == nil { | ||||||
|  | 		return &tls.Config{} | ||||||
|  | 	} | ||||||
|  | 	return cfg.Clone() | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								vendor/github.com/gorilla/websocket/client_clone_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/gorilla/websocket/client_clone_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build !go1.8 | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import "crypto/tls" | ||||||
|  |  | ||||||
|  | // cloneTLSConfig clones all public fields except the fields | ||||||
|  | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the | ||||||
|  | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a | ||||||
|  | // config in active use. | ||||||
|  | func cloneTLSConfig(cfg *tls.Config) *tls.Config { | ||||||
|  | 	if cfg == nil { | ||||||
|  | 		return &tls.Config{} | ||||||
|  | 	} | ||||||
|  | 	return &tls.Config{ | ||||||
|  | 		Rand:                     cfg.Rand, | ||||||
|  | 		Time:                     cfg.Time, | ||||||
|  | 		Certificates:             cfg.Certificates, | ||||||
|  | 		NameToCertificate:        cfg.NameToCertificate, | ||||||
|  | 		GetCertificate:           cfg.GetCertificate, | ||||||
|  | 		RootCAs:                  cfg.RootCAs, | ||||||
|  | 		NextProtos:               cfg.NextProtos, | ||||||
|  | 		ServerName:               cfg.ServerName, | ||||||
|  | 		ClientAuth:               cfg.ClientAuth, | ||||||
|  | 		ClientCAs:                cfg.ClientCAs, | ||||||
|  | 		InsecureSkipVerify:       cfg.InsecureSkipVerify, | ||||||
|  | 		CipherSuites:             cfg.CipherSuites, | ||||||
|  | 		PreferServerCipherSuites: cfg.PreferServerCipherSuites, | ||||||
|  | 		ClientSessionCache:       cfg.ClientSessionCache, | ||||||
|  | 		MinVersion:               cfg.MinVersion, | ||||||
|  | 		MaxVersion:               cfg.MaxVersion, | ||||||
|  | 		CurvePreferences:         cfg.CurvePreferences, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										148
									
								
								vendor/github.com/gorilla/websocket/compression.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/gorilla/websocket/compression.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"compress/flate" | ||||||
|  | 	"errors" | ||||||
|  | 	"io" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	minCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6 | ||||||
|  | 	maxCompressionLevel     = flate.BestCompression | ||||||
|  | 	defaultCompressionLevel = 1 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool | ||||||
|  | 	flateReaderPool  = sync.Pool{New: func() interface{} { | ||||||
|  | 		return flate.NewReader(nil) | ||||||
|  | 	}} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func decompressNoContextTakeover(r io.Reader) io.ReadCloser { | ||||||
|  | 	const tail = | ||||||
|  | 	// Add four bytes as specified in RFC | ||||||
|  | 	"\x00\x00\xff\xff" + | ||||||
|  | 		// Add final block to squelch unexpected EOF error from flate reader. | ||||||
|  | 		"\x01\x00\x00\xff\xff" | ||||||
|  |  | ||||||
|  | 	fr, _ := flateReaderPool.Get().(io.ReadCloser) | ||||||
|  | 	fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) | ||||||
|  | 	return &flateReadWrapper{fr} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func isValidCompressionLevel(level int) bool { | ||||||
|  | 	return minCompressionLevel <= level && level <= maxCompressionLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { | ||||||
|  | 	p := &flateWriterPools[level-minCompressionLevel] | ||||||
|  | 	tw := &truncWriter{w: w} | ||||||
|  | 	fw, _ := p.Get().(*flate.Writer) | ||||||
|  | 	if fw == nil { | ||||||
|  | 		fw, _ = flate.NewWriter(tw, level) | ||||||
|  | 	} else { | ||||||
|  | 		fw.Reset(tw) | ||||||
|  | 	} | ||||||
|  | 	return &flateWriteWrapper{fw: fw, tw: tw, p: p} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // truncWriter is an io.Writer that writes all but the last four bytes of the | ||||||
|  | // stream to another io.Writer. | ||||||
|  | type truncWriter struct { | ||||||
|  | 	w io.WriteCloser | ||||||
|  | 	n int | ||||||
|  | 	p [4]byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w *truncWriter) Write(p []byte) (int, error) { | ||||||
|  | 	n := 0 | ||||||
|  |  | ||||||
|  | 	// fill buffer first for simplicity. | ||||||
|  | 	if w.n < len(w.p) { | ||||||
|  | 		n = copy(w.p[w.n:], p) | ||||||
|  | 		p = p[n:] | ||||||
|  | 		w.n += n | ||||||
|  | 		if len(p) == 0 { | ||||||
|  | 			return n, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m := len(p) | ||||||
|  | 	if m > len(w.p) { | ||||||
|  | 		m = len(w.p) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if nn, err := w.w.Write(w.p[:m]); err != nil { | ||||||
|  | 		return n + nn, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	copy(w.p[:], w.p[m:]) | ||||||
|  | 	copy(w.p[len(w.p)-m:], p[len(p)-m:]) | ||||||
|  | 	nn, err := w.w.Write(p[:len(p)-m]) | ||||||
|  | 	return n + nn, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type flateWriteWrapper struct { | ||||||
|  | 	fw *flate.Writer | ||||||
|  | 	tw *truncWriter | ||||||
|  | 	p  *sync.Pool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w *flateWriteWrapper) Write(p []byte) (int, error) { | ||||||
|  | 	if w.fw == nil { | ||||||
|  | 		return 0, errWriteClosed | ||||||
|  | 	} | ||||||
|  | 	return w.fw.Write(p) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w *flateWriteWrapper) Close() error { | ||||||
|  | 	if w.fw == nil { | ||||||
|  | 		return errWriteClosed | ||||||
|  | 	} | ||||||
|  | 	err1 := w.fw.Flush() | ||||||
|  | 	w.p.Put(w.fw) | ||||||
|  | 	w.fw = nil | ||||||
|  | 	if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { | ||||||
|  | 		return errors.New("websocket: internal error, unexpected bytes at end of flate stream") | ||||||
|  | 	} | ||||||
|  | 	err2 := w.tw.w.Close() | ||||||
|  | 	if err1 != nil { | ||||||
|  | 		return err1 | ||||||
|  | 	} | ||||||
|  | 	return err2 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type flateReadWrapper struct { | ||||||
|  | 	fr io.ReadCloser | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *flateReadWrapper) Read(p []byte) (int, error) { | ||||||
|  | 	if r.fr == nil { | ||||||
|  | 		return 0, io.ErrClosedPipe | ||||||
|  | 	} | ||||||
|  | 	n, err := r.fr.Read(p) | ||||||
|  | 	if err == io.EOF { | ||||||
|  | 		// Preemptively place the reader back in the pool. This helps with | ||||||
|  | 		// scenarios where the application does not call NextReader() soon after | ||||||
|  | 		// this final read. | ||||||
|  | 		r.Close() | ||||||
|  | 	} | ||||||
|  | 	return n, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *flateReadWrapper) Close() error { | ||||||
|  | 	if r.fr == nil { | ||||||
|  | 		return io.ErrClosedPipe | ||||||
|  | 	} | ||||||
|  | 	err := r.fr.Close() | ||||||
|  | 	flateReaderPool.Put(r.fr) | ||||||
|  | 	r.fr = nil | ||||||
|  | 	return err | ||||||
|  | } | ||||||
							
								
								
									
										632
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										632
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,15 +13,25 @@ import ( | |||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"net" | 	"net" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  | 	"unicode/utf8" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
|  | 	// Frame header byte 0 bits from Section 5.2 of RFC 6455 | ||||||
|  | 	finalBit = 1 << 7 | ||||||
|  | 	rsv1Bit  = 1 << 6 | ||||||
|  | 	rsv2Bit  = 1 << 5 | ||||||
|  | 	rsv3Bit  = 1 << 4 | ||||||
|  |  | ||||||
|  | 	// Frame header byte 1 bits from Section 5.2 of RFC 6455 | ||||||
|  | 	maskBit = 1 << 7 | ||||||
|  |  | ||||||
| 	maxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask | 	maxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask | ||||||
| 	maxControlFramePayloadSize = 125 | 	maxControlFramePayloadSize = 125 | ||||||
| 	finalBit                   = 1 << 7 |  | ||||||
| 	maskBit                    = 1 << 7 | 	writeWait = time.Second | ||||||
| 	writeWait                  = time.Second |  | ||||||
|  |  | ||||||
| 	defaultReadBufferSize  = 4096 | 	defaultReadBufferSize  = 4096 | ||||||
| 	defaultWriteBufferSize = 4096 | 	defaultWriteBufferSize = 4096 | ||||||
| @@ -43,6 +53,8 @@ const ( | |||||||
| 	CloseMessageTooBig           = 1009 | 	CloseMessageTooBig           = 1009 | ||||||
| 	CloseMandatoryExtension      = 1010 | 	CloseMandatoryExtension      = 1010 | ||||||
| 	CloseInternalServerErr       = 1011 | 	CloseInternalServerErr       = 1011 | ||||||
|  | 	CloseServiceRestart          = 1012 | ||||||
|  | 	CloseTryAgainLater           = 1013 | ||||||
| 	CloseTLSHandshake            = 1015 | 	CloseTLSHandshake            = 1015 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -169,6 +181,11 @@ var ( | |||||||
| 	errInvalidControlFrame = errors.New("websocket: invalid control frame") | 	errInvalidControlFrame = errors.New("websocket: invalid control frame") | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func newMaskKey() [4]byte { | ||||||
|  | 	n := rand.Uint32() | ||||||
|  | 	return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} | ||||||
|  | } | ||||||
|  |  | ||||||
| func hideTempErr(err error) error { | func hideTempErr(err error) error { | ||||||
| 	if e, ok := err.(net.Error); ok && e.Temporary() { | 	if e, ok := err.(net.Error); ok && e.Temporary() { | ||||||
| 		err = &netError{msg: e.Error(), timeout: e.Timeout()} | 		err = &netError{msg: e.Error(), timeout: e.Timeout()} | ||||||
| @@ -184,74 +201,138 @@ func isData(frameType int) bool { | |||||||
| 	return frameType == TextMessage || frameType == BinaryMessage | 	return frameType == TextMessage || frameType == BinaryMessage | ||||||
| } | } | ||||||
|  |  | ||||||
| func maskBytes(key [4]byte, pos int, b []byte) int { | var validReceivedCloseCodes = map[int]bool{ | ||||||
| 	for i := range b { | 	// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number | ||||||
| 		b[i] ^= key[pos&3] |  | ||||||
| 		pos++ | 	CloseNormalClosure:           true, | ||||||
| 	} | 	CloseGoingAway:               true, | ||||||
| 	return pos & 3 | 	CloseProtocolError:           true, | ||||||
|  | 	CloseUnsupportedData:         true, | ||||||
|  | 	CloseNoStatusReceived:        false, | ||||||
|  | 	CloseAbnormalClosure:         false, | ||||||
|  | 	CloseInvalidFramePayloadData: true, | ||||||
|  | 	ClosePolicyViolation:         true, | ||||||
|  | 	CloseMessageTooBig:           true, | ||||||
|  | 	CloseMandatoryExtension:      true, | ||||||
|  | 	CloseInternalServerErr:       true, | ||||||
|  | 	CloseServiceRestart:          true, | ||||||
|  | 	CloseTryAgainLater:           true, | ||||||
|  | 	CloseTLSHandshake:            false, | ||||||
| } | } | ||||||
|  |  | ||||||
| func newMaskKey() [4]byte { | func isValidReceivedCloseCode(code int) bool { | ||||||
| 	n := rand.Uint32() | 	return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) | ||||||
| 	return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Conn represents a WebSocket connection. | // The Conn type represents a WebSocket connection. | ||||||
| type Conn struct { | type Conn struct { | ||||||
| 	conn        net.Conn | 	conn        net.Conn | ||||||
| 	isServer    bool | 	isServer    bool | ||||||
| 	subprotocol string | 	subprotocol string | ||||||
|  |  | ||||||
| 	// Write fields | 	// Write fields | ||||||
| 	mu        chan bool // used as mutex to protect write to conn and closeSent | 	mu            chan bool // used as mutex to protect write to conn | ||||||
| 	closeSent bool      // true if close message was sent | 	writeBuf      []byte    // frame is constructed in this buffer. | ||||||
|  | 	writeDeadline time.Time | ||||||
|  | 	writer        io.WriteCloser // the current writer returned to the application | ||||||
|  | 	isWriting     bool           // for best-effort concurrent write detection | ||||||
|  |  | ||||||
| 	// Message writer fields. | 	writeErrMu sync.Mutex | ||||||
| 	writeErr       error | 	writeErr   error | ||||||
| 	writeBuf       []byte // frame is constructed in this buffer. |  | ||||||
| 	writePos       int    // end of data in writeBuf. | 	enableWriteCompression bool | ||||||
| 	writeFrameType int    // type of the current frame. | 	compressionLevel       int | ||||||
| 	writeSeq       int    // incremented to invalidate message writers. | 	newCompressionWriter   func(io.WriteCloser, int) io.WriteCloser | ||||||
| 	writeDeadline  time.Time |  | ||||||
| 	isWriting      bool // for best-effort concurrent write detection |  | ||||||
|  |  | ||||||
| 	// Read fields | 	// Read fields | ||||||
|  | 	reader        io.ReadCloser // the current reader returned to the application | ||||||
| 	readErr       error | 	readErr       error | ||||||
| 	br            *bufio.Reader | 	br            *bufio.Reader | ||||||
| 	readRemaining int64 // bytes remaining in current frame. | 	readRemaining int64 // bytes remaining in current frame. | ||||||
| 	readFinal     bool  // true the current message has more frames. | 	readFinal     bool  // true the current message has more frames. | ||||||
| 	readSeq       int   // incremented to invalidate message readers. |  | ||||||
| 	readLength    int64 // Message size. | 	readLength    int64 // Message size. | ||||||
| 	readLimit     int64 // Maximum message size. | 	readLimit     int64 // Maximum message size. | ||||||
| 	readMaskPos   int | 	readMaskPos   int | ||||||
| 	readMaskKey   [4]byte | 	readMaskKey   [4]byte | ||||||
| 	handlePong    func(string) error | 	handlePong    func(string) error | ||||||
| 	handlePing    func(string) error | 	handlePing    func(string) error | ||||||
|  | 	handleClose   func(int, string) error | ||||||
| 	readErrCount  int | 	readErrCount  int | ||||||
|  | 	messageReader *messageReader // the current low-level reader | ||||||
|  |  | ||||||
|  | 	readDecompress         bool // whether last read frame had RSV1 set | ||||||
|  | 	newDecompressionReader func(io.Reader) io.ReadCloser | ||||||
| } | } | ||||||
|  |  | ||||||
| func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { | func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { | ||||||
|  | 	return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type writeHook struct { | ||||||
|  | 	p []byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wh *writeHook) Write(p []byte) (int, error) { | ||||||
|  | 	wh.p = p | ||||||
|  | 	return len(p), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { | ||||||
| 	mu := make(chan bool, 1) | 	mu := make(chan bool, 1) | ||||||
| 	mu <- true | 	mu <- true | ||||||
|  |  | ||||||
| 	if readBufferSize == 0 { | 	var br *bufio.Reader | ||||||
| 		readBufferSize = defaultReadBufferSize | 	if readBufferSize == 0 && brw != nil && brw.Reader != nil { | ||||||
|  | 		// Reuse the supplied bufio.Reader if the buffer has a useful size. | ||||||
|  | 		// This code assumes that peek on a reader returns | ||||||
|  | 		// bufio.Reader.buf[:0]. | ||||||
|  | 		brw.Reader.Reset(conn) | ||||||
|  | 		if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { | ||||||
|  | 			br = brw.Reader | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	if writeBufferSize == 0 { | 	if br == nil { | ||||||
| 		writeBufferSize = defaultWriteBufferSize | 		if readBufferSize == 0 { | ||||||
|  | 			readBufferSize = defaultReadBufferSize | ||||||
|  | 		} | ||||||
|  | 		if readBufferSize < maxControlFramePayloadSize { | ||||||
|  | 			readBufferSize = maxControlFramePayloadSize | ||||||
|  | 		} | ||||||
|  | 		br = bufio.NewReaderSize(conn, readBufferSize) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var writeBuf []byte | ||||||
|  | 	if writeBufferSize == 0 && brw != nil && brw.Writer != nil { | ||||||
|  | 		// Use the bufio.Writer's buffer if the buffer has a useful size. This | ||||||
|  | 		// code assumes that bufio.Writer.buf[:1] is passed to the | ||||||
|  | 		// bufio.Writer's underlying writer. | ||||||
|  | 		var wh writeHook | ||||||
|  | 		brw.Writer.Reset(&wh) | ||||||
|  | 		brw.Writer.WriteByte(0) | ||||||
|  | 		brw.Flush() | ||||||
|  | 		if cap(wh.p) >= maxFrameHeaderSize+256 { | ||||||
|  | 			writeBuf = wh.p[:cap(wh.p)] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if writeBuf == nil { | ||||||
|  | 		if writeBufferSize == 0 { | ||||||
|  | 			writeBufferSize = defaultWriteBufferSize | ||||||
|  | 		} | ||||||
|  | 		writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c := &Conn{ | 	c := &Conn{ | ||||||
| 		isServer:       isServer, | 		isServer:               isServer, | ||||||
| 		br:             bufio.NewReaderSize(conn, readBufferSize), | 		br:                     br, | ||||||
| 		conn:           conn, | 		conn:                   conn, | ||||||
| 		mu:             mu, | 		mu:                     mu, | ||||||
| 		readFinal:      true, | 		readFinal:              true, | ||||||
| 		writeBuf:       make([]byte, writeBufferSize+maxFrameHeaderSize), | 		writeBuf:               writeBuf, | ||||||
| 		writeFrameType: noFrame, | 		enableWriteCompression: true, | ||||||
| 		writePos:       maxFrameHeaderSize, | 		compressionLevel:       defaultCompressionLevel, | ||||||
| 	} | 	} | ||||||
|  | 	c.SetCloseHandler(nil) | ||||||
| 	c.SetPingHandler(nil) | 	c.SetPingHandler(nil) | ||||||
| 	c.SetPongHandler(nil) | 	c.SetPongHandler(nil) | ||||||
| 	return c | 	return c | ||||||
| @@ -279,29 +360,40 @@ func (c *Conn) RemoteAddr() net.Addr { | |||||||
|  |  | ||||||
| // Write methods | // Write methods | ||||||
|  |  | ||||||
|  | func (c *Conn) writeFatal(err error) error { | ||||||
|  | 	err = hideTempErr(err) | ||||||
|  | 	c.writeErrMu.Lock() | ||||||
|  | 	if c.writeErr == nil { | ||||||
|  | 		c.writeErr = err | ||||||
|  | 	} | ||||||
|  | 	c.writeErrMu.Unlock() | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { | func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { | ||||||
| 	<-c.mu | 	<-c.mu | ||||||
| 	defer func() { c.mu <- true }() | 	defer func() { c.mu <- true }() | ||||||
|  |  | ||||||
| 	if c.closeSent { | 	c.writeErrMu.Lock() | ||||||
| 		return ErrCloseSent | 	err := c.writeErr | ||||||
| 	} else if frameType == CloseMessage { | 	c.writeErrMu.Unlock() | ||||||
| 		c.closeSent = true | 	if err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c.conn.SetWriteDeadline(deadline) | 	c.conn.SetWriteDeadline(deadline) | ||||||
| 	for _, buf := range bufs { | 	for _, buf := range bufs { | ||||||
| 		if len(buf) > 0 { | 		if len(buf) > 0 { | ||||||
| 			n, err := c.conn.Write(buf) | 			_, err := c.conn.Write(buf) | ||||||
| 			if n != len(buf) { |  | ||||||
| 				// Close on partial write. |  | ||||||
| 				c.conn.Close() |  | ||||||
| 			} |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return c.writeFatal(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if frameType == CloseMessage { | ||||||
|  | 		c.writeFatal(ErrCloseSent) | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -350,60 +442,104 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||||||
| 	} | 	} | ||||||
| 	defer func() { c.mu <- true }() | 	defer func() { c.mu <- true }() | ||||||
|  |  | ||||||
| 	if c.closeSent { | 	c.writeErrMu.Lock() | ||||||
| 		return ErrCloseSent | 	err := c.writeErr | ||||||
| 	} else if messageType == CloseMessage { | 	c.writeErrMu.Unlock() | ||||||
| 		c.closeSent = true | 	if err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c.conn.SetWriteDeadline(deadline) | 	c.conn.SetWriteDeadline(deadline) | ||||||
| 	n, err := c.conn.Write(buf) | 	_, err = c.conn.Write(buf) | ||||||
| 	if n != 0 && n != len(buf) { | 	if err != nil { | ||||||
| 		c.conn.Close() | 		return c.writeFatal(err) | ||||||
| 	} | 	} | ||||||
| 	return hideTempErr(err) | 	if messageType == CloseMessage { | ||||||
|  | 		c.writeFatal(ErrCloseSent) | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| // NextWriter returns a writer for the next message to send.  The writer's | func (c *Conn) prepWrite(messageType int) error { | ||||||
| // Close method flushes the complete message to the network. | 	// Close previous writer if not already closed by the application. It's | ||||||
|  | 	// probably better to return an error in this situation, but we cannot | ||||||
|  | 	// change this without breaking existing applications. | ||||||
|  | 	if c.writer != nil { | ||||||
|  | 		c.writer.Close() | ||||||
|  | 		c.writer = nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !isControl(messageType) && !isData(messageType) { | ||||||
|  | 		return errBadWriteOpCode | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.writeErrMu.Lock() | ||||||
|  | 	err := c.writeErr | ||||||
|  | 	c.writeErrMu.Unlock() | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NextWriter returns a writer for the next message to send. The writer's Close | ||||||
|  | // method flushes the complete message to the network. | ||||||
| // | // | ||||||
| // There can be at most one open writer on a connection. NextWriter closes the | // There can be at most one open writer on a connection. NextWriter closes the | ||||||
| // previous writer if the application has not already done so. | // previous writer if the application has not already done so. | ||||||
| func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { | func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { | ||||||
| 	if c.writeErr != nil { | 	if err := c.prepWrite(messageType); err != nil { | ||||||
| 		return nil, c.writeErr | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if c.writeFrameType != noFrame { | 	mw := &messageWriter{ | ||||||
| 		if err := c.flushFrame(true, nil); err != nil { | 		c:         c, | ||||||
| 			return nil, err | 		frameType: messageType, | ||||||
| 		} | 		pos:       maxFrameHeaderSize, | ||||||
| 	} | 	} | ||||||
|  | 	c.writer = mw | ||||||
| 	if !isControl(messageType) && !isData(messageType) { | 	if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { | ||||||
| 		return nil, errBadWriteOpCode | 		w := c.newCompressionWriter(c.writer, c.compressionLevel) | ||||||
|  | 		mw.compress = true | ||||||
|  | 		c.writer = w | ||||||
| 	} | 	} | ||||||
|  | 	return c.writer, nil | ||||||
| 	c.writeFrameType = messageType |  | ||||||
| 	return messageWriter{c, c.writeSeq}, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Conn) flushFrame(final bool, extra []byte) error { | type messageWriter struct { | ||||||
| 	length := c.writePos - maxFrameHeaderSize + len(extra) | 	c         *Conn | ||||||
|  | 	compress  bool // whether next call to flushFrame should set RSV1 | ||||||
|  | 	pos       int  // end of data in writeBuf. | ||||||
|  | 	frameType int  // type of the current frame. | ||||||
|  | 	err       error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w *messageWriter) fatal(err error) error { | ||||||
|  | 	if w.err != nil { | ||||||
|  | 		w.err = err | ||||||
|  | 		w.c.writer = nil | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // flushFrame writes buffered data and extra as a frame to the network. The | ||||||
|  | // final argument indicates that this is the last frame in the message. | ||||||
|  | func (w *messageWriter) flushFrame(final bool, extra []byte) error { | ||||||
|  | 	c := w.c | ||||||
|  | 	length := w.pos - maxFrameHeaderSize + len(extra) | ||||||
|  |  | ||||||
| 	// Check for invalid control frames. | 	// Check for invalid control frames. | ||||||
| 	if isControl(c.writeFrameType) && | 	if isControl(w.frameType) && | ||||||
| 		(!final || length > maxControlFramePayloadSize) { | 		(!final || length > maxControlFramePayloadSize) { | ||||||
| 		c.writeSeq++ | 		return w.fatal(errInvalidControlFrame) | ||||||
| 		c.writeFrameType = noFrame |  | ||||||
| 		c.writePos = maxFrameHeaderSize |  | ||||||
| 		return errInvalidControlFrame |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b0 := byte(c.writeFrameType) | 	b0 := byte(w.frameType) | ||||||
| 	if final { | 	if final { | ||||||
| 		b0 |= finalBit | 		b0 |= finalBit | ||||||
| 	} | 	} | ||||||
|  | 	if w.compress { | ||||||
|  | 		b0 |= rsv1Bit | ||||||
|  | 	} | ||||||
|  | 	w.compress = false | ||||||
|  |  | ||||||
| 	b1 := byte(0) | 	b1 := byte(0) | ||||||
| 	if !c.isServer { | 	if !c.isServer { | ||||||
| 		b1 |= maskBit | 		b1 |= maskBit | ||||||
| @@ -435,10 +571,9 @@ func (c *Conn) flushFrame(final bool, extra []byte) error { | |||||||
| 	if !c.isServer { | 	if !c.isServer { | ||||||
| 		key := newMaskKey() | 		key := newMaskKey() | ||||||
| 		copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | 		copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | ||||||
| 		maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos]) | 		maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) | ||||||
| 		if len(extra) > 0 { | 		if len(extra) > 0 { | ||||||
| 			c.writeErr = errors.New("websocket: internal error, extra used in client mode") | 			return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) | ||||||
| 			return c.writeErr |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -451,46 +586,35 @@ func (c *Conn) flushFrame(final bool, extra []byte) error { | |||||||
| 	} | 	} | ||||||
| 	c.isWriting = true | 	c.isWriting = true | ||||||
|  |  | ||||||
| 	c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra) | 	err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) | ||||||
|  |  | ||||||
| 	if !c.isWriting { | 	if !c.isWriting { | ||||||
| 		panic("concurrent write to websocket connection") | 		panic("concurrent write to websocket connection") | ||||||
| 	} | 	} | ||||||
| 	c.isWriting = false | 	c.isWriting = false | ||||||
|  |  | ||||||
| 	// Setup for next frame. | 	if err != nil { | ||||||
| 	c.writePos = maxFrameHeaderSize | 		return w.fatal(err) | ||||||
| 	c.writeFrameType = continuationFrame | 	} | ||||||
|  |  | ||||||
| 	if final { | 	if final { | ||||||
| 		c.writeSeq++ | 		c.writer = nil | ||||||
| 		c.writeFrameType = noFrame | 		return nil | ||||||
| 	} | 	} | ||||||
| 	return c.writeErr |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type messageWriter struct { | 	// Setup for next frame. | ||||||
| 	c   *Conn | 	w.pos = maxFrameHeaderSize | ||||||
| 	seq int | 	w.frameType = continuationFrame | ||||||
| } |  | ||||||
|  |  | ||||||
| func (w messageWriter) err() error { |  | ||||||
| 	c := w.c |  | ||||||
| 	if c.writeSeq != w.seq { |  | ||||||
| 		return errWriteClosed |  | ||||||
| 	} |  | ||||||
| 	if c.writeErr != nil { |  | ||||||
| 		return c.writeErr |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w messageWriter) ncopy(max int) (int, error) { | func (w *messageWriter) ncopy(max int) (int, error) { | ||||||
| 	n := len(w.c.writeBuf) - w.c.writePos | 	n := len(w.c.writeBuf) - w.pos | ||||||
| 	if n <= 0 { | 	if n <= 0 { | ||||||
| 		if err := w.c.flushFrame(false, nil); err != nil { | 		if err := w.flushFrame(false, nil); err != nil { | ||||||
| 			return 0, err | 			return 0, err | ||||||
| 		} | 		} | ||||||
| 		n = len(w.c.writeBuf) - w.c.writePos | 		n = len(w.c.writeBuf) - w.pos | ||||||
| 	} | 	} | ||||||
| 	if n > max { | 	if n > max { | ||||||
| 		n = max | 		n = max | ||||||
| @@ -498,14 +622,14 @@ func (w messageWriter) ncopy(max int) (int, error) { | |||||||
| 	return n, nil | 	return n, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w messageWriter) write(final bool, p []byte) (int, error) { | func (w *messageWriter) Write(p []byte) (int, error) { | ||||||
| 	if err := w.err(); err != nil { | 	if w.err != nil { | ||||||
| 		return 0, err | 		return 0, w.err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { | 	if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { | ||||||
| 		// Don't buffer large messages. | 		// Don't buffer large messages. | ||||||
| 		err := w.c.flushFrame(final, p) | 		err := w.flushFrame(false, p) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return 0, err | 			return 0, err | ||||||
| 		} | 		} | ||||||
| @@ -518,20 +642,16 @@ func (w messageWriter) write(final bool, p []byte) (int, error) { | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return 0, err | 			return 0, err | ||||||
| 		} | 		} | ||||||
| 		copy(w.c.writeBuf[w.c.writePos:], p[:n]) | 		copy(w.c.writeBuf[w.pos:], p[:n]) | ||||||
| 		w.c.writePos += n | 		w.pos += n | ||||||
| 		p = p[n:] | 		p = p[n:] | ||||||
| 	} | 	} | ||||||
| 	return nn, nil | 	return nn, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w messageWriter) Write(p []byte) (int, error) { | func (w *messageWriter) WriteString(p string) (int, error) { | ||||||
| 	return w.write(false, p) | 	if w.err != nil { | ||||||
| } | 		return 0, w.err | ||||||
|  |  | ||||||
| func (w messageWriter) WriteString(p string) (int, error) { |  | ||||||
| 	if err := w.err(); err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	nn := len(p) | 	nn := len(p) | ||||||
| @@ -540,27 +660,27 @@ func (w messageWriter) WriteString(p string) (int, error) { | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return 0, err | 			return 0, err | ||||||
| 		} | 		} | ||||||
| 		copy(w.c.writeBuf[w.c.writePos:], p[:n]) | 		copy(w.c.writeBuf[w.pos:], p[:n]) | ||||||
| 		w.c.writePos += n | 		w.pos += n | ||||||
| 		p = p[n:] | 		p = p[n:] | ||||||
| 	} | 	} | ||||||
| 	return nn, nil | 	return nn, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { | func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { | ||||||
| 	if err := w.err(); err != nil { | 	if w.err != nil { | ||||||
| 		return 0, err | 		return 0, w.err | ||||||
| 	} | 	} | ||||||
| 	for { | 	for { | ||||||
| 		if w.c.writePos == len(w.c.writeBuf) { | 		if w.pos == len(w.c.writeBuf) { | ||||||
| 			err = w.c.flushFrame(false, nil) | 			err = w.flushFrame(false, nil) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		var n int | 		var n int | ||||||
| 		n, err = r.Read(w.c.writeBuf[w.c.writePos:]) | 		n, err = r.Read(w.c.writeBuf[w.pos:]) | ||||||
| 		w.c.writePos += n | 		w.pos += n | ||||||
| 		nn += int64(n) | 		nn += int64(n) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if err == io.EOF { | 			if err == io.EOF { | ||||||
| @@ -572,30 +692,64 @@ func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { | |||||||
| 	return nn, err | 	return nn, err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w messageWriter) Close() error { | func (w *messageWriter) Close() error { | ||||||
| 	if err := w.err(); err != nil { | 	if w.err != nil { | ||||||
|  | 		return w.err | ||||||
|  | 	} | ||||||
|  | 	if err := w.flushFrame(true, nil); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return w.c.flushFrame(true, nil) | 	w.err = errWriteClosed | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WritePreparedMessage writes prepared message into connection. | ||||||
|  | func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { | ||||||
|  | 	frameType, frameData, err := pm.frame(prepareKey{ | ||||||
|  | 		isServer:         c.isServer, | ||||||
|  | 		compress:         c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), | ||||||
|  | 		compressionLevel: c.compressionLevel, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if c.isWriting { | ||||||
|  | 		panic("concurrent write to websocket connection") | ||||||
|  | 	} | ||||||
|  | 	c.isWriting = true | ||||||
|  | 	err = c.write(frameType, c.writeDeadline, frameData, nil) | ||||||
|  | 	if !c.isWriting { | ||||||
|  | 		panic("concurrent write to websocket connection") | ||||||
|  | 	} | ||||||
|  | 	c.isWriting = false | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| // WriteMessage is a helper method for getting a writer using NextWriter, | // WriteMessage is a helper method for getting a writer using NextWriter, | ||||||
| // writing the message and closing the writer. | // writing the message and closing the writer. | ||||||
| func (c *Conn) WriteMessage(messageType int, data []byte) error { | func (c *Conn) WriteMessage(messageType int, data []byte) error { | ||||||
| 	wr, err := c.NextWriter(messageType) |  | ||||||
|  | 	if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { | ||||||
|  | 		// Fast path with no allocations and single frame. | ||||||
|  |  | ||||||
|  | 		if err := c.prepWrite(messageType); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} | ||||||
|  | 		n := copy(c.writeBuf[mw.pos:], data) | ||||||
|  | 		mw.pos += n | ||||||
|  | 		data = data[n:] | ||||||
|  | 		return mw.flushFrame(true, data) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	w, err := c.NextWriter(messageType) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	w := wr.(messageWriter) | 	if _, err = w.Write(data); err != nil { | ||||||
| 	if _, err := w.write(true, data); err != nil { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if c.writeSeq == w.seq { | 	return w.Close() | ||||||
| 		if err := c.flushFrame(true, nil); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetWriteDeadline sets the write deadline on the underlying network | // SetWriteDeadline sets the write deadline on the underlying network | ||||||
| @@ -609,22 +763,6 @@ func (c *Conn) SetWriteDeadline(t time.Time) error { | |||||||
|  |  | ||||||
| // Read methods | // Read methods | ||||||
|  |  | ||||||
| // readFull is like io.ReadFull except that io.EOF is never returned. |  | ||||||
| func (c *Conn) readFull(p []byte) (err error) { |  | ||||||
| 	var n int |  | ||||||
| 	for n < len(p) && err == nil { |  | ||||||
| 		var nn int |  | ||||||
| 		nn, err = c.br.Read(p[n:]) |  | ||||||
| 		n += nn |  | ||||||
| 	} |  | ||||||
| 	if n == len(p) { |  | ||||||
| 		err = nil |  | ||||||
| 	} else if err == io.EOF { |  | ||||||
| 		err = errUnexpectedEOF |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Conn) advanceFrame() (int, error) { | func (c *Conn) advanceFrame() (int, error) { | ||||||
|  |  | ||||||
| 	// 1. Skip remainder of previous frame. | 	// 1. Skip remainder of previous frame. | ||||||
| @@ -637,19 +775,24 @@ func (c *Conn) advanceFrame() (int, error) { | |||||||
|  |  | ||||||
| 	// 2. Read and parse first two bytes of frame header. | 	// 2. Read and parse first two bytes of frame header. | ||||||
|  |  | ||||||
| 	var b [8]byte | 	p, err := c.read(2) | ||||||
| 	if err := c.readFull(b[:2]); err != nil { | 	if err != nil { | ||||||
| 		return noFrame, err | 		return noFrame, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	final := b[0]&finalBit != 0 | 	final := p[0]&finalBit != 0 | ||||||
| 	frameType := int(b[0] & 0xf) | 	frameType := int(p[0] & 0xf) | ||||||
| 	reserved := int((b[0] >> 4) & 0x7) | 	mask := p[1]&maskBit != 0 | ||||||
| 	mask := b[1]&maskBit != 0 | 	c.readRemaining = int64(p[1] & 0x7f) | ||||||
| 	c.readRemaining = int64(b[1] & 0x7f) |  | ||||||
|  |  | ||||||
| 	if reserved != 0 { | 	c.readDecompress = false | ||||||
| 		return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved)) | 	if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { | ||||||
|  | 		c.readDecompress = true | ||||||
|  | 		p[0] &^= rsv1Bit | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { | ||||||
|  | 		return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch frameType { | 	switch frameType { | ||||||
| @@ -678,15 +821,17 @@ func (c *Conn) advanceFrame() (int, error) { | |||||||
|  |  | ||||||
| 	switch c.readRemaining { | 	switch c.readRemaining { | ||||||
| 	case 126: | 	case 126: | ||||||
| 		if err := c.readFull(b[:2]); err != nil { | 		p, err := c.read(2) | ||||||
|  | 		if err != nil { | ||||||
| 			return noFrame, err | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
| 		c.readRemaining = int64(binary.BigEndian.Uint16(b[:2])) | 		c.readRemaining = int64(binary.BigEndian.Uint16(p)) | ||||||
| 	case 127: | 	case 127: | ||||||
| 		if err := c.readFull(b[:8]); err != nil { | 		p, err := c.read(8) | ||||||
|  | 		if err != nil { | ||||||
| 			return noFrame, err | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
| 		c.readRemaining = int64(binary.BigEndian.Uint64(b[:8])) | 		c.readRemaining = int64(binary.BigEndian.Uint64(p)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 4. Handle frame masking. | 	// 4. Handle frame masking. | ||||||
| @@ -697,9 +842,11 @@ func (c *Conn) advanceFrame() (int, error) { | |||||||
|  |  | ||||||
| 	if mask { | 	if mask { | ||||||
| 		c.readMaskPos = 0 | 		c.readMaskPos = 0 | ||||||
| 		if err := c.readFull(c.readMaskKey[:]); err != nil { | 		p, err := c.read(len(c.readMaskKey)) | ||||||
|  | 		if err != nil { | ||||||
| 			return noFrame, err | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
|  | 		copy(c.readMaskKey[:], p) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 5. For text and binary messages, enforce read limit and return. | 	// 5. For text and binary messages, enforce read limit and return. | ||||||
| @@ -719,9 +866,9 @@ func (c *Conn) advanceFrame() (int, error) { | |||||||
|  |  | ||||||
| 	var payload []byte | 	var payload []byte | ||||||
| 	if c.readRemaining > 0 { | 	if c.readRemaining > 0 { | ||||||
| 		payload = make([]byte, c.readRemaining) | 		payload, err = c.read(int(c.readRemaining)) | ||||||
| 		c.readRemaining = 0 | 		c.readRemaining = 0 | ||||||
| 		if err := c.readFull(payload); err != nil { | 		if err != nil { | ||||||
| 			return noFrame, err | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
| 		if c.isServer { | 		if c.isServer { | ||||||
| @@ -741,15 +888,21 @@ func (c *Conn) advanceFrame() (int, error) { | |||||||
| 			return noFrame, err | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
| 	case CloseMessage: | 	case CloseMessage: | ||||||
| 		echoMessage := []byte{} |  | ||||||
| 		closeCode := CloseNoStatusReceived | 		closeCode := CloseNoStatusReceived | ||||||
| 		closeText := "" | 		closeText := "" | ||||||
| 		if len(payload) >= 2 { | 		if len(payload) >= 2 { | ||||||
| 			echoMessage = payload[:2] |  | ||||||
| 			closeCode = int(binary.BigEndian.Uint16(payload)) | 			closeCode = int(binary.BigEndian.Uint16(payload)) | ||||||
|  | 			if !isValidReceivedCloseCode(closeCode) { | ||||||
|  | 				return noFrame, c.handleProtocolError("invalid close code") | ||||||
|  | 			} | ||||||
| 			closeText = string(payload[2:]) | 			closeText = string(payload[2:]) | ||||||
|  | 			if !utf8.ValidString(closeText) { | ||||||
|  | 				return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if err := c.handleClose(closeCode, closeText); err != nil { | ||||||
|  | 			return noFrame, err | ||||||
| 		} | 		} | ||||||
| 		c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait)) |  | ||||||
| 		return noFrame, &CloseError{Code: closeCode, Text: closeText} | 		return noFrame, &CloseError{Code: closeCode, Text: closeText} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -772,8 +925,13 @@ func (c *Conn) handleProtocolError(message string) error { | |||||||
| // permanent. Once this method returns a non-nil error, all subsequent calls to | // permanent. Once this method returns a non-nil error, all subsequent calls to | ||||||
| // this method return the same error. | // this method return the same error. | ||||||
| func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | ||||||
|  | 	// Close previous reader, only relevant for decompression. | ||||||
|  | 	if c.reader != nil { | ||||||
|  | 		c.reader.Close() | ||||||
|  | 		c.reader = nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	c.readSeq++ | 	c.messageReader = nil | ||||||
| 	c.readLength = 0 | 	c.readLength = 0 | ||||||
|  |  | ||||||
| 	for c.readErr == nil { | 	for c.readErr == nil { | ||||||
| @@ -783,7 +941,12 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		if frameType == TextMessage || frameType == BinaryMessage { | 		if frameType == TextMessage || frameType == BinaryMessage { | ||||||
| 			return frameType, messageReader{c, c.readSeq}, nil | 			c.messageReader = &messageReader{c} | ||||||
|  | 			c.reader = c.messageReader | ||||||
|  | 			if c.readDecompress { | ||||||
|  | 				c.reader = c.newDecompressionReader(c.reader) | ||||||
|  | 			} | ||||||
|  | 			return frameType, c.reader, nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -798,53 +961,57 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | |||||||
| 	return noFrame, nil, c.readErr | 	return noFrame, nil, c.readErr | ||||||
| } | } | ||||||
|  |  | ||||||
| type messageReader struct { | type messageReader struct{ c *Conn } | ||||||
| 	c   *Conn |  | ||||||
| 	seq int |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r messageReader) Read(b []byte) (int, error) { | func (r *messageReader) Read(b []byte) (int, error) { | ||||||
|  | 	c := r.c | ||||||
| 	if r.seq != r.c.readSeq { | 	if c.messageReader != r { | ||||||
| 		return 0, io.EOF | 		return 0, io.EOF | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for r.c.readErr == nil { | 	for c.readErr == nil { | ||||||
|  |  | ||||||
| 		if r.c.readRemaining > 0 { | 		if c.readRemaining > 0 { | ||||||
| 			if int64(len(b)) > r.c.readRemaining { | 			if int64(len(b)) > c.readRemaining { | ||||||
| 				b = b[:r.c.readRemaining] | 				b = b[:c.readRemaining] | ||||||
| 			} | 			} | ||||||
| 			n, err := r.c.br.Read(b) | 			n, err := c.br.Read(b) | ||||||
| 			r.c.readErr = hideTempErr(err) | 			c.readErr = hideTempErr(err) | ||||||
| 			if r.c.isServer { | 			if c.isServer { | ||||||
| 				r.c.readMaskPos = maskBytes(r.c.readMaskKey, r.c.readMaskPos, b[:n]) | 				c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) | ||||||
| 			} | 			} | ||||||
| 			r.c.readRemaining -= int64(n) | 			c.readRemaining -= int64(n) | ||||||
| 			return n, r.c.readErr | 			if c.readRemaining > 0 && c.readErr == io.EOF { | ||||||
|  | 				c.readErr = errUnexpectedEOF | ||||||
|  | 			} | ||||||
|  | 			return n, c.readErr | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if r.c.readFinal { | 		if c.readFinal { | ||||||
| 			r.c.readSeq++ | 			c.messageReader = nil | ||||||
| 			return 0, io.EOF | 			return 0, io.EOF | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		frameType, err := r.c.advanceFrame() | 		frameType, err := c.advanceFrame() | ||||||
| 		switch { | 		switch { | ||||||
| 		case err != nil: | 		case err != nil: | ||||||
| 			r.c.readErr = hideTempErr(err) | 			c.readErr = hideTempErr(err) | ||||||
| 		case frameType == TextMessage || frameType == BinaryMessage: | 		case frameType == TextMessage || frameType == BinaryMessage: | ||||||
| 			r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") | 			c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err := r.c.readErr | 	err := c.readErr | ||||||
| 	if err == io.EOF && r.seq == r.c.readSeq { | 	if err == io.EOF && c.messageReader == r { | ||||||
| 		err = errUnexpectedEOF | 		err = errUnexpectedEOF | ||||||
| 	} | 	} | ||||||
| 	return 0, err | 	return 0, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (r *messageReader) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // ReadMessage is a helper method for getting a reader using NextReader and | // ReadMessage is a helper method for getting a reader using NextReader and | ||||||
| // reading from that reader to a buffer. | // reading from that reader to a buffer. | ||||||
| func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { | func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { | ||||||
| @@ -872,9 +1039,49 @@ func (c *Conn) SetReadLimit(limit int64) { | |||||||
| 	c.readLimit = limit | 	c.readLimit = limit | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // CloseHandler returns the current close handler | ||||||
|  | func (c *Conn) CloseHandler() func(code int, text string) error { | ||||||
|  | 	return c.handleClose | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetCloseHandler sets the handler for close messages received from the peer. | ||||||
|  | // The code argument to h is the received close code or CloseNoStatusReceived | ||||||
|  | // if the close message is empty. The default close handler sends a close frame | ||||||
|  | // back to the peer. | ||||||
|  | // | ||||||
|  | // The application must read the connection to process close messages as | ||||||
|  | // described in the section on Control Frames above. | ||||||
|  | // | ||||||
|  | // The connection read methods return a CloseError when a close frame is | ||||||
|  | // received. Most applications should handle close messages as part of their | ||||||
|  | // normal error handling. Applications should only set a close handler when the | ||||||
|  | // application must perform some action before sending a close frame back to | ||||||
|  | // the peer. | ||||||
|  | func (c *Conn) SetCloseHandler(h func(code int, text string) error) { | ||||||
|  | 	if h == nil { | ||||||
|  | 		h = func(code int, text string) error { | ||||||
|  | 			message := []byte{} | ||||||
|  | 			if code != CloseNoStatusReceived { | ||||||
|  | 				message = FormatCloseMessage(code, "") | ||||||
|  | 			} | ||||||
|  | 			c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	c.handleClose = h | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PingHandler returns the current ping handler | ||||||
|  | func (c *Conn) PingHandler() func(appData string) error { | ||||||
|  | 	return c.handlePing | ||||||
|  | } | ||||||
|  |  | ||||||
| // SetPingHandler sets the handler for ping messages received from the peer. | // SetPingHandler sets the handler for ping messages received from the peer. | ||||||
| // The appData argument to h is the PING frame application data. The default | // The appData argument to h is the PING frame application data. The default | ||||||
| // ping handler sends a pong to the peer. | // ping handler sends a pong to the peer. | ||||||
|  | // | ||||||
|  | // The application must read the connection to process ping messages as | ||||||
|  | // described in the section on Control Frames above. | ||||||
| func (c *Conn) SetPingHandler(h func(appData string) error) { | func (c *Conn) SetPingHandler(h func(appData string) error) { | ||||||
| 	if h == nil { | 	if h == nil { | ||||||
| 		h = func(message string) error { | 		h = func(message string) error { | ||||||
| @@ -890,9 +1097,17 @@ func (c *Conn) SetPingHandler(h func(appData string) error) { | |||||||
| 	c.handlePing = h | 	c.handlePing = h | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // PongHandler returns the current pong handler | ||||||
|  | func (c *Conn) PongHandler() func(appData string) error { | ||||||
|  | 	return c.handlePong | ||||||
|  | } | ||||||
|  |  | ||||||
| // SetPongHandler sets the handler for pong messages received from the peer. | // SetPongHandler sets the handler for pong messages received from the peer. | ||||||
| // The appData argument to h is the PONG frame application data. The default | // The appData argument to h is the PONG frame application data. The default | ||||||
| // pong handler does nothing. | // pong handler does nothing. | ||||||
|  | // | ||||||
|  | // The application must read the connection to process ping messages as | ||||||
|  | // described in the section on Control Frames above. | ||||||
| func (c *Conn) SetPongHandler(h func(appData string) error) { | func (c *Conn) SetPongHandler(h func(appData string) error) { | ||||||
| 	if h == nil { | 	if h == nil { | ||||||
| 		h = func(string) error { return nil } | 		h = func(string) error { return nil } | ||||||
| @@ -906,6 +1121,25 @@ func (c *Conn) UnderlyingConn() net.Conn { | |||||||
| 	return c.conn | 	return c.conn | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // EnableWriteCompression enables and disables write compression of | ||||||
|  | // subsequent text and binary messages. This function is a noop if | ||||||
|  | // compression was not negotiated with the peer. | ||||||
|  | func (c *Conn) EnableWriteCompression(enable bool) { | ||||||
|  | 	c.enableWriteCompression = enable | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetCompressionLevel sets the flate compression level for subsequent text and | ||||||
|  | // binary messages. This function is a noop if compression was not negotiated | ||||||
|  | // with the peer. See the compress/flate package for a description of | ||||||
|  | // compression levels. | ||||||
|  | func (c *Conn) SetCompressionLevel(level int) error { | ||||||
|  | 	if !isValidCompressionLevel(level) { | ||||||
|  | 		return errors.New("websocket: invalid compression level") | ||||||
|  | 	} | ||||||
|  | 	c.compressionLevel = level | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // FormatCloseMessage formats closeCode and text as a WebSocket close message. | // FormatCloseMessage formats closeCode and text as a WebSocket close message. | ||||||
| func FormatCloseMessage(closeCode int, text string) []byte { | func FormatCloseMessage(closeCode int, text string) []byte { | ||||||
| 	buf := make([]byte, 2+len(text)) | 	buf := make([]byte, 2+len(text)) | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								vendor/github.com/gorilla/websocket/conn_read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/gorilla/websocket/conn_read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build go1.5 | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import "io" | ||||||
|  |  | ||||||
|  | func (c *Conn) read(n int) ([]byte, error) { | ||||||
|  | 	p, err := c.br.Peek(n) | ||||||
|  | 	if err == io.EOF { | ||||||
|  | 		err = errUnexpectedEOF | ||||||
|  | 	} | ||||||
|  | 	c.br.Discard(len(p)) | ||||||
|  | 	return p, err | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								vendor/github.com/gorilla/websocket/conn_read_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/gorilla/websocket/conn_read_legacy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build !go1.5 | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import "io" | ||||||
|  |  | ||||||
|  | func (c *Conn) read(n int) ([]byte, error) { | ||||||
|  | 	p, err := c.br.Peek(n) | ||||||
|  | 	if err == io.EOF { | ||||||
|  | 		err = errUnexpectedEOF | ||||||
|  | 	} | ||||||
|  | 	if len(p) > 0 { | ||||||
|  | 		// advance over the bytes just read | ||||||
|  | 		io.ReadFull(c.br, p) | ||||||
|  | 	} | ||||||
|  | 	return p, err | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -118,9 +118,10 @@ | |||||||
| // | // | ||||||
| // Applications are responsible for ensuring that no more than one goroutine | // Applications are responsible for ensuring that no more than one goroutine | ||||||
| // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, | // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, | ||||||
| // WriteJSON) concurrently and that no more than one goroutine calls the read | // WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and | ||||||
| // methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, | // that no more than one goroutine calls the read methods (NextReader, | ||||||
| // SetPingHandler) concurrently. | // SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) | ||||||
|  | // concurrently. | ||||||
| // | // | ||||||
| // The Close and WriteControl methods can be called concurrently with all other | // The Close and WriteControl methods can be called concurrently with all other | ||||||
| // methods. | // methods. | ||||||
| @@ -149,4 +150,31 @@ | |||||||
| // The deprecated Upgrade function does not enforce an origin policy. It's the | // The deprecated Upgrade function does not enforce an origin policy. It's the | ||||||
| // application's responsibility to check the Origin header before calling | // application's responsibility to check the Origin header before calling | ||||||
| // Upgrade. | // Upgrade. | ||||||
|  | // | ||||||
|  | // Compression EXPERIMENTAL | ||||||
|  | // | ||||||
|  | // Per message compression extensions (RFC 7692) are experimentally supported | ||||||
|  | // by this package in a limited capacity. Setting the EnableCompression option | ||||||
|  | // to true in Dialer or Upgrader will attempt to negotiate per message deflate | ||||||
|  | // support. | ||||||
|  | // | ||||||
|  | //  var upgrader = websocket.Upgrader{ | ||||||
|  | //      EnableCompression: true, | ||||||
|  | //  } | ||||||
|  | // | ||||||
|  | // If compression was successfully negotiated with the connection's peer, any | ||||||
|  | // message received in compressed form will be automatically decompressed. | ||||||
|  | // All Read methods will return uncompressed bytes. | ||||||
|  | // | ||||||
|  | // Per message compression of messages written to a connection can be enabled | ||||||
|  | // or disabled by calling the corresponding Conn method: | ||||||
|  | // | ||||||
|  | //  conn.EnableWriteCompression(false) | ||||||
|  | // | ||||||
|  | // Currently this package does not support compression with "context takeover". | ||||||
|  | // This means that messages must be compressed and decompressed in isolation, | ||||||
|  | // without retaining sliding window or dictionary state across messages. For | ||||||
|  | // more details refer to RFC 7692. | ||||||
|  | // | ||||||
|  | // Use of compression is experimental and may result in decreased performance. | ||||||
| package websocket | package websocket | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								vendor/github.com/gorilla/websocket/examples/autobahn/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/gorilla/websocket/examples/autobahn/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,17 +8,19 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"github.com/gorilla/websocket" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"time" | 	"time" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
|  |  | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var upgrader = websocket.Upgrader{ | var upgrader = websocket.Upgrader{ | ||||||
| 	ReadBufferSize:  4096, | 	ReadBufferSize:    4096, | ||||||
| 	WriteBufferSize: 4096, | 	WriteBufferSize:   4096, | ||||||
|  | 	EnableCompression: true, | ||||||
| 	CheckOrigin: func(r *http.Request) bool { | 	CheckOrigin: func(r *http.Request) bool { | ||||||
| 		return true | 		return true | ||||||
| 	}, | 	}, | ||||||
| @@ -83,7 +85,7 @@ func echoCopyFull(w http.ResponseWriter, r *http.Request) { | |||||||
|  |  | ||||||
| // echoReadAll echoes messages from the client by reading the entire message | // echoReadAll echoes messages from the client by reading the entire message | ||||||
| // with ioutil.ReadAll. | // with ioutil.ReadAll. | ||||||
| func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { | func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) { | ||||||
| 	conn, err := upgrader.Upgrade(w, r, nil) | 	conn, err := upgrader.Upgrade(w, r, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Println("Upgrade:", err) | 		log.Println("Upgrade:", err) | ||||||
| @@ -107,9 +109,21 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if writeMessage { | 		if writeMessage { | ||||||
| 			err = conn.WriteMessage(mt, b) | 			if !writePrepared { | ||||||
| 			if err != nil { | 				err = conn.WriteMessage(mt, b) | ||||||
| 				log.Println("WriteMessage:", err) | 				if err != nil { | ||||||
|  | 					log.Println("WriteMessage:", err) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				pm, err := websocket.NewPreparedMessage(mt, b) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Println("NewPreparedMessage:", err) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				err = conn.WritePreparedMessage(pm) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Println("WritePreparedMessage:", err) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			w, err := conn.NextWriter(mt) | 			w, err := conn.NextWriter(mt) | ||||||
| @@ -130,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { | func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { | ||||||
| 	echoReadAll(w, r, false) | 	echoReadAll(w, r, false, false) | ||||||
| } | } | ||||||
|  |  | ||||||
| func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { | func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { | ||||||
| 	echoReadAll(w, r, true) | 	echoReadAll(w, r, true, false) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	echoReadAll(w, r, true, true) | ||||||
| } | } | ||||||
|  |  | ||||||
| func serveHome(w http.ResponseWriter, r *http.Request) { | func serveHome(w http.ResponseWriter, r *http.Request) { | ||||||
| @@ -159,6 +177,7 @@ func main() { | |||||||
| 	http.HandleFunc("/f", echoCopyFull) | 	http.HandleFunc("/f", echoCopyFull) | ||||||
| 	http.HandleFunc("/r", echoReadAllWriter) | 	http.HandleFunc("/r", echoReadAllWriter) | ||||||
| 	http.HandleFunc("/m", echoReadAllWriteMessage) | 	http.HandleFunc("/m", echoReadAllWriteMessage) | ||||||
|  | 	http.HandleFunc("/p", echoReadAllWritePreparedMessage) | ||||||
| 	err := http.ListenAndServe(*addr, nil) | 	err := http.ListenAndServe(*addr, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("ListenAndServe: ", err) | 		log.Fatal("ListenAndServe: ", err) | ||||||
|   | |||||||
							
								
								
									
										134
									
								
								vendor/github.com/gorilla/websocket/examples/chat/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/gorilla/websocket/examples/chat/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | |||||||
|  | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// Time allowed to write a message to the peer. | ||||||
|  | 	writeWait = 10 * time.Second | ||||||
|  |  | ||||||
|  | 	// Time allowed to read the next pong message from the peer. | ||||||
|  | 	pongWait = 60 * time.Second | ||||||
|  |  | ||||||
|  | 	// Send pings to peer with this period. Must be less than pongWait. | ||||||
|  | 	pingPeriod = (pongWait * 9) / 10 | ||||||
|  |  | ||||||
|  | 	// Maximum message size allowed from peer. | ||||||
|  | 	maxMessageSize = 512 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	newline = []byte{'\n'} | ||||||
|  | 	space   = []byte{' '} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var upgrader = websocket.Upgrader{ | ||||||
|  | 	ReadBufferSize:  1024, | ||||||
|  | 	WriteBufferSize: 1024, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Client is a middleman between the websocket connection and the hub. | ||||||
|  | type Client struct { | ||||||
|  | 	hub *Hub | ||||||
|  |  | ||||||
|  | 	// The websocket connection. | ||||||
|  | 	conn *websocket.Conn | ||||||
|  |  | ||||||
|  | 	// Buffered channel of outbound messages. | ||||||
|  | 	send chan []byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // readPump pumps messages from the websocket connection to the hub. | ||||||
|  | // | ||||||
|  | // The application runs readPump in a per-connection goroutine. The application | ||||||
|  | // ensures that there is at most one reader on a connection by executing all | ||||||
|  | // reads from this goroutine. | ||||||
|  | func (c *Client) readPump() { | ||||||
|  | 	defer func() { | ||||||
|  | 		c.hub.unregister <- c | ||||||
|  | 		c.conn.Close() | ||||||
|  | 	}() | ||||||
|  | 	c.conn.SetReadLimit(maxMessageSize) | ||||||
|  | 	c.conn.SetReadDeadline(time.Now().Add(pongWait)) | ||||||
|  | 	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) | ||||||
|  | 	for { | ||||||
|  | 		_, message, err := c.conn.ReadMessage() | ||||||
|  | 		if err != nil { | ||||||
|  | 			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { | ||||||
|  | 				log.Printf("error: %v", err) | ||||||
|  | 			} | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1)) | ||||||
|  | 		c.hub.broadcast <- message | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // writePump pumps messages from the hub to the websocket connection. | ||||||
|  | // | ||||||
|  | // A goroutine running writePump is started for each connection. The | ||||||
|  | // application ensures that there is at most one writer to a connection by | ||||||
|  | // executing all writes from this goroutine. | ||||||
|  | func (c *Client) writePump() { | ||||||
|  | 	ticker := time.NewTicker(pingPeriod) | ||||||
|  | 	defer func() { | ||||||
|  | 		ticker.Stop() | ||||||
|  | 		c.conn.Close() | ||||||
|  | 	}() | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case message, ok := <-c.send: | ||||||
|  | 			c.conn.SetWriteDeadline(time.Now().Add(writeWait)) | ||||||
|  | 			if !ok { | ||||||
|  | 				// The hub closed the channel. | ||||||
|  | 				c.conn.WriteMessage(websocket.CloseMessage, []byte{}) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			w, err := c.conn.NextWriter(websocket.TextMessage) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			w.Write(message) | ||||||
|  |  | ||||||
|  | 			// Add queued chat messages to the current websocket message. | ||||||
|  | 			n := len(c.send) | ||||||
|  | 			for i := 0; i < n; i++ { | ||||||
|  | 				w.Write(newline) | ||||||
|  | 				w.Write(<-c.send) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := w.Close(); err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			c.conn.SetWriteDeadline(time.Now().Add(writeWait)) | ||||||
|  | 			if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // serveWs handles websocket requests from the peer. | ||||||
|  | func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	conn, err := upgrader.Upgrade(w, r, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Println(err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)} | ||||||
|  | 	client.hub.register <- client | ||||||
|  | 	go client.writePump() | ||||||
|  | 	client.readPump() | ||||||
|  | } | ||||||
							
								
								
									
										105
									
								
								vendor/github.com/gorilla/websocket/examples/chat/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/gorilla/websocket/examples/chat/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,105 +0,0 @@ | |||||||
| // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/gorilla/websocket" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	// Time allowed to write a message to the peer. |  | ||||||
| 	writeWait = 10 * time.Second |  | ||||||
|  |  | ||||||
| 	// Time allowed to read the next pong message from the peer. |  | ||||||
| 	pongWait = 60 * time.Second |  | ||||||
|  |  | ||||||
| 	// Send pings to peer with this period. Must be less than pongWait. |  | ||||||
| 	pingPeriod = (pongWait * 9) / 10 |  | ||||||
|  |  | ||||||
| 	// Maximum message size allowed from peer. |  | ||||||
| 	maxMessageSize = 512 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var upgrader = websocket.Upgrader{ |  | ||||||
| 	ReadBufferSize:  1024, |  | ||||||
| 	WriteBufferSize: 1024, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // connection is an middleman between the websocket connection and the hub. |  | ||||||
| type connection struct { |  | ||||||
| 	// The websocket connection. |  | ||||||
| 	ws *websocket.Conn |  | ||||||
|  |  | ||||||
| 	// Buffered channel of outbound messages. |  | ||||||
| 	send chan []byte |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // readPump pumps messages from the websocket connection to the hub. |  | ||||||
| func (c *connection) readPump() { |  | ||||||
| 	defer func() { |  | ||||||
| 		h.unregister <- c |  | ||||||
| 		c.ws.Close() |  | ||||||
| 	}() |  | ||||||
| 	c.ws.SetReadLimit(maxMessageSize) |  | ||||||
| 	c.ws.SetReadDeadline(time.Now().Add(pongWait)) |  | ||||||
| 	c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) |  | ||||||
| 	for { |  | ||||||
| 		_, message, err := c.ws.ReadMessage() |  | ||||||
| 		if err != nil { |  | ||||||
| 			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { |  | ||||||
| 				log.Printf("error: %v", err) |  | ||||||
| 			} |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		h.broadcast <- message |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // write writes a message with the given message type and payload. |  | ||||||
| func (c *connection) write(mt int, payload []byte) error { |  | ||||||
| 	c.ws.SetWriteDeadline(time.Now().Add(writeWait)) |  | ||||||
| 	return c.ws.WriteMessage(mt, payload) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // writePump pumps messages from the hub to the websocket connection. |  | ||||||
| func (c *connection) writePump() { |  | ||||||
| 	ticker := time.NewTicker(pingPeriod) |  | ||||||
| 	defer func() { |  | ||||||
| 		ticker.Stop() |  | ||||||
| 		c.ws.Close() |  | ||||||
| 	}() |  | ||||||
| 	for { |  | ||||||
| 		select { |  | ||||||
| 		case message, ok := <-c.send: |  | ||||||
| 			if !ok { |  | ||||||
| 				c.write(websocket.CloseMessage, []byte{}) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			if err := c.write(websocket.TextMessage, message); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		case <-ticker.C: |  | ||||||
| 			if err := c.write(websocket.PingMessage, []byte{}); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // serveWs handles websocket requests from the peer. |  | ||||||
| func serveWs(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 	ws, err := upgrader.Upgrade(w, r, nil) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Println(err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	c := &connection{send: make(chan []byte, 256), ws: ws} |  | ||||||
| 	h.register <- c |  | ||||||
| 	go c.writePump() |  | ||||||
| 	c.readPump() |  | ||||||
| } |  | ||||||
							
								
								
									
										56
									
								
								vendor/github.com/gorilla/websocket/examples/chat/hub.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/gorilla/websocket/examples/chat/hub.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,46 +4,48 @@ | |||||||
|  |  | ||||||
| package main | package main | ||||||
|  |  | ||||||
| // hub maintains the set of active connections and broadcasts messages to the | // hub maintains the set of active clients and broadcasts messages to the | ||||||
| // connections. | // clients. | ||||||
| type hub struct { | type Hub struct { | ||||||
| 	// Registered connections. | 	// Registered clients. | ||||||
| 	connections map[*connection]bool | 	clients map[*Client]bool | ||||||
|  |  | ||||||
| 	// Inbound messages from the connections. | 	// Inbound messages from the clients. | ||||||
| 	broadcast chan []byte | 	broadcast chan []byte | ||||||
|  |  | ||||||
| 	// Register requests from the connections. | 	// Register requests from the clients. | ||||||
| 	register chan *connection | 	register chan *Client | ||||||
|  |  | ||||||
| 	// Unregister requests from connections. | 	// Unregister requests from clients. | ||||||
| 	unregister chan *connection | 	unregister chan *Client | ||||||
| } | } | ||||||
|  |  | ||||||
| var h = hub{ | func newHub() *Hub { | ||||||
| 	broadcast:   make(chan []byte), | 	return &Hub{ | ||||||
| 	register:    make(chan *connection), | 		broadcast:  make(chan []byte), | ||||||
| 	unregister:  make(chan *connection), | 		register:   make(chan *Client), | ||||||
| 	connections: make(map[*connection]bool), | 		unregister: make(chan *Client), | ||||||
|  | 		clients:    make(map[*Client]bool), | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *hub) run() { | func (h *Hub) run() { | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| 		case c := <-h.register: | 		case client := <-h.register: | ||||||
| 			h.connections[c] = true | 			h.clients[client] = true | ||||||
| 		case c := <-h.unregister: | 		case client := <-h.unregister: | ||||||
| 			if _, ok := h.connections[c]; ok { | 			if _, ok := h.clients[client]; ok { | ||||||
| 				delete(h.connections, c) | 				delete(h.clients, client) | ||||||
| 				close(c.send) | 				close(client.send) | ||||||
| 			} | 			} | ||||||
| 		case m := <-h.broadcast: | 		case message := <-h.broadcast: | ||||||
| 			for c := range h.connections { | 			for client := range h.clients { | ||||||
| 				select { | 				select { | ||||||
| 				case c.send <- m: | 				case client.send <- message: | ||||||
| 				default: | 				default: | ||||||
| 					close(c.send) | 					close(client.send) | ||||||
| 					delete(h.connections, c) | 					delete(h.clients, client) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								vendor/github.com/gorilla/websocket/examples/chat/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/gorilla/websocket/examples/chat/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,13 +8,12 @@ import ( | |||||||
| 	"flag" | 	"flag" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"text/template" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var addr = flag.String("addr", ":8080", "http service address") | var addr = flag.String("addr", ":8080", "http service address") | ||||||
| var homeTempl = template.Must(template.ParseFiles("home.html")) |  | ||||||
|  |  | ||||||
| func serveHome(w http.ResponseWriter, r *http.Request) { | func serveHome(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	log.Println(r.URL) | ||||||
| 	if r.URL.Path != "/" { | 	if r.URL.Path != "/" { | ||||||
| 		http.Error(w, "Not found", 404) | 		http.Error(w, "Not found", 404) | ||||||
| 		return | 		return | ||||||
| @@ -23,15 +22,17 @@ func serveHome(w http.ResponseWriter, r *http.Request) { | |||||||
| 		http.Error(w, "Method not allowed", 405) | 		http.Error(w, "Method not allowed", 405) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | 	http.ServeFile(w, r, "home.html") | ||||||
| 	homeTempl.Execute(w, r.Host) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
| 	go h.run() | 	hub := newHub() | ||||||
|  | 	go hub.run() | ||||||
| 	http.HandleFunc("/", serveHome) | 	http.HandleFunc("/", serveHome) | ||||||
| 	http.HandleFunc("/ws", serveWs) | 	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		serveWs(hub, w, r) | ||||||
|  | 	}) | ||||||
| 	err := http.ListenAndServe(*addr, nil) | 	err := http.ListenAndServe(*addr, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("ListenAndServe: ", err) | 		log.Fatal("ListenAndServe: ", err) | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,16 +12,14 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"text/template" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gorilla/websocket" | 	"github.com/gorilla/websocket" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	addr      = flag.String("addr", "127.0.0.1:8080", "http service address") | 	addr    = flag.String("addr", "127.0.0.1:8080", "http service address") | ||||||
| 	cmdPath   string | 	cmdPath string | ||||||
| 	homeTempl = template.Must(template.ParseFiles("home.html")) |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -36,6 +34,9 @@ const ( | |||||||
|  |  | ||||||
| 	// Send pings to peer with this period. Must be less than pongWait. | 	// Send pings to peer with this period. Must be less than pongWait. | ||||||
| 	pingPeriod = (pongWait * 9) / 10 | 	pingPeriod = (pongWait * 9) / 10 | ||||||
|  |  | ||||||
|  | 	// Time to wait before force close on connection. | ||||||
|  | 	closeGracePeriod = 10 * time.Second | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func pumpStdin(ws *websocket.Conn, w io.Writer) { | func pumpStdin(ws *websocket.Conn, w io.Writer) { | ||||||
| @@ -57,19 +58,24 @@ func pumpStdin(ws *websocket.Conn, w io.Writer) { | |||||||
|  |  | ||||||
| func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { | func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		ws.Close() |  | ||||||
| 		close(done) |  | ||||||
| 	}() | 	}() | ||||||
| 	s := bufio.NewScanner(r) | 	s := bufio.NewScanner(r) | ||||||
| 	for s.Scan() { | 	for s.Scan() { | ||||||
| 		ws.SetWriteDeadline(time.Now().Add(writeWait)) | 		ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||||
| 		if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { | 		if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { | ||||||
|  | 			ws.Close() | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if s.Err() != nil { | 	if s.Err() != nil { | ||||||
| 		log.Println("scan:", s.Err()) | 		log.Println("scan:", s.Err()) | ||||||
| 	} | 	} | ||||||
|  | 	close(done) | ||||||
|  |  | ||||||
|  | 	ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||||
|  | 	ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) | ||||||
|  | 	time.Sleep(closeGracePeriod) | ||||||
|  | 	ws.Close() | ||||||
| } | } | ||||||
|  |  | ||||||
| func ping(ws *websocket.Conn, done chan struct{}) { | func ping(ws *websocket.Conn, done chan struct{}) { | ||||||
| @@ -168,8 +174,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) { | |||||||
| 		http.Error(w, "Method not allowed", 405) | 		http.Error(w, "Method not allowed", 405) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | 	http.ServeFile(w, r, "home.html") | ||||||
| 	homeTempl.Execute(w, r.Host) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								vendor/github.com/gorilla/websocket/examples/filewatch/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/gorilla/websocket/examples/filewatch/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,12 +6,12 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"flag" | 	"flag" | ||||||
|  | 	"html/template" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"text/template" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gorilla/websocket" | 	"github.com/gorilla/websocket" | ||||||
| @@ -120,7 +120,7 @@ func serveWs(w http.ResponseWriter, r *http.Request) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var lastMod time.Time | 	var lastMod time.Time | ||||||
| 	if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil { | 	if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil { | ||||||
| 		lastMod = time.Unix(0, n) | 		lastMod = time.Unix(0, n) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								vendor/github.com/gorilla/websocket/mask.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/gorilla/websocket/mask.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of | ||||||
|  | // this source code is governed by a BSD-style license that can be found in the | ||||||
|  | // LICENSE file. | ||||||
|  |  | ||||||
|  | // +build !appengine | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import "unsafe" | ||||||
|  |  | ||||||
|  | const wordSize = int(unsafe.Sizeof(uintptr(0))) | ||||||
|  |  | ||||||
|  | func maskBytes(key [4]byte, pos int, b []byte) int { | ||||||
|  |  | ||||||
|  | 	// Mask one byte at a time for small buffers. | ||||||
|  | 	if len(b) < 2*wordSize { | ||||||
|  | 		for i := range b { | ||||||
|  | 			b[i] ^= key[pos&3] | ||||||
|  | 			pos++ | ||||||
|  | 		} | ||||||
|  | 		return pos & 3 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Mask one byte at a time to word boundary. | ||||||
|  | 	if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { | ||||||
|  | 		n = wordSize - n | ||||||
|  | 		for i := range b[:n] { | ||||||
|  | 			b[i] ^= key[pos&3] | ||||||
|  | 			pos++ | ||||||
|  | 		} | ||||||
|  | 		b = b[n:] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create aligned word size key. | ||||||
|  | 	var k [wordSize]byte | ||||||
|  | 	for i := range k { | ||||||
|  | 		k[i] = key[(pos+i)&3] | ||||||
|  | 	} | ||||||
|  | 	kw := *(*uintptr)(unsafe.Pointer(&k)) | ||||||
|  |  | ||||||
|  | 	// Mask one word at a time. | ||||||
|  | 	n := (len(b) / wordSize) * wordSize | ||||||
|  | 	for i := 0; i < n; i += wordSize { | ||||||
|  | 		*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Mask one byte at a time for remaining bytes. | ||||||
|  | 	b = b[n:] | ||||||
|  | 	for i := range b { | ||||||
|  | 		b[i] ^= key[pos&3] | ||||||
|  | 		pos++ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pos & 3 | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								vendor/github.com/gorilla/websocket/mask_safe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/gorilla/websocket/mask_safe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of | ||||||
|  | // this source code is governed by a BSD-style license that can be found in the | ||||||
|  | // LICENSE file. | ||||||
|  |  | ||||||
|  | // +build appengine | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | func maskBytes(key [4]byte, pos int, b []byte) int { | ||||||
|  | 	for i := range b { | ||||||
|  | 		b[i] ^= key[pos&3] | ||||||
|  | 		pos++ | ||||||
|  | 	} | ||||||
|  | 	return pos & 3 | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package websocket | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"net" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // PreparedMessage caches on the wire representations of a message payload. | ||||||
|  | // Use PreparedMessage to efficiently send a message payload to multiple | ||||||
|  | // connections. PreparedMessage is especially useful when compression is used | ||||||
|  | // because the CPU and memory expensive compression operation can be executed | ||||||
|  | // once for a given set of compression options. | ||||||
|  | type PreparedMessage struct { | ||||||
|  | 	messageType int | ||||||
|  | 	data        []byte | ||||||
|  | 	err         error | ||||||
|  | 	mu          sync.Mutex | ||||||
|  | 	frames      map[prepareKey]*preparedFrame | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. | ||||||
|  | type prepareKey struct { | ||||||
|  | 	isServer         bool | ||||||
|  | 	compress         bool | ||||||
|  | 	compressionLevel int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // preparedFrame contains data in wire representation. | ||||||
|  | type preparedFrame struct { | ||||||
|  | 	once sync.Once | ||||||
|  | 	data []byte | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewPreparedMessage returns an initialized PreparedMessage. You can then send | ||||||
|  | // it to connection using WritePreparedMessage method. Valid wire | ||||||
|  | // representation will be calculated lazily only once for a set of current | ||||||
|  | // connection options. | ||||||
|  | func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { | ||||||
|  | 	pm := &PreparedMessage{ | ||||||
|  | 		messageType: messageType, | ||||||
|  | 		frames:      make(map[prepareKey]*preparedFrame), | ||||||
|  | 		data:        data, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Prepare a plain server frame. | ||||||
|  | 	_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// To protect against caller modifying the data argument, remember the data | ||||||
|  | 	// copied to the plain server frame. | ||||||
|  | 	pm.data = frameData[len(frameData)-len(data):] | ||||||
|  | 	return pm, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { | ||||||
|  | 	pm.mu.Lock() | ||||||
|  | 	frame, ok := pm.frames[key] | ||||||
|  | 	if !ok { | ||||||
|  | 		frame = &preparedFrame{} | ||||||
|  | 		pm.frames[key] = frame | ||||||
|  | 	} | ||||||
|  | 	pm.mu.Unlock() | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	frame.once.Do(func() { | ||||||
|  | 		// Prepare a frame using a 'fake' connection. | ||||||
|  | 		// TODO: Refactor code in conn.go to allow more direct construction of | ||||||
|  | 		// the frame. | ||||||
|  | 		mu := make(chan bool, 1) | ||||||
|  | 		mu <- true | ||||||
|  | 		var nc prepareConn | ||||||
|  | 		c := &Conn{ | ||||||
|  | 			conn:                   &nc, | ||||||
|  | 			mu:                     mu, | ||||||
|  | 			isServer:               key.isServer, | ||||||
|  | 			compressionLevel:       key.compressionLevel, | ||||||
|  | 			enableWriteCompression: true, | ||||||
|  | 			writeBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), | ||||||
|  | 		} | ||||||
|  | 		if key.compress { | ||||||
|  | 			c.newCompressionWriter = compressNoContextTakeover | ||||||
|  | 		} | ||||||
|  | 		err = c.WriteMessage(pm.messageType, pm.data) | ||||||
|  | 		frame.data = nc.buf.Bytes() | ||||||
|  | 	}) | ||||||
|  | 	return pm.messageType, frame.data, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type prepareConn struct { | ||||||
|  | 	buf bytes.Buffer | ||||||
|  | 	net.Conn | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) } | ||||||
|  | func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } | ||||||
							
								
								
									
										61
									
								
								vendor/github.com/gorilla/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/gorilla/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -28,8 +28,9 @@ type Upgrader struct { | |||||||
| 	HandshakeTimeout time.Duration | 	HandshakeTimeout time.Duration | ||||||
|  |  | ||||||
| 	// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer | 	// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer | ||||||
| 	// size is zero, then a default value of 4096 is used. The I/O buffer sizes | 	// size is zero, then buffers allocated by the HTTP server are used. The | ||||||
| 	// do not limit the size of the messages that can be sent or received. | 	// I/O buffer sizes do not limit the size of the messages that can be sent | ||||||
|  | 	// or received. | ||||||
| 	ReadBufferSize, WriteBufferSize int | 	ReadBufferSize, WriteBufferSize int | ||||||
|  |  | ||||||
| 	// Subprotocols specifies the server's supported protocols in order of | 	// Subprotocols specifies the server's supported protocols in order of | ||||||
| @@ -46,6 +47,12 @@ type Upgrader struct { | |||||||
| 	// CheckOrigin is nil, the host in the Origin header must not be set or | 	// CheckOrigin is nil, the host in the Origin header must not be set or | ||||||
| 	// must match the host of the request. | 	// must match the host of the request. | ||||||
| 	CheckOrigin func(r *http.Request) bool | 	CheckOrigin func(r *http.Request) bool | ||||||
|  |  | ||||||
|  | 	// EnableCompression specify if the server should attempt to negotiate per | ||||||
|  | 	// message compression (RFC 7692). Setting this value to true does not | ||||||
|  | 	// guarantee that compression will be supported. Currently only "no context | ||||||
|  | 	// takeover" modes are supported. | ||||||
|  | 	EnableCompression bool | ||||||
| } | } | ||||||
|  |  | ||||||
| func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { | func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { | ||||||
| @@ -53,6 +60,7 @@ func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status in | |||||||
| 	if u.Error != nil { | 	if u.Error != nil { | ||||||
| 		u.Error(w, r, status, err) | 		u.Error(w, r, status, err) | ||||||
| 	} else { | 	} else { | ||||||
|  | 		w.Header().Set("Sec-Websocket-Version", "13") | ||||||
| 		http.Error(w, http.StatusText(status), status) | 		http.Error(w, http.StatusText(status), status) | ||||||
| 	} | 	} | ||||||
| 	return nil, err | 	return nil, err | ||||||
| @@ -97,18 +105,23 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header | |||||||
| // response. | // response. | ||||||
| func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { | ||||||
| 	if r.Method != "GET" { | 	if r.Method != "GET" { | ||||||
| 		return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET") | 		return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET") | ||||||
| 	} | 	} | ||||||
| 	if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" { |  | ||||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13") | 	if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { | ||||||
|  | 		return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !tokenListContainsValue(r.Header, "Connection", "upgrade") { | 	if !tokenListContainsValue(r.Header, "Connection", "upgrade") { | ||||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'") | 		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { | 	if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { | ||||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'") | 		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { | ||||||
|  | 		return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	checkOrigin := u.CheckOrigin | 	checkOrigin := u.CheckOrigin | ||||||
| @@ -116,19 +129,30 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade | |||||||
| 		checkOrigin = checkSameOrigin | 		checkOrigin = checkSameOrigin | ||||||
| 	} | 	} | ||||||
| 	if !checkOrigin(r) { | 	if !checkOrigin(r) { | ||||||
| 		return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed") | 		return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	challengeKey := r.Header.Get("Sec-Websocket-Key") | 	challengeKey := r.Header.Get("Sec-Websocket-Key") | ||||||
| 	if challengeKey == "" { | 	if challengeKey == "" { | ||||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank") | 		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	subprotocol := u.selectSubprotocol(r, responseHeader) | 	subprotocol := u.selectSubprotocol(r, responseHeader) | ||||||
|  |  | ||||||
|  | 	// Negotiate PMCE | ||||||
|  | 	var compress bool | ||||||
|  | 	if u.EnableCompression { | ||||||
|  | 		for _, ext := range parseExtensions(r.Header) { | ||||||
|  | 			if ext[""] != "permessage-deflate" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			compress = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		netConn net.Conn | 		netConn net.Conn | ||||||
| 		br      *bufio.Reader |  | ||||||
| 		err     error | 		err     error | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| @@ -136,21 +160,25 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade | |||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") | 		return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") | ||||||
| 	} | 	} | ||||||
| 	var rw *bufio.ReadWriter | 	var brw *bufio.ReadWriter | ||||||
| 	netConn, rw, err = h.Hijack() | 	netConn, brw, err = h.Hijack() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return u.returnError(w, r, http.StatusInternalServerError, err.Error()) | 		return u.returnError(w, r, http.StatusInternalServerError, err.Error()) | ||||||
| 	} | 	} | ||||||
| 	br = rw.Reader |  | ||||||
|  |  | ||||||
| 	if br.Buffered() > 0 { | 	if brw.Reader.Buffered() > 0 { | ||||||
| 		netConn.Close() | 		netConn.Close() | ||||||
| 		return nil, errors.New("websocket: client sent data before handshake is complete") | 		return nil, errors.New("websocket: client sent data before handshake is complete") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) | 	c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) | ||||||
| 	c.subprotocol = subprotocol | 	c.subprotocol = subprotocol | ||||||
|  |  | ||||||
|  | 	if compress { | ||||||
|  | 		c.newCompressionWriter = compressNoContextTakeover | ||||||
|  | 		c.newDecompressionReader = decompressNoContextTakeover | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	p := c.writeBuf[:0] | 	p := c.writeBuf[:0] | ||||||
| 	p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) | 	p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) | ||||||
| 	p = append(p, computeAcceptKey(challengeKey)...) | 	p = append(p, computeAcceptKey(challengeKey)...) | ||||||
| @@ -160,6 +188,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade | |||||||
| 		p = append(p, c.subprotocol...) | 		p = append(p, c.subprotocol...) | ||||||
| 		p = append(p, "\r\n"...) | 		p = append(p, "\r\n"...) | ||||||
| 	} | 	} | ||||||
|  | 	if compress { | ||||||
|  | 		p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) | ||||||
|  | 	} | ||||||
| 	for k, vs := range responseHeader { | 	for k, vs := range responseHeader { | ||||||
| 		if k == "Sec-Websocket-Protocol" { | 		if k == "Sec-Websocket-Protocol" { | ||||||
| 			continue | 			continue | ||||||
|   | |||||||
							
								
								
									
										196
									
								
								vendor/github.com/gorilla/websocket/util.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										196
									
								
								vendor/github.com/gorilla/websocket/util.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,19 +13,6 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // tokenListContainsValue returns true if the 1#token header with the given |  | ||||||
| // name contains token. |  | ||||||
| func tokenListContainsValue(header http.Header, name string, value string) bool { |  | ||||||
| 	for _, v := range header[name] { |  | ||||||
| 		for _, s := range strings.Split(v, ",") { |  | ||||||
| 			if strings.EqualFold(value, strings.TrimSpace(s)) { |  | ||||||
| 				return true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") | ||||||
|  |  | ||||||
| func computeAcceptKey(challengeKey string) string { | func computeAcceptKey(challengeKey string) string { | ||||||
| @@ -42,3 +29,186 @@ func generateChallengeKey() (string, error) { | |||||||
| 	} | 	} | ||||||
| 	return base64.StdEncoding.EncodeToString(p), nil | 	return base64.StdEncoding.EncodeToString(p), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Octet types from RFC 2616. | ||||||
|  | var octetTypes [256]byte | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	isTokenOctet = 1 << iota | ||||||
|  | 	isSpaceOctet | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	// From RFC 2616 | ||||||
|  | 	// | ||||||
|  | 	// OCTET      = <any 8-bit sequence of data> | ||||||
|  | 	// CHAR       = <any US-ASCII character (octets 0 - 127)> | ||||||
|  | 	// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)> | ||||||
|  | 	// CR         = <US-ASCII CR, carriage return (13)> | ||||||
|  | 	// LF         = <US-ASCII LF, linefeed (10)> | ||||||
|  | 	// SP         = <US-ASCII SP, space (32)> | ||||||
|  | 	// HT         = <US-ASCII HT, horizontal-tab (9)> | ||||||
|  | 	// <">        = <US-ASCII double-quote mark (34)> | ||||||
|  | 	// CRLF       = CR LF | ||||||
|  | 	// LWS        = [CRLF] 1*( SP | HT ) | ||||||
|  | 	// TEXT       = <any OCTET except CTLs, but including LWS> | ||||||
|  | 	// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | ||||||
|  | 	//              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT | ||||||
|  | 	// token      = 1*<any CHAR except CTLs or separators> | ||||||
|  | 	// qdtext     = <any TEXT except <">> | ||||||
|  |  | ||||||
|  | 	for c := 0; c < 256; c++ { | ||||||
|  | 		var t byte | ||||||
|  | 		isCtl := c <= 31 || c == 127 | ||||||
|  | 		isChar := 0 <= c && c <= 127 | ||||||
|  | 		isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 | ||||||
|  | 		if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { | ||||||
|  | 			t |= isSpaceOctet | ||||||
|  | 		} | ||||||
|  | 		if isChar && !isCtl && !isSeparator { | ||||||
|  | 			t |= isTokenOctet | ||||||
|  | 		} | ||||||
|  | 		octetTypes[c] = t | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func skipSpace(s string) (rest string) { | ||||||
|  | 	i := 0 | ||||||
|  | 	for ; i < len(s); i++ { | ||||||
|  | 		if octetTypes[s[i]]&isSpaceOctet == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return s[i:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func nextToken(s string) (token, rest string) { | ||||||
|  | 	i := 0 | ||||||
|  | 	for ; i < len(s); i++ { | ||||||
|  | 		if octetTypes[s[i]]&isTokenOctet == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return s[:i], s[i:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func nextTokenOrQuoted(s string) (value string, rest string) { | ||||||
|  | 	if !strings.HasPrefix(s, "\"") { | ||||||
|  | 		return nextToken(s) | ||||||
|  | 	} | ||||||
|  | 	s = s[1:] | ||||||
|  | 	for i := 0; i < len(s); i++ { | ||||||
|  | 		switch s[i] { | ||||||
|  | 		case '"': | ||||||
|  | 			return s[:i], s[i+1:] | ||||||
|  | 		case '\\': | ||||||
|  | 			p := make([]byte, len(s)-1) | ||||||
|  | 			j := copy(p, s[:i]) | ||||||
|  | 			escape := true | ||||||
|  | 			for i = i + 1; i < len(s); i++ { | ||||||
|  | 				b := s[i] | ||||||
|  | 				switch { | ||||||
|  | 				case escape: | ||||||
|  | 					escape = false | ||||||
|  | 					p[j] = b | ||||||
|  | 					j += 1 | ||||||
|  | 				case b == '\\': | ||||||
|  | 					escape = true | ||||||
|  | 				case b == '"': | ||||||
|  | 					return string(p[:j]), s[i+1:] | ||||||
|  | 				default: | ||||||
|  | 					p[j] = b | ||||||
|  | 					j += 1 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return "", "" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "", "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // tokenListContainsValue returns true if the 1#token header with the given | ||||||
|  | // name contains token. | ||||||
|  | func tokenListContainsValue(header http.Header, name string, value string) bool { | ||||||
|  | headers: | ||||||
|  | 	for _, s := range header[name] { | ||||||
|  | 		for { | ||||||
|  | 			var t string | ||||||
|  | 			t, s = nextToken(skipSpace(s)) | ||||||
|  | 			if t == "" { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			s = skipSpace(s) | ||||||
|  | 			if s != "" && s[0] != ',' { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			if strings.EqualFold(t, value) { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 			if s == "" { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			s = s[1:] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parseExtensiosn parses WebSocket extensions from a header. | ||||||
|  | func parseExtensions(header http.Header) []map[string]string { | ||||||
|  |  | ||||||
|  | 	// From RFC 6455: | ||||||
|  | 	// | ||||||
|  | 	//  Sec-WebSocket-Extensions = extension-list | ||||||
|  | 	//  extension-list = 1#extension | ||||||
|  | 	//  extension = extension-token *( ";" extension-param ) | ||||||
|  | 	//  extension-token = registered-token | ||||||
|  | 	//  registered-token = token | ||||||
|  | 	//  extension-param = token [ "=" (token | quoted-string) ] | ||||||
|  | 	//     ;When using the quoted-string syntax variant, the value | ||||||
|  | 	//     ;after quoted-string unescaping MUST conform to the | ||||||
|  | 	//     ;'token' ABNF. | ||||||
|  |  | ||||||
|  | 	var result []map[string]string | ||||||
|  | headers: | ||||||
|  | 	for _, s := range header["Sec-Websocket-Extensions"] { | ||||||
|  | 		for { | ||||||
|  | 			var t string | ||||||
|  | 			t, s = nextToken(skipSpace(s)) | ||||||
|  | 			if t == "" { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			ext := map[string]string{"": t} | ||||||
|  | 			for { | ||||||
|  | 				s = skipSpace(s) | ||||||
|  | 				if !strings.HasPrefix(s, ";") { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 				var k string | ||||||
|  | 				k, s = nextToken(skipSpace(s[1:])) | ||||||
|  | 				if k == "" { | ||||||
|  | 					continue headers | ||||||
|  | 				} | ||||||
|  | 				s = skipSpace(s) | ||||||
|  | 				var v string | ||||||
|  | 				if strings.HasPrefix(s, "=") { | ||||||
|  | 					v, s = nextTokenOrQuoted(skipSpace(s[1:])) | ||||||
|  | 					s = skipSpace(s) | ||||||
|  | 				} | ||||||
|  | 				if s != "" && s[0] != ',' && s[0] != ';' { | ||||||
|  | 					continue headers | ||||||
|  | 				} | ||||||
|  | 				ext[k] = v | ||||||
|  | 			} | ||||||
|  | 			if s != "" && s[0] != ',' { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			result = append(result, ext) | ||||||
|  | 			if s == "" { | ||||||
|  | 				continue headers | ||||||
|  | 			} | ||||||
|  | 			s = s[1:] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								vendor/github.com/jpillora/backoff/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/jpillora/backoff/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | The MIT License (MIT) | ||||||
|  |  | ||||||
|  | Copyright (c) 2017 Jaime Pillora | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										83
									
								
								vendor/github.com/jpillora/backoff/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/jpillora/backoff/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  | // Package backoff provides an exponential-backoff implementation. | ||||||
| package backoff | package backoff | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -6,64 +7,82 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| //Backoff is a time.Duration counter. It starts at Min. | // Backoff is a time.Duration counter, starting at Min. After every call to | ||||||
| //After every call to Duration() it is  multiplied by Factor. | // the Duration method the current timing is multiplied by Factor, but it | ||||||
| //It is capped at Max. It returns to Min on every call to Reset(). | // never exceeds Max. | ||||||
| //Used in conjunction with the time package. |  | ||||||
| // | // | ||||||
| // Backoff is not threadsafe, but the ForAttempt method can be | // Backoff is not generally concurrent-safe, but the ForAttempt method can | ||||||
| // used concurrently if non-zero values for Factor, Max, and Min | // be used concurrently. | ||||||
| // are set on the Backoff shared among threads. |  | ||||||
| type Backoff struct { | type Backoff struct { | ||||||
| 	//Factor is the multiplying factor for each increment step | 	//Factor is the multiplying factor for each increment step | ||||||
| 	attempts, Factor float64 | 	attempt, Factor float64 | ||||||
| 	//Jitter eases contention by randomizing backoff steps | 	//Jitter eases contention by randomizing backoff steps | ||||||
| 	Jitter bool | 	Jitter bool | ||||||
| 	//Min and Max are the minimum and maximum values of the counter | 	//Min and Max are the minimum and maximum values of the counter | ||||||
| 	Min, Max time.Duration | 	Min, Max time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| //Returns the current value of the counter and then | // Duration returns the duration for the current attempt before incrementing | ||||||
| //multiplies it Factor | // the attempt counter. See ForAttempt. | ||||||
| func (b *Backoff) Duration() time.Duration { | func (b *Backoff) Duration() time.Duration { | ||||||
| 	d := b.ForAttempt(b.attempts) | 	d := b.ForAttempt(b.attempt) | ||||||
| 	b.attempts++ | 	b.attempt++ | ||||||
| 	return d | 	return d | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const maxInt64 = float64(math.MaxInt64 - 512) | ||||||
|  |  | ||||||
| // ForAttempt returns the duration for a specific attempt. This is useful if | // ForAttempt returns the duration for a specific attempt. This is useful if | ||||||
| // you have a large number of independent Backoffs, but don't want use | // you have a large number of independent Backoffs, but don't want use | ||||||
| // unnecessary memory storing the Backoff parameters per Backoff. The first | // unnecessary memory storing the Backoff parameters per Backoff. The first | ||||||
| // attempt should be 0. | // attempt should be 0. | ||||||
| // | // | ||||||
| // ForAttempt is threadsafe iff non-zero values for Factor, Max, and Min | // ForAttempt is concurrent-safe. | ||||||
| // are set before any calls to ForAttempt are made. |  | ||||||
| func (b *Backoff) ForAttempt(attempt float64) time.Duration { | func (b *Backoff) ForAttempt(attempt float64) time.Duration { | ||||||
| 	//Zero-values are nonsensical, so we use | 	// Zero-values are nonsensical, so we use | ||||||
| 	//them to apply defaults | 	// them to apply defaults | ||||||
| 	if b.Min == 0 { | 	min := b.Min | ||||||
| 		b.Min = 100 * time.Millisecond | 	if min <= 0 { | ||||||
|  | 		min = 100 * time.Millisecond | ||||||
| 	} | 	} | ||||||
| 	if b.Max == 0 { | 	max := b.Max | ||||||
| 		b.Max = 10 * time.Second | 	if max <= 0 { | ||||||
|  | 		max = 10 * time.Second | ||||||
| 	} | 	} | ||||||
| 	if b.Factor == 0 { | 	if min >= max { | ||||||
| 		b.Factor = 2 | 		// short-circuit | ||||||
|  | 		return max | ||||||
|  | 	} | ||||||
|  | 	factor := b.Factor | ||||||
|  | 	if factor <= 0 { | ||||||
|  | 		factor = 2 | ||||||
| 	} | 	} | ||||||
| 	//calculate this duration | 	//calculate this duration | ||||||
| 	dur := float64(b.Min) * math.Pow(b.Factor, attempt) | 	minf := float64(min) | ||||||
| 	if b.Jitter == true { | 	durf := minf * math.Pow(factor, attempt) | ||||||
| 		dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min) | 	if b.Jitter { | ||||||
|  | 		durf = rand.Float64()*(durf-minf) + minf | ||||||
| 	} | 	} | ||||||
| 	//cap! | 	//ensure float64 wont overflow int64 | ||||||
| 	if dur > float64(b.Max) { | 	if durf > maxInt64 { | ||||||
| 		return b.Max | 		return max | ||||||
| 	} | 	} | ||||||
| 	//return as a time.Duration | 	dur := time.Duration(durf) | ||||||
| 	return time.Duration(dur) | 	//keep within bounds | ||||||
|  | 	if dur < min { | ||||||
|  | 		return min | ||||||
|  | 	} else if dur > max { | ||||||
|  | 		return max | ||||||
|  | 	} | ||||||
|  | 	return dur | ||||||
| } | } | ||||||
|  |  | ||||||
| //Resets the current value of the counter back to Min | // Reset restarts the current attempt counter at zero. | ||||||
| func (b *Backoff) Reset() { | func (b *Backoff) Reset() { | ||||||
| 	b.attempts = 0 | 	b.attempt = 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Attempt returns the current attempt counter value. | ||||||
|  | func (b *Backoff) Attempt() float64 { | ||||||
|  | 	return b.attempt | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,10 +12,15 @@ type ClusterInterface interface { | |||||||
| 	StopInterNodeCommunication() | 	StopInterNodeCommunication() | ||||||
| 	GetClusterInfos() []*model.ClusterInfo | 	GetClusterInfos() []*model.ClusterInfo | ||||||
| 	GetClusterStats() ([]*model.ClusterStats, *model.AppError) | 	GetClusterStats() ([]*model.ClusterStats, *model.AppError) | ||||||
| 	RemoveAllSessionsForUserId(userId string) | 	ClearSessionCacheForUser(userId string) | ||||||
| 	InvalidateCacheForUser(userId string) | 	InvalidateCacheForUser(userId string) | ||||||
| 	InvalidateCacheForChannel(channelId string) | 	InvalidateCacheForChannel(channelId string) | ||||||
|  | 	InvalidateCacheForChannelByName(teamId, name string) | ||||||
|  | 	InvalidateCacheForChannelMembers(channelId string) | ||||||
|  | 	InvalidateCacheForChannelMembersNotifyProps(channelId string) | ||||||
| 	InvalidateCacheForChannelPosts(channelId string) | 	InvalidateCacheForChannelPosts(channelId string) | ||||||
|  | 	InvalidateCacheForWebhook(webhookId string) | ||||||
|  | 	InvalidateCacheForReactions(postId string) | ||||||
| 	Publish(event *model.WebSocketEvent) | 	Publish(event *model.WebSocketEvent) | ||||||
| 	UpdateStatus(status *model.Status) | 	UpdateStatus(status *model.Status) | ||||||
| 	GetLogs() ([]string, *model.AppError) | 	GetLogs() ([]string, *model.AppError) | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								vendor/github.com/mattermost/platform/einterfaces/metrics.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/mattermost/platform/einterfaces/metrics.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,6 +8,7 @@ type MetricsInterface interface { | |||||||
| 	StopServer() | 	StopServer() | ||||||
|  |  | ||||||
| 	IncrementPostCreate() | 	IncrementPostCreate() | ||||||
|  | 	IncrementWebhookPost() | ||||||
| 	IncrementPostSentEmail() | 	IncrementPostSentEmail() | ||||||
| 	IncrementPostSentPush() | 	IncrementPostSentPush() | ||||||
| 	IncrementPostBroadcast() | 	IncrementPostBroadcast() | ||||||
| @@ -17,6 +18,9 @@ type MetricsInterface interface { | |||||||
| 	IncrementHttpError() | 	IncrementHttpError() | ||||||
| 	ObserveHttpRequestDuration(elapsed float64) | 	ObserveHttpRequestDuration(elapsed float64) | ||||||
|  |  | ||||||
|  | 	IncrementClusterRequest() | ||||||
|  | 	ObserveClusterRequestDuration(elapsed float64) | ||||||
|  |  | ||||||
| 	IncrementLogin() | 	IncrementLogin() | ||||||
| 	IncrementLoginFail() | 	IncrementLoginFail() | ||||||
|  |  | ||||||
| @@ -25,6 +29,10 @@ type MetricsInterface interface { | |||||||
|  |  | ||||||
| 	IncrementMemCacheHitCounter(cacheName string) | 	IncrementMemCacheHitCounter(cacheName string) | ||||||
| 	IncrementMemCacheMissCounter(cacheName string) | 	IncrementMemCacheMissCounter(cacheName string) | ||||||
|  | 	IncrementMemCacheMissCounterSession() | ||||||
|  | 	IncrementMemCacheHitCounterSession() | ||||||
|  |  | ||||||
|  | 	IncrementWebsocketEvent(eventType string) | ||||||
|  |  | ||||||
| 	AddMemCacheHitCounter(cacheName string, amount float64) | 	AddMemCacheHitCounter(cacheName string, amount float64) | ||||||
| 	AddMemCacheMissCounter(cacheName string, amount float64) | 	AddMemCacheMissCounter(cacheName string, amount float64) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,7 +10,7 @@ import ( | |||||||
| type SamlInterface interface { | type SamlInterface interface { | ||||||
| 	ConfigureSP() *model.AppError | 	ConfigureSP() *model.AppError | ||||||
| 	BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError) | 	BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError) | ||||||
| 	DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError) | 	DoLogin(encodedXML string, relayState map[string]string, siteURL string) (*model.User, *model.AppError) | ||||||
| 	GetMetadata() (string, *model.AppError) | 	GetMetadata() (string, *model.AppError) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								vendor/github.com/mattermost/platform/model/authorization.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/mattermost/platform/model/authorization.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -27,7 +27,10 @@ var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission | |||||||
| var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission | var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission | ||||||
| var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission | var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission | ||||||
| var PERMISSION_MANAGE_ROLES *Permission | var PERMISSION_MANAGE_ROLES *Permission | ||||||
|  | var PERMISSION_MANAGE_TEAM_ROLES *Permission | ||||||
|  | var PERMISSION_MANAGE_CHANNEL_ROLES *Permission | ||||||
| var PERMISSION_CREATE_DIRECT_CHANNEL *Permission | var PERMISSION_CREATE_DIRECT_CHANNEL *Permission | ||||||
|  | var PERMISSION_CREATE_GROUP_CHANNEL *Permission | ||||||
| var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission | var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission | ||||||
| var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission | var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission | ||||||
| var PERMISSION_LIST_TEAM_CHANNELS *Permission | var PERMISSION_LIST_TEAM_CHANNELS *Permission | ||||||
| @@ -46,9 +49,13 @@ var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission | |||||||
| var PERMISSION_CREATE_POST *Permission | var PERMISSION_CREATE_POST *Permission | ||||||
| var PERMISSION_EDIT_POST *Permission | var PERMISSION_EDIT_POST *Permission | ||||||
| var PERMISSION_EDIT_OTHERS_POSTS *Permission | var PERMISSION_EDIT_OTHERS_POSTS *Permission | ||||||
|  | var PERMISSION_DELETE_POST *Permission | ||||||
|  | var PERMISSION_DELETE_OTHERS_POSTS *Permission | ||||||
| var PERMISSION_REMOVE_USER_FROM_TEAM *Permission | var PERMISSION_REMOVE_USER_FROM_TEAM *Permission | ||||||
|  | var PERMISSION_CREATE_TEAM *Permission | ||||||
| var PERMISSION_MANAGE_TEAM *Permission | var PERMISSION_MANAGE_TEAM *Permission | ||||||
| var PERMISSION_IMPORT_TEAM *Permission | var PERMISSION_IMPORT_TEAM *Permission | ||||||
|  | var PERMISSION_VIEW_TEAM *Permission | ||||||
|  |  | ||||||
| // General permission that encompases all system admin functions | // General permission that encompases all system admin functions | ||||||
| // in the future this could be broken up to allow access to some | // in the future this could be broken up to allow access to some | ||||||
| @@ -123,6 +130,16 @@ func InitalizePermissions() { | |||||||
| 		"authentication.permissions.manage_roles.name", | 		"authentication.permissions.manage_roles.name", | ||||||
| 		"authentication.permissions.manage_roles.description", | 		"authentication.permissions.manage_roles.description", | ||||||
| 	} | 	} | ||||||
|  | 	PERMISSION_MANAGE_TEAM_ROLES = &Permission{ | ||||||
|  | 		"manage_team_roles", | ||||||
|  | 		"authentication.permissions.manage_team_roles.name", | ||||||
|  | 		"authentication.permissions.manage_team_roles.description", | ||||||
|  | 	} | ||||||
|  | 	PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ | ||||||
|  | 		"manage_channel_roles", | ||||||
|  | 		"authentication.permissions.manage_channel_roles.name", | ||||||
|  | 		"authentication.permissions.manage_channel_roles.description", | ||||||
|  | 	} | ||||||
| 	PERMISSION_MANAGE_SYSTEM = &Permission{ | 	PERMISSION_MANAGE_SYSTEM = &Permission{ | ||||||
| 		"manage_system", | 		"manage_system", | ||||||
| 		"authentication.permissions.manage_system.name", | 		"authentication.permissions.manage_system.name", | ||||||
| @@ -133,6 +150,11 @@ func InitalizePermissions() { | |||||||
| 		"authentication.permissions.create_direct_channel.name", | 		"authentication.permissions.create_direct_channel.name", | ||||||
| 		"authentication.permissions.create_direct_channel.description", | 		"authentication.permissions.create_direct_channel.description", | ||||||
| 	} | 	} | ||||||
|  | 	PERMISSION_CREATE_GROUP_CHANNEL = &Permission{ | ||||||
|  | 		"create_group_channel", | ||||||
|  | 		"authentication.permissions.create_group_channel.name", | ||||||
|  | 		"authentication.permissions.create_group_channel.description", | ||||||
|  | 	} | ||||||
| 	PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ | 	PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ | ||||||
| 		"manage__publicchannel_properties", | 		"manage__publicchannel_properties", | ||||||
| 		"authentication.permissions.manage_public_channel_properties.name", | 		"authentication.permissions.manage_public_channel_properties.name", | ||||||
| @@ -223,11 +245,26 @@ func InitalizePermissions() { | |||||||
| 		"authentication.permissions.edit_others_posts.name", | 		"authentication.permissions.edit_others_posts.name", | ||||||
| 		"authentication.permissions.edit_others_posts.description", | 		"authentication.permissions.edit_others_posts.description", | ||||||
| 	} | 	} | ||||||
|  | 	PERMISSION_DELETE_POST = &Permission{ | ||||||
|  | 		"delete_post", | ||||||
|  | 		"authentication.permissions.delete_post.name", | ||||||
|  | 		"authentication.permissions.delete_post.description", | ||||||
|  | 	} | ||||||
|  | 	PERMISSION_DELETE_OTHERS_POSTS = &Permission{ | ||||||
|  | 		"delete_others_posts", | ||||||
|  | 		"authentication.permissions.delete_others_posts.name", | ||||||
|  | 		"authentication.permissions.delete_others_posts.description", | ||||||
|  | 	} | ||||||
| 	PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ | 	PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ | ||||||
| 		"remove_user_from_team", | 		"remove_user_from_team", | ||||||
| 		"authentication.permissions.remove_user_from_team.name", | 		"authentication.permissions.remove_user_from_team.name", | ||||||
| 		"authentication.permissions.remove_user_from_team.description", | 		"authentication.permissions.remove_user_from_team.description", | ||||||
| 	} | 	} | ||||||
|  | 	PERMISSION_CREATE_TEAM = &Permission{ | ||||||
|  | 		"create_team", | ||||||
|  | 		"authentication.permissions.create_team.name", | ||||||
|  | 		"authentication.permissions.create_team.description", | ||||||
|  | 	} | ||||||
| 	PERMISSION_MANAGE_TEAM = &Permission{ | 	PERMISSION_MANAGE_TEAM = &Permission{ | ||||||
| 		"manage_team", | 		"manage_team", | ||||||
| 		"authentication.permissions.manage_team.name", | 		"authentication.permissions.manage_team.name", | ||||||
| @@ -238,6 +275,11 @@ func InitalizePermissions() { | |||||||
| 		"authentication.permissions.import_team.name", | 		"authentication.permissions.import_team.name", | ||||||
| 		"authentication.permissions.import_team.description", | 		"authentication.permissions.import_team.description", | ||||||
| 	} | 	} | ||||||
|  | 	PERMISSION_VIEW_TEAM = &Permission{ | ||||||
|  | 		"view_team", | ||||||
|  | 		"authentication.permissions.view_team.name", | ||||||
|  | 		"authentication.permissions.view_team.description", | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func InitalizeRoles() { | func InitalizeRoles() { | ||||||
| @@ -264,7 +306,9 @@ func InitalizeRoles() { | |||||||
| 		"channel_admin", | 		"channel_admin", | ||||||
| 		"authentication.roles.channel_admin.name", | 		"authentication.roles.channel_admin.name", | ||||||
| 		"authentication.roles.channel_admin.description", | 		"authentication.roles.channel_admin.description", | ||||||
| 		[]string{}, | 		[]string{ | ||||||
|  | 			PERMISSION_MANAGE_CHANNEL_ROLES.Id, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 	BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN | 	BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN | ||||||
| 	ROLE_CHANNEL_GUEST = &Role{ | 	ROLE_CHANNEL_GUEST = &Role{ | ||||||
| @@ -282,6 +326,7 @@ func InitalizeRoles() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			PERMISSION_LIST_TEAM_CHANNELS.Id, | 			PERMISSION_LIST_TEAM_CHANNELS.Id, | ||||||
| 			PERMISSION_JOIN_PUBLIC_CHANNELS.Id, | 			PERMISSION_JOIN_PUBLIC_CHANNELS.Id, | ||||||
|  | 			PERMISSION_VIEW_TEAM.Id, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER | 	BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER | ||||||
| @@ -295,7 +340,8 @@ func InitalizeRoles() { | |||||||
| 			PERMISSION_REMOVE_USER_FROM_TEAM.Id, | 			PERMISSION_REMOVE_USER_FROM_TEAM.Id, | ||||||
| 			PERMISSION_MANAGE_TEAM.Id, | 			PERMISSION_MANAGE_TEAM.Id, | ||||||
| 			PERMISSION_IMPORT_TEAM.Id, | 			PERMISSION_IMPORT_TEAM.Id, | ||||||
| 			PERMISSION_MANAGE_ROLES.Id, | 			PERMISSION_MANAGE_TEAM_ROLES.Id, | ||||||
|  | 			PERMISSION_MANAGE_CHANNEL_ROLES.Id, | ||||||
| 			PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, | 			PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, | ||||||
| 			PERMISSION_MANAGE_SLASH_COMMANDS.Id, | 			PERMISSION_MANAGE_SLASH_COMMANDS.Id, | ||||||
| 			PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, | 			PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, | ||||||
| @@ -310,6 +356,7 @@ func InitalizeRoles() { | |||||||
| 		"authentication.roles.global_user.description", | 		"authentication.roles.global_user.description", | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			PERMISSION_CREATE_DIRECT_CHANNEL.Id, | 			PERMISSION_CREATE_DIRECT_CHANNEL.Id, | ||||||
|  | 			PERMISSION_CREATE_GROUP_CHANNEL.Id, | ||||||
| 			PERMISSION_PERMANENT_DELETE_USER.Id, | 			PERMISSION_PERMANENT_DELETE_USER.Id, | ||||||
| 			PERMISSION_MANAGE_OAUTH.Id, | 			PERMISSION_MANAGE_OAUTH.Id, | ||||||
| 		}, | 		}, | ||||||
| @@ -329,6 +376,7 @@ func InitalizeRoles() { | |||||||
| 						[]string{ | 						[]string{ | ||||||
| 							PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, | 							PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id, | ||||||
| 							PERMISSION_MANAGE_SYSTEM.Id, | 							PERMISSION_MANAGE_SYSTEM.Id, | ||||||
|  | 							PERMISSION_MANAGE_ROLES.Id, | ||||||
| 							PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, | 							PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, | ||||||
| 							PERMISSION_DELETE_PUBLIC_CHANNEL.Id, | 							PERMISSION_DELETE_PUBLIC_CHANNEL.Id, | ||||||
| 							PERMISSION_CREATE_PUBLIC_CHANNEL.Id, | 							PERMISSION_CREATE_PUBLIC_CHANNEL.Id, | ||||||
| @@ -340,6 +388,9 @@ func InitalizeRoles() { | |||||||
| 							PERMISSION_EDIT_OTHER_USERS.Id, | 							PERMISSION_EDIT_OTHER_USERS.Id, | ||||||
| 							PERMISSION_MANAGE_OAUTH.Id, | 							PERMISSION_MANAGE_OAUTH.Id, | ||||||
| 							PERMISSION_INVITE_USER.Id, | 							PERMISSION_INVITE_USER.Id, | ||||||
|  | 							PERMISSION_DELETE_POST.Id, | ||||||
|  | 							PERMISSION_DELETE_OTHERS_POSTS.Id, | ||||||
|  | 							PERMISSION_CREATE_TEAM.Id, | ||||||
| 						}, | 						}, | ||||||
| 						ROLE_TEAM_USER.Permissions..., | 						ROLE_TEAM_USER.Permissions..., | ||||||
| 					), | 					), | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,8 +4,12 @@ | |||||||
| package model | package model | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"encoding/hex" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -13,11 +17,16 @@ const ( | |||||||
| 	CHANNEL_OPEN                   = "O" | 	CHANNEL_OPEN                   = "O" | ||||||
| 	CHANNEL_PRIVATE                = "P" | 	CHANNEL_PRIVATE                = "P" | ||||||
| 	CHANNEL_DIRECT                 = "D" | 	CHANNEL_DIRECT                 = "D" | ||||||
|  | 	CHANNEL_GROUP                  = "G" | ||||||
|  | 	CHANNEL_GROUP_MAX_USERS        = 8 | ||||||
|  | 	CHANNEL_GROUP_MIN_USERS        = 3 | ||||||
| 	DEFAULT_CHANNEL                = "town-square" | 	DEFAULT_CHANNEL                = "town-square" | ||||||
| 	CHANNEL_DISPLAY_NAME_MAX_RUNES = 64 | 	CHANNEL_DISPLAY_NAME_MAX_RUNES = 64 | ||||||
|  | 	CHANNEL_NAME_MIN_LENGTH        = 2 | ||||||
| 	CHANNEL_NAME_MAX_LENGTH        = 64 | 	CHANNEL_NAME_MAX_LENGTH        = 64 | ||||||
| 	CHANNEL_HEADER_MAX_RUNES       = 1024 | 	CHANNEL_HEADER_MAX_RUNES       = 1024 | ||||||
| 	CHANNEL_PURPOSE_MAX_RUNES      = 250 | 	CHANNEL_PURPOSE_MAX_RUNES      = 250 | ||||||
|  | 	CHANNEL_CACHE_SIZE             = 25000 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Channel struct { | type Channel struct { | ||||||
| @@ -83,15 +92,11 @@ func (o *Channel) IsValid() *AppError { | |||||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) | 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.Name) > CHANNEL_NAME_MAX_LENGTH { |  | ||||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !IsValidChannelIdentifier(o.Name) { | 	if !IsValidChannelIdentifier(o.Name) { | ||||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id) | 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) { | 	if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP) { | ||||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) | 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -128,6 +133,10 @@ func (o *Channel) ExtraUpdated() { | |||||||
| 	o.ExtraUpdateAt = GetMillis() | 	o.ExtraUpdateAt = GetMillis() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (o *Channel) IsGroupOrDirect() bool { | ||||||
|  | 	return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP | ||||||
|  | } | ||||||
|  |  | ||||||
| func GetDMNameFromIds(userId1, userId2 string) string { | func GetDMNameFromIds(userId1, userId2 string) string { | ||||||
| 	if userId1 > userId2 { | 	if userId1 > userId2 { | ||||||
| 		return userId2 + "__" + userId1 | 		return userId2 + "__" + userId1 | ||||||
| @@ -135,3 +144,31 @@ func GetDMNameFromIds(userId1, userId2 string) string { | |||||||
| 		return userId1 + "__" + userId2 | 		return userId1 + "__" + userId2 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func GetGroupDisplayNameFromUsers(users []*User, truncate bool) string { | ||||||
|  | 	usernames := make([]string, len(users)) | ||||||
|  | 	for index, user := range users { | ||||||
|  | 		usernames[index] = user.Username | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sort.Strings(usernames) | ||||||
|  |  | ||||||
|  | 	name := strings.Join(usernames, ", ") | ||||||
|  |  | ||||||
|  | 	if truncate && len(name) > CHANNEL_NAME_MAX_LENGTH { | ||||||
|  | 		name = name[:CHANNEL_NAME_MAX_LENGTH] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetGroupNameFromUserIds(userIds []string) string { | ||||||
|  | 	sort.Strings(userIds) | ||||||
|  |  | ||||||
|  | 	h := sha1.New() | ||||||
|  | 	for _, id := range userIds { | ||||||
|  | 		io.WriteString(h, id) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return hex.EncodeToString(h.Sum(nil)) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								vendor/github.com/mattermost/platform/model/channel_member.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/mattermost/platform/model/channel_member.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -88,18 +88,32 @@ func (o *ChannelMember) IsValid() *AppError { | |||||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") | 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	notifyLevel := o.NotifyProps["desktop"] | 	notifyLevel := o.NotifyProps[DESKTOP_NOTIFY_PROP] | ||||||
| 	if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { | 	if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { | ||||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", | 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", | ||||||
| 			nil, "notify_level="+notifyLevel) | 			nil, "notify_level="+notifyLevel) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	markUnreadLevel := o.NotifyProps["mark_unread"] | 	markUnreadLevel := o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] | ||||||
| 	if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { | 	if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { | ||||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", | 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", | ||||||
| 			nil, "mark_unread_level="+markUnreadLevel) | 			nil, "mark_unread_level="+markUnreadLevel) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if pushLevel, ok := o.NotifyProps[PUSH_NOTIFY_PROP]; ok { | ||||||
|  | 		if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) { | ||||||
|  | 			return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error", | ||||||
|  | 				nil, "push_notification_level="+pushLevel) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if sendEmail, ok := o.NotifyProps[EMAIL_NOTIFY_PROP]; ok { | ||||||
|  | 		if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) { | ||||||
|  | 			return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error", | ||||||
|  | 				nil, "push_notification_level="+sendEmail) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -126,9 +140,15 @@ func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool { | |||||||
| 	return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION | 	return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func IsSendEmailValid(sendEmail string) bool { | ||||||
|  | 	return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false" | ||||||
|  | } | ||||||
|  |  | ||||||
| func GetDefaultChannelNotifyProps() StringMap { | func GetDefaultChannelNotifyProps() StringMap { | ||||||
| 	return StringMap{ | 	return StringMap{ | ||||||
| 		"desktop":     CHANNEL_NOTIFY_DEFAULT, | 		DESKTOP_NOTIFY_PROP:     CHANNEL_NOTIFY_DEFAULT, | ||||||
| 		"mark_unread": CHANNEL_MARK_UNREAD_ALL, | 		MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL, | ||||||
|  | 		PUSH_NOTIFY_PROP:        CHANNEL_NOTIFY_DEFAULT, | ||||||
|  | 		EMAIL_NOTIFY_PROP:       CHANNEL_NOTIFY_DEFAULT, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -35,12 +35,14 @@ const ( | |||||||
| 	STATUS                    = "status" | 	STATUS                    = "status" | ||||||
| 	STATUS_OK                 = "OK" | 	STATUS_OK                 = "OK" | ||||||
| 	STATUS_FAIL               = "FAIL" | 	STATUS_FAIL               = "FAIL" | ||||||
|  | 	STATUS_REMOVE             = "REMOVE" | ||||||
|  |  | ||||||
| 	CLIENT_DIR = "webapp/dist" | 	CLIENT_DIR = "webapp/dist" | ||||||
|  |  | ||||||
| 	API_URL_SUFFIX_V1 = "/api/v1" | 	API_URL_SUFFIX_V1 = "/api/v1" | ||||||
| 	API_URL_SUFFIX_V3 = "/api/v3" | 	API_URL_SUFFIX_V3 = "/api/v3" | ||||||
| 	API_URL_SUFFIX    = API_URL_SUFFIX_V3 | 	API_URL_SUFFIX_V4 = "/api/v4" | ||||||
|  | 	API_URL_SUFFIX    = API_URL_SUFFIX_V4 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Result struct { | type Result struct { | ||||||
| @@ -71,7 +73,7 @@ type Client struct { | |||||||
| // NewClient constructs a new client with convienence methods for talking to | // NewClient constructs a new client with convienence methods for talking to | ||||||
| // the server. | // the server. | ||||||
| func NewClient(url string) *Client { | func NewClient(url string) *Client { | ||||||
| 	return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", "", "", "", "", ""} | 	return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""} | ||||||
| } | } | ||||||
|  |  | ||||||
| func closeBody(r *http.Response) { | func closeBody(r *http.Response) { | ||||||
| @@ -782,7 +784,7 @@ func (c *Client) GetSessions(id string) (*Result, *AppError) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) { | func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) { | ||||||
| 	if r, err := c.DoApiPost("/users/claim/email_to_sso", MapToJson(m)); err != nil { | 	if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} else { | 	} else { | ||||||
| 		defer closeBody(r) | 		defer closeBody(r) | ||||||
| @@ -1119,6 +1121,16 @@ func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) { | ||||||
|  | 	if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else { | ||||||
|  | 		defer closeBody(r) | ||||||
|  | 		return &Result{r.Header.Get(HEADER_REQUEST_ID), | ||||||
|  | 			r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) { | func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) { | ||||||
| 	if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil { | 	if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -1471,6 +1483,21 @@ func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMe | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetPermalink returns a post list, based on the provided channel and post ID. | ||||||
|  | func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) { | ||||||
|  | 	if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil { | ||||||
|  | 		return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err} | ||||||
|  | 	} else { | ||||||
|  | 		defer closeBody(r) | ||||||
|  | 		return PostListFromJson(r.Body), | ||||||
|  | 			&ResponseMetadata{ | ||||||
|  | 				StatusCode: r.StatusCode, | ||||||
|  | 				RequestId:  r.Header.Get(HEADER_REQUEST_ID), | ||||||
|  | 				Etag:       r.Header.Get(HEADER_ETAG_SERVER), | ||||||
|  | 			} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) { | func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) { | ||||||
| 	if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil { | 	if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -1991,6 +2018,16 @@ func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppErro | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) { | ||||||
|  | 	if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else { | ||||||
|  | 		defer closeBody(r) | ||||||
|  | 		return &Result{r.Header.Get(HEADER_REQUEST_ID), | ||||||
|  | 			r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) { | func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) { | ||||||
| 	if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil { | 	if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -2082,6 +2119,16 @@ func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppErro | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) { | ||||||
|  | 	if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else { | ||||||
|  | 		defer closeBody(r) | ||||||
|  | 		return &Result{r.Header.Get(HEADER_REQUEST_ID), | ||||||
|  | 			r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) { | func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) { | ||||||
| 	data := make(map[string]string) | 	data := make(map[string]string) | ||||||
| 	data["id"] = id | 	data["id"] = id | ||||||
| @@ -2319,3 +2366,26 @@ func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *A | |||||||
| 		return ReactionsFromJson(r.Body), nil | 		return ReactionsFromJson(r.Body), nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Updates the user's roles in the channel by replacing them with the roles provided. | ||||||
|  | func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) { | ||||||
|  | 	data := make(map[string]string) | ||||||
|  | 	data["new_roles"] = roles | ||||||
|  | 	data["user_id"] = userId | ||||||
|  |  | ||||||
|  | 	if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil { | ||||||
|  | 		metadata := ResponseMetadata{Error: err} | ||||||
|  | 		if r != nil { | ||||||
|  | 			metadata.StatusCode = r.StatusCode | ||||||
|  | 		} | ||||||
|  | 		return nil, &metadata | ||||||
|  | 	} else { | ||||||
|  | 		defer closeBody(r) | ||||||
|  | 		return MapFromJson(r.Body), | ||||||
|  | 			&ResponseMetadata{ | ||||||
|  | 				StatusCode: r.StatusCode, | ||||||
|  | 				RequestId:  r.Header.Get(HEADER_REQUEST_ID), | ||||||
|  | 				Etag:       r.Header.Get(HEADER_ETAG_SERVER), | ||||||
|  | 			} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										1006
									
								
								vendor/github.com/mattermost/platform/model/client4.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1006
									
								
								vendor/github.com/mattermost/platform/model/client4.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										30
									
								
								vendor/github.com/mattermost/platform/model/command_response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/mattermost/platform/model/command_response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,6 +5,7 @@ package model | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -14,12 +15,12 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type CommandResponse struct { | type CommandResponse struct { | ||||||
| 	ResponseType string      `json:"response_type"` | 	ResponseType string             `json:"response_type"` | ||||||
| 	Text         string      `json:"text"` | 	Text         string             `json:"text"` | ||||||
| 	Username     string      `json:"username"` | 	Username     string             `json:"username"` | ||||||
| 	IconURL      string      `json:"icon_url"` | 	IconURL      string             `json:"icon_url"` | ||||||
| 	GotoLocation string      `json:"goto_location"` | 	GotoLocation string             `json:"goto_location"` | ||||||
| 	Attachments  interface{} `json:"attachments"` | 	Attachments  []*SlackAttachment `json:"attachments"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (o *CommandResponse) ToJson() string { | func (o *CommandResponse) ToJson() string { | ||||||
| @@ -34,10 +35,19 @@ func (o *CommandResponse) ToJson() string { | |||||||
| func CommandResponseFromJson(data io.Reader) *CommandResponse { | func CommandResponseFromJson(data io.Reader) *CommandResponse { | ||||||
| 	decoder := json.NewDecoder(data) | 	decoder := json.NewDecoder(data) | ||||||
| 	var o CommandResponse | 	var o CommandResponse | ||||||
| 	err := decoder.Decode(&o) |  | ||||||
| 	if err == nil { | 	if err := decoder.Decode(&o); err != nil { | ||||||
| 		return &o |  | ||||||
| 	} else { |  | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure attachment fields are stored as strings | ||||||
|  | 	for _, attachment := range o.Attachments { | ||||||
|  | 		for _, field := range attachment.Fields { | ||||||
|  | 			if field.Value != nil { | ||||||
|  | 				field.Value = fmt.Sprintf("%v", field.Value) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &o | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										245
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										245
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -49,49 +49,109 @@ const ( | |||||||
| 	RESTRICT_EMOJI_CREATION_ADMIN        = "admin" | 	RESTRICT_EMOJI_CREATION_ADMIN        = "admin" | ||||||
| 	RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin" | 	RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin" | ||||||
|  |  | ||||||
|  | 	PERMISSIONS_DELETE_POST_ALL          = "all" | ||||||
|  | 	PERMISSIONS_DELETE_POST_TEAM_ADMIN   = "team_admin" | ||||||
|  | 	PERMISSIONS_DELETE_POST_SYSTEM_ADMIN = "system_admin" | ||||||
|  |  | ||||||
|  | 	ALLOW_EDIT_POST_ALWAYS     = "always" | ||||||
|  | 	ALLOW_EDIT_POST_NEVER      = "never" | ||||||
|  | 	ALLOW_EDIT_POST_TIME_LIMIT = "time_limit" | ||||||
|  |  | ||||||
| 	EMAIL_BATCHING_BUFFER_SIZE = 256 | 	EMAIL_BATCHING_BUFFER_SIZE = 256 | ||||||
| 	EMAIL_BATCHING_INTERVAL    = 30 | 	EMAIL_BATCHING_INTERVAL    = 30 | ||||||
|  |  | ||||||
| 	SITENAME_MAX_LENGTH = 30 | 	SITENAME_MAX_LENGTH = 30 | ||||||
|  |  | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_SITE_URL        = "" | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE   = "" | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE    = "" | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT    = 300 | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT   = 300 | ||||||
|  | 	SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = "" | ||||||
|  |  | ||||||
|  | 	TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT        = "" | ||||||
|  | 	TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT  = "" | ||||||
|  | 	TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT = 300 | ||||||
|  |  | ||||||
|  | 	EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = "" | ||||||
|  |  | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/" | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK   = "https://about.mattermost.com/default-privacy-policy/" | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK            = "https://about.mattermost.com/default-about/" | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_HELP_LINK             = "https://about.mattermost.com/default-help/" | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/" | ||||||
|  | 	SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL         = "feedback@mattermost.com" | ||||||
|  |  | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE  = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE      = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE   = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE   = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE         = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE   = "" | ||||||
|  | 	LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME     = "" | ||||||
|  |  | ||||||
|  | 	SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE  = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE      = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE   = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE   = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE     = "" | ||||||
|  | 	SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE   = "" | ||||||
|  |  | ||||||
|  | 	NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK         = "https://about.mattermost.com/downloads/" | ||||||
|  | 	NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-android-app/" | ||||||
|  | 	NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK     = "https://about.mattermost.com/mattermost-ios-app/" | ||||||
|  |  | ||||||
|  | 	WEBRTC_SETTINGS_DEFAULT_STUN_URI = "" | ||||||
|  | 	WEBRTC_SETTINGS_DEFAULT_TURN_URI = "" | ||||||
|  |  | ||||||
|  | 	ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type ServiceSettings struct { | type ServiceSettings struct { | ||||||
| 	SiteURL                           *string | 	SiteURL                                  *string | ||||||
| 	ListenAddress                     string | 	ListenAddress                            string | ||||||
| 	ConnectionSecurity                *string | 	ConnectionSecurity                       *string | ||||||
| 	TLSCertFile                       *string | 	TLSCertFile                              *string | ||||||
| 	TLSKeyFile                        *string | 	TLSKeyFile                               *string | ||||||
| 	UseLetsEncrypt                    *bool | 	UseLetsEncrypt                           *bool | ||||||
| 	LetsEncryptCertificateCacheFile   *string | 	LetsEncryptCertificateCacheFile          *string | ||||||
| 	Forward80To443                    *bool | 	Forward80To443                           *bool | ||||||
| 	ReadTimeout                       *int | 	ReadTimeout                              *int | ||||||
| 	WriteTimeout                      *int | 	WriteTimeout                             *int | ||||||
| 	MaximumLoginAttempts              int | 	MaximumLoginAttempts                     int | ||||||
| 	SegmentDeveloperKey               string | 	GoogleDeveloperKey                       string | ||||||
| 	GoogleDeveloperKey                string | 	EnableOAuthServiceProvider               bool | ||||||
| 	EnableOAuthServiceProvider        bool | 	EnableIncomingWebhooks                   bool | ||||||
| 	EnableIncomingWebhooks            bool | 	EnableOutgoingWebhooks                   bool | ||||||
| 	EnableOutgoingWebhooks            bool | 	EnableCommands                           *bool | ||||||
| 	EnableCommands                    *bool | 	EnableOnlyAdminIntegrations              *bool | ||||||
| 	EnableOnlyAdminIntegrations       *bool | 	EnablePostUsernameOverride               bool | ||||||
| 	EnablePostUsernameOverride        bool | 	EnablePostIconOverride                   bool | ||||||
| 	EnablePostIconOverride            bool | 	EnableLinkPreviews                       *bool | ||||||
| 	EnableTesting                     bool | 	EnableTesting                            bool | ||||||
| 	EnableDeveloper                   *bool | 	EnableDeveloper                          *bool | ||||||
| 	EnableSecurityFixAlert            *bool | 	EnableSecurityFixAlert                   *bool | ||||||
| 	EnableInsecureOutgoingConnections *bool | 	EnableInsecureOutgoingConnections        *bool | ||||||
| 	EnableMultifactorAuthentication   *bool | 	EnableMultifactorAuthentication          *bool | ||||||
| 	EnforceMultifactorAuthentication  *bool | 	EnforceMultifactorAuthentication         *bool | ||||||
| 	AllowCorsFrom                     *string | 	AllowCorsFrom                            *string | ||||||
| 	SessionLengthWebInDays            *int | 	SessionLengthWebInDays                   *int | ||||||
| 	SessionLengthMobileInDays         *int | 	SessionLengthMobileInDays                *int | ||||||
| 	SessionLengthSSOInDays            *int | 	SessionLengthSSOInDays                   *int | ||||||
| 	SessionCacheInMinutes             *int | 	SessionCacheInMinutes                    *int | ||||||
| 	WebsocketSecurePort               *int | 	WebsocketSecurePort                      *int | ||||||
| 	WebsocketPort                     *int | 	WebsocketPort                            *int | ||||||
| 	WebserverMode                     *string | 	WebserverMode                            *string | ||||||
| 	EnableCustomEmoji                 *bool | 	EnableCustomEmoji                        *bool | ||||||
| 	RestrictCustomEmojiCreation       *string | 	RestrictCustomEmojiCreation              *string | ||||||
|  | 	RestrictPostDelete                       *string | ||||||
|  | 	AllowEditPost                            *string | ||||||
|  | 	PostEditTimeLimit                        *int | ||||||
|  | 	TimeBetweenUserTypingUpdatesMilliseconds *int64 | ||||||
|  | 	EnableUserTypingMessages                 *bool | ||||||
|  | 	ClusterLogTimeoutMilliseconds            *int | ||||||
| } | } | ||||||
|  |  | ||||||
| type ClusterSettings struct { | type ClusterSettings struct { | ||||||
| @@ -433,7 +493,12 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.ServiceSettings.SiteURL == nil { | 	if o.ServiceSettings.SiteURL == nil { | ||||||
| 		o.ServiceSettings.SiteURL = new(string) | 		o.ServiceSettings.SiteURL = new(string) | ||||||
| 		*o.ServiceSettings.SiteURL = "" | 		*o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.EnableLinkPreviews == nil { | ||||||
|  | 		o.ServiceSettings.EnableLinkPreviews = new(bool) | ||||||
|  | 		*o.ServiceSettings.EnableLinkPreviews = false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.EnableDeveloper == nil { | 	if o.ServiceSettings.EnableDeveloper == nil { | ||||||
| @@ -493,12 +558,12 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.TeamSettings.CustomBrandText == nil { | 	if o.TeamSettings.CustomBrandText == nil { | ||||||
| 		o.TeamSettings.CustomBrandText = new(string) | 		o.TeamSettings.CustomBrandText = new(string) | ||||||
| 		*o.TeamSettings.CustomBrandText = "" | 		*o.TeamSettings.CustomBrandText = TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.TeamSettings.CustomDescriptionText == nil { | 	if o.TeamSettings.CustomDescriptionText == nil { | ||||||
| 		o.TeamSettings.CustomDescriptionText = new(string) | 		o.TeamSettings.CustomDescriptionText = new(string) | ||||||
| 		*o.TeamSettings.CustomDescriptionText = "" | 		*o.TeamSettings.CustomDescriptionText = TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.TeamSettings.EnableOpenServer == nil { | 	if o.TeamSettings.EnableOpenServer == nil { | ||||||
| @@ -552,7 +617,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.TeamSettings.UserStatusAwayTimeout == nil { | 	if o.TeamSettings.UserStatusAwayTimeout == nil { | ||||||
| 		o.TeamSettings.UserStatusAwayTimeout = new(int64) | 		o.TeamSettings.UserStatusAwayTimeout = new(int64) | ||||||
| 		*o.TeamSettings.UserStatusAwayTimeout = 300 | 		*o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.TeamSettings.MaxChannelsPerTeam == nil { | 	if o.TeamSettings.MaxChannelsPerTeam == nil { | ||||||
| @@ -597,7 +662,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.EmailSettings.FeedbackOrganization == nil { | 	if o.EmailSettings.FeedbackOrganization == nil { | ||||||
| 		o.EmailSettings.FeedbackOrganization = new(string) | 		o.EmailSettings.FeedbackOrganization = new(string) | ||||||
| 		*o.EmailSettings.FeedbackOrganization = "" | 		*o.EmailSettings.FeedbackOrganization = EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.EmailSettings.EnableEmailBatching == nil { | 	if o.EmailSettings.EnableEmailBatching == nil { | ||||||
| @@ -621,7 +686,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SupportSettings.TermsOfServiceLink == nil { | 	if o.SupportSettings.TermsOfServiceLink == nil { | ||||||
| 		o.SupportSettings.TermsOfServiceLink = new(string) | 		o.SupportSettings.TermsOfServiceLink = new(string) | ||||||
| 		*o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/" | 		*o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { | 	if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { | ||||||
| @@ -630,7 +695,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SupportSettings.PrivacyPolicyLink == nil { | 	if o.SupportSettings.PrivacyPolicyLink == nil { | ||||||
| 		o.SupportSettings.PrivacyPolicyLink = new(string) | 		o.SupportSettings.PrivacyPolicyLink = new(string) | ||||||
| 		*o.SupportSettings.PrivacyPolicyLink = "" | 		*o.SupportSettings.PrivacyPolicyLink = SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsSafeLink(o.SupportSettings.AboutLink) { | 	if !IsSafeLink(o.SupportSettings.AboutLink) { | ||||||
| @@ -639,7 +704,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SupportSettings.AboutLink == nil { | 	if o.SupportSettings.AboutLink == nil { | ||||||
| 		o.SupportSettings.AboutLink = new(string) | 		o.SupportSettings.AboutLink = new(string) | ||||||
| 		*o.SupportSettings.AboutLink = "" | 		*o.SupportSettings.AboutLink = SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsSafeLink(o.SupportSettings.HelpLink) { | 	if !IsSafeLink(o.SupportSettings.HelpLink) { | ||||||
| @@ -648,7 +713,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SupportSettings.HelpLink == nil { | 	if o.SupportSettings.HelpLink == nil { | ||||||
| 		o.SupportSettings.HelpLink = new(string) | 		o.SupportSettings.HelpLink = new(string) | ||||||
| 		*o.SupportSettings.HelpLink = "" | 		*o.SupportSettings.HelpLink = SUPPORT_SETTINGS_DEFAULT_HELP_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { | 	if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { | ||||||
| @@ -657,12 +722,12 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SupportSettings.ReportAProblemLink == nil { | 	if o.SupportSettings.ReportAProblemLink == nil { | ||||||
| 		o.SupportSettings.ReportAProblemLink = new(string) | 		o.SupportSettings.ReportAProblemLink = new(string) | ||||||
| 		*o.SupportSettings.ReportAProblemLink = "" | 		*o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SupportSettings.SupportEmail == nil { | 	if o.SupportSettings.SupportEmail == nil { | ||||||
| 		o.SupportSettings.SupportEmail = new(string) | 		o.SupportSettings.SupportEmail = new(string) | ||||||
| 		*o.SupportSettings.SupportEmail = "feedback@mattermost.com" | 		*o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.Enable == nil { | 	if o.LdapSettings.Enable == nil { | ||||||
| @@ -707,37 +772,37 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.LdapSettings.FirstNameAttribute == nil { | 	if o.LdapSettings.FirstNameAttribute == nil { | ||||||
| 		o.LdapSettings.FirstNameAttribute = new(string) | 		o.LdapSettings.FirstNameAttribute = new(string) | ||||||
| 		*o.LdapSettings.FirstNameAttribute = "" | 		*o.LdapSettings.FirstNameAttribute = LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.LastNameAttribute == nil { | 	if o.LdapSettings.LastNameAttribute == nil { | ||||||
| 		o.LdapSettings.LastNameAttribute = new(string) | 		o.LdapSettings.LastNameAttribute = new(string) | ||||||
| 		*o.LdapSettings.LastNameAttribute = "" | 		*o.LdapSettings.LastNameAttribute = LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.EmailAttribute == nil { | 	if o.LdapSettings.EmailAttribute == nil { | ||||||
| 		o.LdapSettings.EmailAttribute = new(string) | 		o.LdapSettings.EmailAttribute = new(string) | ||||||
| 		*o.LdapSettings.EmailAttribute = "" | 		*o.LdapSettings.EmailAttribute = LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.UsernameAttribute == nil { | 	if o.LdapSettings.UsernameAttribute == nil { | ||||||
| 		o.LdapSettings.UsernameAttribute = new(string) | 		o.LdapSettings.UsernameAttribute = new(string) | ||||||
| 		*o.LdapSettings.UsernameAttribute = "" | 		*o.LdapSettings.UsernameAttribute = LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.NicknameAttribute == nil { | 	if o.LdapSettings.NicknameAttribute == nil { | ||||||
| 		o.LdapSettings.NicknameAttribute = new(string) | 		o.LdapSettings.NicknameAttribute = new(string) | ||||||
| 		*o.LdapSettings.NicknameAttribute = "" | 		*o.LdapSettings.NicknameAttribute = LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.IdAttribute == nil { | 	if o.LdapSettings.IdAttribute == nil { | ||||||
| 		o.LdapSettings.IdAttribute = new(string) | 		o.LdapSettings.IdAttribute = new(string) | ||||||
| 		*o.LdapSettings.IdAttribute = "" | 		*o.LdapSettings.IdAttribute = LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.PositionAttribute == nil { | 	if o.LdapSettings.PositionAttribute == nil { | ||||||
| 		o.LdapSettings.PositionAttribute = new(string) | 		o.LdapSettings.PositionAttribute = new(string) | ||||||
| 		*o.LdapSettings.PositionAttribute = "" | 		*o.LdapSettings.PositionAttribute = LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.LdapSettings.SyncIntervalMinutes == nil { | 	if o.LdapSettings.SyncIntervalMinutes == nil { | ||||||
| @@ -762,7 +827,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.LdapSettings.LoginFieldName == nil { | 	if o.LdapSettings.LoginFieldName == nil { | ||||||
| 		o.LdapSettings.LoginFieldName = new(string) | 		o.LdapSettings.LoginFieldName = new(string) | ||||||
| 		*o.LdapSettings.LoginFieldName = "" | 		*o.LdapSettings.LoginFieldName = LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.SessionLengthWebInDays == nil { | 	if o.ServiceSettings.SessionLengthWebInDays == nil { | ||||||
| @@ -807,7 +872,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.ServiceSettings.AllowCorsFrom == nil { | 	if o.ServiceSettings.AllowCorsFrom == nil { | ||||||
| 		o.ServiceSettings.AllowCorsFrom = new(string) | 		o.ServiceSettings.AllowCorsFrom = new(string) | ||||||
| 		*o.ServiceSettings.AllowCorsFrom = "" | 		*o.ServiceSettings.AllowCorsFrom = SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.WebserverMode == nil { | 	if o.ServiceSettings.WebserverMode == nil { | ||||||
| @@ -827,6 +892,21 @@ func (o *Config) SetDefaults() { | |||||||
| 		*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL | 		*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.RestrictPostDelete == nil { | ||||||
|  | 		o.ServiceSettings.RestrictPostDelete = new(string) | ||||||
|  | 		*o.ServiceSettings.RestrictPostDelete = PERMISSIONS_DELETE_POST_ALL | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.AllowEditPost == nil { | ||||||
|  | 		o.ServiceSettings.AllowEditPost = new(string) | ||||||
|  | 		*o.ServiceSettings.AllowEditPost = ALLOW_EDIT_POST_ALWAYS | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.PostEditTimeLimit == nil { | ||||||
|  | 		o.ServiceSettings.PostEditTimeLimit = new(int) | ||||||
|  | 		*o.ServiceSettings.PostEditTimeLimit = 300 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if o.ClusterSettings.InterNodeListenAddress == nil { | 	if o.ClusterSettings.InterNodeListenAddress == nil { | ||||||
| 		o.ClusterSettings.InterNodeListenAddress = new(string) | 		o.ClusterSettings.InterNodeListenAddress = new(string) | ||||||
| 		*o.ClusterSettings.InterNodeListenAddress = ":8075" | 		*o.ClusterSettings.InterNodeListenAddress = ":8075" | ||||||
| @@ -853,7 +933,7 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.AnalyticsSettings.MaxUsersForStatistics == nil { | 	if o.AnalyticsSettings.MaxUsersForStatistics == nil { | ||||||
| 		o.AnalyticsSettings.MaxUsersForStatistics = new(int) | 		o.AnalyticsSettings.MaxUsersForStatistics = new(int) | ||||||
| 		*o.AnalyticsSettings.MaxUsersForStatistics = 2500 | 		*o.AnalyticsSettings.MaxUsersForStatistics = ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ComplianceSettings.Enable == nil { | 	if o.ComplianceSettings.Enable == nil { | ||||||
| @@ -943,52 +1023,52 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.SamlSettings.FirstNameAttribute == nil { | 	if o.SamlSettings.FirstNameAttribute == nil { | ||||||
| 		o.SamlSettings.FirstNameAttribute = new(string) | 		o.SamlSettings.FirstNameAttribute = new(string) | ||||||
| 		*o.SamlSettings.FirstNameAttribute = "" | 		*o.SamlSettings.FirstNameAttribute = SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.LastNameAttribute == nil { | 	if o.SamlSettings.LastNameAttribute == nil { | ||||||
| 		o.SamlSettings.LastNameAttribute = new(string) | 		o.SamlSettings.LastNameAttribute = new(string) | ||||||
| 		*o.SamlSettings.LastNameAttribute = "" | 		*o.SamlSettings.LastNameAttribute = SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.EmailAttribute == nil { | 	if o.SamlSettings.EmailAttribute == nil { | ||||||
| 		o.SamlSettings.EmailAttribute = new(string) | 		o.SamlSettings.EmailAttribute = new(string) | ||||||
| 		*o.SamlSettings.EmailAttribute = "" | 		*o.SamlSettings.EmailAttribute = SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.UsernameAttribute == nil { | 	if o.SamlSettings.UsernameAttribute == nil { | ||||||
| 		o.SamlSettings.UsernameAttribute = new(string) | 		o.SamlSettings.UsernameAttribute = new(string) | ||||||
| 		*o.SamlSettings.UsernameAttribute = "" | 		*o.SamlSettings.UsernameAttribute = SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.NicknameAttribute == nil { | 	if o.SamlSettings.NicknameAttribute == nil { | ||||||
| 		o.SamlSettings.NicknameAttribute = new(string) | 		o.SamlSettings.NicknameAttribute = new(string) | ||||||
| 		*o.SamlSettings.NicknameAttribute = "" | 		*o.SamlSettings.NicknameAttribute = SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.PositionAttribute == nil { | 	if o.SamlSettings.PositionAttribute == nil { | ||||||
| 		o.SamlSettings.PositionAttribute = new(string) | 		o.SamlSettings.PositionAttribute = new(string) | ||||||
| 		*o.SamlSettings.PositionAttribute = "" | 		*o.SamlSettings.PositionAttribute = SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.SamlSettings.LocaleAttribute == nil { | 	if o.SamlSettings.LocaleAttribute == nil { | ||||||
| 		o.SamlSettings.LocaleAttribute = new(string) | 		o.SamlSettings.LocaleAttribute = new(string) | ||||||
| 		*o.SamlSettings.LocaleAttribute = "" | 		*o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.NativeAppSettings.AppDownloadLink == nil { | 	if o.NativeAppSettings.AppDownloadLink == nil { | ||||||
| 		o.NativeAppSettings.AppDownloadLink = new(string) | 		o.NativeAppSettings.AppDownloadLink = new(string) | ||||||
| 		*o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/" | 		*o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.NativeAppSettings.AndroidAppDownloadLink == nil { | 	if o.NativeAppSettings.AndroidAppDownloadLink == nil { | ||||||
| 		o.NativeAppSettings.AndroidAppDownloadLink = new(string) | 		o.NativeAppSettings.AndroidAppDownloadLink = new(string) | ||||||
| 		*o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/" | 		*o.NativeAppSettings.AndroidAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.NativeAppSettings.IosAppDownloadLink == nil { | 	if o.NativeAppSettings.IosAppDownloadLink == nil { | ||||||
| 		o.NativeAppSettings.IosAppDownloadLink = new(string) | 		o.NativeAppSettings.IosAppDownloadLink = new(string) | ||||||
| 		*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/" | 		*o.NativeAppSettings.IosAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.RateLimitSettings.Enable == nil { | 	if o.RateLimitSettings.Enable == nil { | ||||||
| @@ -1008,12 +1088,12 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.ServiceSettings.TLSKeyFile == nil { | 	if o.ServiceSettings.TLSKeyFile == nil { | ||||||
| 		o.ServiceSettings.TLSKeyFile = new(string) | 		o.ServiceSettings.TLSKeyFile = new(string) | ||||||
| 		*o.ServiceSettings.TLSKeyFile = "" | 		*o.ServiceSettings.TLSKeyFile = SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.TLSCertFile == nil { | 	if o.ServiceSettings.TLSCertFile == nil { | ||||||
| 		o.ServiceSettings.TLSCertFile = new(string) | 		o.ServiceSettings.TLSCertFile = new(string) | ||||||
| 		*o.ServiceSettings.TLSCertFile = "" | 		*o.ServiceSettings.TLSCertFile = SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.UseLetsEncrypt == nil { | 	if o.ServiceSettings.UseLetsEncrypt == nil { | ||||||
| @@ -1028,12 +1108,12 @@ func (o *Config) SetDefaults() { | |||||||
|  |  | ||||||
| 	if o.ServiceSettings.ReadTimeout == nil { | 	if o.ServiceSettings.ReadTimeout == nil { | ||||||
| 		o.ServiceSettings.ReadTimeout = new(int) | 		o.ServiceSettings.ReadTimeout = new(int) | ||||||
| 		*o.ServiceSettings.ReadTimeout = 300 | 		*o.ServiceSettings.ReadTimeout = SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.WriteTimeout == nil { | 	if o.ServiceSettings.WriteTimeout == nil { | ||||||
| 		o.ServiceSettings.WriteTimeout = new(int) | 		o.ServiceSettings.WriteTimeout = new(int) | ||||||
| 		*o.ServiceSettings.WriteTimeout = 300 | 		*o.ServiceSettings.WriteTimeout = SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.ServiceSettings.Forward80To443 == nil { | 	if o.ServiceSettings.Forward80To443 == nil { | ||||||
| @@ -1046,6 +1126,21 @@ func (o *Config) SetDefaults() { | |||||||
| 		*o.MetricsSettings.BlockProfileRate = 0 | 		*o.MetricsSettings.BlockProfileRate = 0 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds == nil { | ||||||
|  | 		o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = new(int64) | ||||||
|  | 		*o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.EnableUserTypingMessages == nil { | ||||||
|  | 		o.ServiceSettings.EnableUserTypingMessages = new(bool) | ||||||
|  | 		*o.ServiceSettings.EnableUserTypingMessages = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil { | ||||||
|  | 		o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int) | ||||||
|  | 		*o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	o.defaultWebrtcSettings() | 	o.defaultWebrtcSettings() | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1277,6 +1372,10 @@ func (o *Config) IsValid() *AppError { | |||||||
| 		return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "") | 		return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds < 1000 { | ||||||
|  | 		return NewLocAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1339,12 +1438,12 @@ func (o *Config) defaultWebrtcSettings() { | |||||||
|  |  | ||||||
| 	if o.WebrtcSettings.StunURI == nil { | 	if o.WebrtcSettings.StunURI == nil { | ||||||
| 		o.WebrtcSettings.StunURI = new(string) | 		o.WebrtcSettings.StunURI = new(string) | ||||||
| 		*o.WebrtcSettings.StunURI = "" | 		*o.WebrtcSettings.StunURI = WEBRTC_SETTINGS_DEFAULT_STUN_URI | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.WebrtcSettings.TurnURI == nil { | 	if o.WebrtcSettings.TurnURI == nil { | ||||||
| 		o.WebrtcSettings.TurnURI = new(string) | 		o.WebrtcSettings.TurnURI = new(string) | ||||||
| 		*o.WebrtcSettings.TurnURI = "" | 		*o.WebrtcSettings.TurnURI = WEBRTC_SETTINGS_DEFAULT_TURN_URI | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.WebrtcSettings.TurnUsername == nil { | 	if o.WebrtcSettings.TurnUsername == nil { | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								vendor/github.com/mattermost/platform/model/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/mattermost/platform/model/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,6 +8,10 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image | ||||||
|  | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"} | 	IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"} | ||||||
| 	IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"} | 	IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"} | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -65,6 +65,15 @@ func gitLabUserFromJson(data io.Reader) *GitLabUser { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (glu *GitLabUser) ToJson() string { | ||||||
|  | 	b, err := json.Marshal(glu) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} else { | ||||||
|  | 		return string(b) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (glu *GitLabUser) IsValid() bool { | func (glu *GitLabUser) IsValid() bool { | ||||||
| 	if glu.Id == 0 { | 	if glu.Id == 0 { | ||||||
| 		return false | 		return false | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -29,13 +29,13 @@ type IncomingWebhook struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type IncomingWebhookRequest struct { | type IncomingWebhookRequest struct { | ||||||
| 	Text        string          `json:"text"` | 	Text        string             `json:"text"` | ||||||
| 	Username    string          `json:"username"` | 	Username    string             `json:"username"` | ||||||
| 	IconURL     string          `json:"icon_url"` | 	IconURL     string             `json:"icon_url"` | ||||||
| 	ChannelName string          `json:"channel"` | 	ChannelName string             `json:"channel"` | ||||||
| 	Props       StringInterface `json:"props"` | 	Props       StringInterface    `json:"props"` | ||||||
| 	Attachments interface{}     `json:"attachments"` | 	Attachments []*SlackAttachment `json:"attachments"` | ||||||
| 	Type        string          `json:"type"` | 	Type        string             `json:"type"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (o *IncomingWebhook) ToJson() string { | func (o *IncomingWebhook) ToJson() string { | ||||||
| @@ -212,31 +212,15 @@ func expandAnnouncement(text string) string { | |||||||
| func expandAnnouncements(i *IncomingWebhookRequest) { | func expandAnnouncements(i *IncomingWebhookRequest) { | ||||||
| 	i.Text = expandAnnouncement(i.Text) | 	i.Text = expandAnnouncement(i.Text) | ||||||
|  |  | ||||||
| 	if i.Attachments != nil { | 	for _, attachment := range i.Attachments { | ||||||
| 		attachments := i.Attachments.([]interface{}) | 		attachment.Pretext = expandAnnouncement(attachment.Pretext) | ||||||
| 		for _, attachment := range attachments { | 		attachment.Text = expandAnnouncement(attachment.Text) | ||||||
| 			a := attachment.(map[string]interface{}) | 		attachment.Title = expandAnnouncement(attachment.Title) | ||||||
|  |  | ||||||
| 			if a["pretext"] != nil { | 		for _, field := range attachment.Fields { | ||||||
| 				a["pretext"] = expandAnnouncement(a["pretext"].(string)) | 			if field.Value != nil { | ||||||
| 			} | 				// Ensure the value is set to a string if it is set | ||||||
|  | 				field.Value = expandAnnouncement(fmt.Sprintf("%v", field.Value)) | ||||||
| 			if a["text"] != nil { |  | ||||||
| 				a["text"] = expandAnnouncement(a["text"].(string)) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if a["title"] != nil { |  | ||||||
| 				a["title"] = expandAnnouncement(a["title"].(string)) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if a["fields"] != nil { |  | ||||||
| 				fields := a["fields"].([]interface{}) |  | ||||||
| 				for _, field := range fields { |  | ||||||
| 					f := field.(map[string]interface{}) |  | ||||||
| 					if f["value"] != nil { |  | ||||||
| 						f["value"] = expandAnnouncement(fmt.Sprintf("%v", f["value"])) |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								vendor/github.com/mattermost/platform/model/job.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/mattermost/platform/model/job.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -14,8 +14,8 @@ type ScheduledTask struct { | |||||||
| 	Name      string        `json:"name"` | 	Name      string        `json:"name"` | ||||||
| 	Interval  time.Duration `json:"interval"` | 	Interval  time.Duration `json:"interval"` | ||||||
| 	Recurring bool          `json:"recurring"` | 	Recurring bool          `json:"recurring"` | ||||||
| 	function  TaskFunc      `json:",omitempty"` | 	function  TaskFunc | ||||||
| 	timer     *time.Timer   `json:",omitempty"` | 	timer     *time.Timer | ||||||
| } | } | ||||||
|  |  | ||||||
| var tasks = make(map[string]*ScheduledTask) | var tasks = make(map[string]*ScheduledTask) | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/model/license.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/model/license.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,6 +8,11 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error" | ||||||
|  | 	INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" | ||||||
|  | ) | ||||||
|  |  | ||||||
| type LicenseRecord struct { | type LicenseRecord struct { | ||||||
| 	Id       string `json:"id"` | 	Id       string `json:"id"` | ||||||
| 	CreateAt int64  `json:"create_at"` | 	CreateAt int64  `json:"create_at"` | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								vendor/github.com/mattermost/platform/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/mattermost/platform/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -14,10 +14,15 @@ const ( | |||||||
| 	POST_DEFAULT               = "" | 	POST_DEFAULT               = "" | ||||||
| 	POST_SLACK_ATTACHMENT      = "slack_attachment" | 	POST_SLACK_ATTACHMENT      = "slack_attachment" | ||||||
| 	POST_SYSTEM_GENERIC        = "system_generic" | 	POST_SYSTEM_GENERIC        = "system_generic" | ||||||
| 	POST_JOIN_LEAVE            = "system_join_leave" | 	POST_JOIN_LEAVE            = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead | ||||||
| 	POST_ADD_REMOVE            = "system_add_remove" | 	POST_JOIN_CHANNEL          = "system_join_channel" | ||||||
|  | 	POST_LEAVE_CHANNEL         = "system_leave_channel" | ||||||
|  | 	POST_ADD_REMOVE            = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead | ||||||
|  | 	POST_ADD_TO_CHANNEL        = "system_add_to_channel" | ||||||
|  | 	POST_REMOVE_FROM_CHANNEL   = "system_remove_from_channel" | ||||||
| 	POST_HEADER_CHANGE         = "system_header_change" | 	POST_HEADER_CHANGE         = "system_header_change" | ||||||
| 	POST_DISPLAYNAME_CHANGE    = "system_displayname_change" | 	POST_DISPLAYNAME_CHANGE    = "system_displayname_change" | ||||||
|  | 	POST_PURPOSE_CHANGE        = "system_purpose_change" | ||||||
| 	POST_CHANNEL_DELETED       = "system_channel_deleted" | 	POST_CHANNEL_DELETED       = "system_channel_deleted" | ||||||
| 	POST_EPHEMERAL             = "system_ephemeral" | 	POST_EPHEMERAL             = "system_ephemeral" | ||||||
| 	POST_FILEIDS_MAX_RUNES     = 150 | 	POST_FILEIDS_MAX_RUNES     = 150 | ||||||
| @@ -31,6 +36,7 @@ type Post struct { | |||||||
| 	Id            string          `json:"id"` | 	Id            string          `json:"id"` | ||||||
| 	CreateAt      int64           `json:"create_at"` | 	CreateAt      int64           `json:"create_at"` | ||||||
| 	UpdateAt      int64           `json:"update_at"` | 	UpdateAt      int64           `json:"update_at"` | ||||||
|  | 	EditAt        int64           `json:"edit_at"` | ||||||
| 	DeleteAt      int64           `json:"delete_at"` | 	DeleteAt      int64           `json:"delete_at"` | ||||||
| 	UserId        string          `json:"user_id"` | 	UserId        string          `json:"user_id"` | ||||||
| 	ChannelId     string          `json:"channel_id"` | 	ChannelId     string          `json:"channel_id"` | ||||||
| @@ -119,7 +125,9 @@ func (o *Post) IsValid() *AppError { | |||||||
|  |  | ||||||
| 	// should be removed once more message types are supported | 	// should be removed once more message types are supported | ||||||
| 	if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE || | 	if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE || | ||||||
| 		o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || | 		o.Type == POST_JOIN_CHANNEL || o.Type == POST_LEAVE_CHANNEL || | ||||||
|  | 		o.Type == POST_REMOVE_FROM_CHANNEL || o.Type == POST_ADD_TO_CHANNEL || | ||||||
|  | 		o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || o.Type == POST_PURPOSE_CHANGE || | ||||||
| 		o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) { | 		o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) { | ||||||
| 		return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type) | 		return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type) | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								vendor/github.com/mattermost/platform/model/post_list.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/mattermost/platform/model/post_list.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,6 +13,13 @@ type PostList struct { | |||||||
| 	Posts map[string]*Post `json:"posts"` | 	Posts map[string]*Post `json:"posts"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewPostList() *PostList { | ||||||
|  | 	return &PostList{ | ||||||
|  | 		Order: make([]string, 0), | ||||||
|  | 		Posts: make(map[string]*Post), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (o *PostList) ToJson() string { | func (o *PostList) ToJson() string { | ||||||
| 	b, err := json.Marshal(o) | 	b, err := json.Marshal(o) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -72,10 +79,18 @@ func (o *PostList) Etag() string { | |||||||
| 		if v.UpdateAt > t { | 		if v.UpdateAt > t { | ||||||
| 			t = v.UpdateAt | 			t = v.UpdateAt | ||||||
| 			id = v.Id | 			id = v.Id | ||||||
|  | 		} else if v.UpdateAt == t && v.Id > id { | ||||||
|  | 			t = v.UpdateAt | ||||||
|  | 			id = v.Id | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return Etag(id, t) | 	orderId := "" | ||||||
|  | 	if len(o.Order) > 0 { | ||||||
|  | 		orderId = o.Order[0] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return Etag(orderId, id, t) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (o *PostList) IsChannelId(channelId string) bool { | func (o *PostList) IsChannelId(channelId string) bool { | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,8 +10,10 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	PUSH_NOTIFY_APPLE   = "apple" | 	PUSH_NOTIFY_APPLE                = "apple" | ||||||
| 	PUSH_NOTIFY_ANDROID = "android" | 	PUSH_NOTIFY_ANDROID              = "android" | ||||||
|  | 	PUSH_NOTIFY_APPLE_REACT_NATIVE   = "apple_rn" | ||||||
|  | 	PUSH_NOTIFY_ANDROID_REACT_NATIVE = "android_rn" | ||||||
|  |  | ||||||
| 	PUSH_TYPE_MESSAGE = "message" | 	PUSH_TYPE_MESSAGE = "message" | ||||||
| 	PUSH_TYPE_CLEAR   = "clear" | 	PUSH_TYPE_CLEAR   = "clear" | ||||||
| @@ -46,12 +48,12 @@ func (me *PushNotification) ToJson() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) { | func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) { | ||||||
| 	if strings.HasPrefix(deviceId, PUSH_NOTIFY_APPLE+":") { |  | ||||||
| 		me.Platform = PUSH_NOTIFY_APPLE | 	index := strings.Index(deviceId, ":") | ||||||
| 		me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_APPLE+":") |  | ||||||
| 	} else if strings.HasPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") { | 	if index > -1 { | ||||||
| 		me.Platform = PUSH_NOTIFY_ANDROID | 		me.Platform = deviceId[:index] | ||||||
| 		me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") | 		me.DeviceId = deviceId[index+1:] | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								vendor/github.com/mattermost/platform/model/push_response.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/mattermost/platform/model/push_response.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||||
|  | // See License.txt for license information. | ||||||
|  |  | ||||||
|  | package model | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	PUSH_STATUS           = "status" | ||||||
|  | 	PUSH_STATUS_OK        = "OK" | ||||||
|  | 	PUSH_STATUS_FAIL      = "FAIL" | ||||||
|  | 	PUSH_STATUS_REMOVE    = "REMOVE" | ||||||
|  | 	PUSH_STATUS_ERROR_MSG = "error" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type PushResponse map[string]string | ||||||
|  |  | ||||||
|  | func NewOkPushResponse() PushResponse { | ||||||
|  | 	m := make(map[string]string) | ||||||
|  | 	m[PUSH_STATUS] = PUSH_STATUS_OK | ||||||
|  | 	return m | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRemovePushResponse() PushResponse { | ||||||
|  | 	m := make(map[string]string) | ||||||
|  | 	m[PUSH_STATUS] = PUSH_STATUS_REMOVE | ||||||
|  | 	return m | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewErrorPushResponse(message string) PushResponse { | ||||||
|  | 	m := make(map[string]string) | ||||||
|  | 	m[PUSH_STATUS] = PUSH_STATUS_FAIL | ||||||
|  | 	m[PUSH_STATUS_ERROR_MSG] = message | ||||||
|  | 	return m | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (me *PushResponse) ToJson() string { | ||||||
|  | 	if b, err := json.Marshal(me); err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} else { | ||||||
|  | 		return string(b) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func PushResponseFromJson(data io.Reader) PushResponse { | ||||||
|  | 	decoder := json.NewDecoder(data) | ||||||
|  |  | ||||||
|  | 	var objmap PushResponse | ||||||
|  | 	if err := decoder.Decode(&objmap); err != nil { | ||||||
|  | 		return make(map[string]string) | ||||||
|  | 	} else { | ||||||
|  | 		return objmap | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/model/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/model/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ import ( | |||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	SESSION_COOKIE_TOKEN  = "MMAUTHTOKEN" | 	SESSION_COOKIE_TOKEN  = "MMAUTHTOKEN" | ||||||
| 	SESSION_CACHE_SIZE    = 25000 | 	SESSION_CACHE_SIZE    = 35000 | ||||||
| 	SESSION_PROP_PLATFORM = "platform" | 	SESSION_PROP_PLATFORM = "platform" | ||||||
| 	SESSION_PROP_OS       = "os" | 	SESSION_PROP_OS       = "os" | ||||||
| 	SESSION_PROP_BROWSER  = "browser" | 	SESSION_PROP_BROWSER  = "browser" | ||||||
| @@ -111,8 +111,7 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (me *Session) IsMobileApp() bool { | func (me *Session) IsMobileApp() bool { | ||||||
| 	return len(me.DeviceId) > 0 && | 	return len(me.DeviceId) > 0 | ||||||
| 		(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":")) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (me *Session) GetUserRoles() []string { | func (me *Session) GetUserRoles() []string { | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								vendor/github.com/mattermost/platform/model/slack_attachment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/mattermost/platform/model/slack_attachment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | // Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. | ||||||
|  | // See License.txt for license information. | ||||||
|  |  | ||||||
|  | package model | ||||||
|  |  | ||||||
|  | type SlackAttachment struct { | ||||||
|  | 	Id         int64                   `json:"id"` | ||||||
|  | 	Fallback   string                  `json:"fallback"` | ||||||
|  | 	Color      string                  `json:"color"` | ||||||
|  | 	Pretext    string                  `json:"pretext"` | ||||||
|  | 	AuthorName string                  `json:"author_name"` | ||||||
|  | 	AuthorLink string                  `json:"author_link"` | ||||||
|  | 	AuthorIcon string                  `json:"author_icon"` | ||||||
|  | 	Title      string                  `json:"title"` | ||||||
|  | 	TitleLink  string                  `json:"title_link"` | ||||||
|  | 	Text       string                  `json:"text"` | ||||||
|  | 	Fields     []*SlackAttachmentField `json:"fields"` | ||||||
|  | 	ImageURL   string                  `json:"image_url"` | ||||||
|  | 	ThumbURL   string                  `json:"thumb_url"` | ||||||
|  | 	Footer     string                  `json:"footer"` | ||||||
|  | 	FooterIcon string                  `json:"footer_icon"` | ||||||
|  | 	Timestamp  interface{}             `json:"ts"` // This is either a string or an int64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SlackAttachmentField struct { | ||||||
|  | 	Title string      `json:"title"` | ||||||
|  | 	Value interface{} `json:"value"` | ||||||
|  | 	Short bool        `json:"short"` | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								vendor/github.com/mattermost/platform/model/status.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/mattermost/platform/model/status.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,7 +12,7 @@ const ( | |||||||
| 	STATUS_OFFLINE         = "offline" | 	STATUS_OFFLINE         = "offline" | ||||||
| 	STATUS_AWAY            = "away" | 	STATUS_AWAY            = "away" | ||||||
| 	STATUS_ONLINE          = "online" | 	STATUS_ONLINE          = "online" | ||||||
| 	STATUS_CACHE_SIZE      = 25000 | 	STATUS_CACHE_SIZE      = SESSION_CACHE_SIZE | ||||||
| 	STATUS_CHANNEL_TIMEOUT = 20000  // 20 seconds | 	STATUS_CHANNEL_TIMEOUT = 20000  // 20 seconds | ||||||
| 	STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes | 	STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,14 +7,22 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net/http" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	TEAM_OPEN   = "O" | 	TEAM_OPEN                       = "O" | ||||||
| 	TEAM_INVITE = "I" | 	TEAM_INVITE                     = "I" | ||||||
|  | 	TEAM_ALLOWED_DOMAINS_MAX_LENGTH = 500 | ||||||
|  | 	TEAM_COMPANY_NAME_MAX_LENGTH    = 64 | ||||||
|  | 	TEAM_DESCRIPTION_MAX_LENGTH     = 255 | ||||||
|  | 	TEAM_DISPLAY_NAME_MAX_RUNES     = 64 | ||||||
|  | 	TEAM_EMAIL_MAX_LENGTH           = 128 | ||||||
|  | 	TEAM_NAME_MAX_LENGTH            = 64 | ||||||
|  | 	TEAM_NAME_MIN_LENGTH            = 2 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Team struct { | type Team struct { | ||||||
| @@ -48,6 +56,14 @@ func InvitesFromJson(data io.Reader) *Invites { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (o *Invites) ToEmailList() []string { | ||||||
|  | 	emailList := make([]string, len(o.Invites)) | ||||||
|  | 	for _, invite := range o.Invites { | ||||||
|  | 		emailList = append(emailList, invite["email"]) | ||||||
|  | 	} | ||||||
|  | 	return emailList | ||||||
|  | } | ||||||
|  |  | ||||||
| func (o *Invites) ToJson() string { | func (o *Invites) ToJson() string { | ||||||
| 	b, err := json.Marshal(o) | 	b, err := json.Marshal(o) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -97,6 +113,26 @@ func TeamMapFromJson(data io.Reader) map[string]*Team { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TeamListToJson(t []*Team) string { | ||||||
|  | 	b, err := json.Marshal(t) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} else { | ||||||
|  | 		return string(b) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TeamListFromJson(data io.Reader) []*Team { | ||||||
|  | 	decoder := json.NewDecoder(data) | ||||||
|  | 	var teams []*Team | ||||||
|  | 	err := decoder.Decode(&teams) | ||||||
|  | 	if err == nil { | ||||||
|  | 		return teams | ||||||
|  | 	} else { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (o *Team) Etag() string { | func (o *Team) Etag() string { | ||||||
| 	return Etag(o.Id, o.UpdateAt) | 	return Etag(o.Id, o.UpdateAt) | ||||||
| } | } | ||||||
| @@ -104,55 +140,55 @@ func (o *Team) Etag() string { | |||||||
| func (o *Team) IsValid() *AppError { | func (o *Team) IsValid() *AppError { | ||||||
|  |  | ||||||
| 	if len(o.Id) != 26 { | 	if len(o.Id) != 26 { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "") | 		return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.CreateAt == 0 { | 	if o.CreateAt == 0 { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if o.UpdateAt == 0 { | 	if o.UpdateAt == 0 { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.Email) > 128 { | 	if len(o.Email) > TEAM_EMAIL_MAX_LENGTH { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.Email) > 0 && !IsValidEmail(o.Email) { | 	if len(o.Email) > 0 && !IsValidEmail(o.Email) { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > 64 { | 	if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > TEAM_DISPLAY_NAME_MAX_RUNES { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.Name) > 64 { | 	if len(o.Name) > TEAM_NAME_MAX_LENGTH { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.Description) > 255 { | 	if len(o.Description) > TEAM_DESCRIPTION_MAX_LENGTH { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if IsReservedTeamName(o.Name) { | 	if IsReservedTeamName(o.Name) { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsValidTeamName(o.Name) { | 	if !IsValidTeamName(o.Name) { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) { | 	if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.CompanyName) > 64 { | 	if len(o.CompanyName) > TEAM_COMPANY_NAME_MAX_LENGTH { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(o.AllowedDomains) > 500 { | 	if len(o.AllowedDomains) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH { | ||||||
| 		return NewLocAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id) | 		return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -193,7 +229,7 @@ func IsValidTeamName(s string) bool { | |||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(s) <= 1 { | 	if len(s) < TEAM_NAME_MIN_LENGTH { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,20 +7,36 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net/http" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"unicode" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
|  |  | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	USER_NOTIFY_ALL            = "all" | 	USER_NOTIFY_ALL         = "all" | ||||||
| 	USER_NOTIFY_MENTION        = "mention" | 	USER_NOTIFY_MENTION     = "mention" | ||||||
| 	USER_NOTIFY_NONE           = "none" | 	USER_NOTIFY_NONE        = "none" | ||||||
|  | 	DESKTOP_NOTIFY_PROP     = "desktop" | ||||||
|  | 	MARK_UNREAD_NOTIFY_PROP = "mark_unread" | ||||||
|  | 	PUSH_NOTIFY_PROP        = "push" | ||||||
|  | 	EMAIL_NOTIFY_PROP       = "email" | ||||||
|  |  | ||||||
| 	DEFAULT_LOCALE             = "en" | 	DEFAULT_LOCALE             = "en" | ||||||
| 	USER_AUTH_SERVICE_EMAIL    = "email" | 	USER_AUTH_SERVICE_EMAIL    = "email" | ||||||
| 	USER_AUTH_SERVICE_USERNAME = "username" | 	USER_AUTH_SERVICE_USERNAME = "username" | ||||||
|  |  | ||||||
|  | 	USER_EMAIL_MAX_LENGTH     = 128 | ||||||
|  | 	USER_NICKNAME_MAX_RUNES   = 64 | ||||||
|  | 	USER_POSITION_MAX_RUNES   = 35 | ||||||
|  | 	USER_FIRST_NAME_MAX_RUNES = 64 | ||||||
|  | 	USER_LAST_NAME_MAX_RUNES  = 64 | ||||||
|  | 	USER_AUTH_DATA_MAX_LENGTH = 128 | ||||||
|  | 	USER_NAME_MAX_LENGTH      = 64 | ||||||
|  | 	USER_NAME_MIN_LENGTH      = 1 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type User struct { | type User struct { | ||||||
| @@ -51,56 +67,68 @@ type User struct { | |||||||
| 	LastActivityAt     int64     `db:"-" json:"last_activity_at,omitempty"` | 	LastActivityAt     int64     `db:"-" json:"last_activity_at,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type UserPatch struct { | ||||||
|  | 	Username    *string    `json:"username"` | ||||||
|  | 	Nickname    *string    `json:"nickname"` | ||||||
|  | 	FirstName   *string    `json:"first_name"` | ||||||
|  | 	LastName    *string    `json:"last_name"` | ||||||
|  | 	Position    *string    `json:"position"` | ||||||
|  | 	Email       *string    `json:"email"` | ||||||
|  | 	Props       *StringMap `json:"props,omitempty"` | ||||||
|  | 	NotifyProps *StringMap `json:"notify_props,omitempty"` | ||||||
|  | 	Locale      *string    `json:"locale"` | ||||||
|  | } | ||||||
|  |  | ||||||
| // IsValid validates the user and returns an error if it isn't configured | // IsValid validates the user and returns an error if it isn't configured | ||||||
| // correctly. | // correctly. | ||||||
| func (u *User) IsValid() *AppError { | func (u *User) IsValid() *AppError { | ||||||
|  |  | ||||||
| 	if len(u.Id) != 26 { | 	if len(u.Id) != 26 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "") | 		return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.CreateAt == 0 { | 	if u.CreateAt == 0 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.UpdateAt == 0 { | 	if u.UpdateAt == 0 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !IsValidUsername(u.Username) { | 	if !IsValidUsername(u.Username) { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(u.Email) > 128 || len(u.Email) == 0 { | 	if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if utf8.RuneCountInString(u.Nickname) > 64 { | 	if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if utf8.RuneCountInString(u.Position) > 35 { | 	if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if utf8.RuneCountInString(u.FirstName) > 64 { | 	if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if utf8.RuneCountInString(u.LastName) > 64 { | 	if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.AuthData != nil && len(*u.AuthData) > 128 { | 	if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 { | 	if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 { | 	if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 { | ||||||
| 		return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id) | 		return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -115,7 +143,7 @@ func (u *User) PreSave() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.Username == "" { | 	if u.Username == "" { | ||||||
| 		u.Username = NewId() | 		u.Username = "n" + NewId() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if u.AuthData != nil && *u.AuthData == "" { | 	if u.AuthData != nil && *u.AuthData == "" { | ||||||
| @@ -205,6 +233,44 @@ func (user *User) UpdateMentionKeysFromUsername(oldUsername string) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (u *User) Patch(patch *UserPatch) { | ||||||
|  | 	if patch.Username != nil { | ||||||
|  | 		u.Username = *patch.Username | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.Nickname != nil { | ||||||
|  | 		u.Nickname = *patch.Nickname | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.FirstName != nil { | ||||||
|  | 		u.FirstName = *patch.FirstName | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.LastName != nil { | ||||||
|  | 		u.LastName = *patch.LastName | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.Position != nil { | ||||||
|  | 		u.Position = *patch.Position | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.Email != nil { | ||||||
|  | 		u.Email = *patch.Email | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.Props != nil { | ||||||
|  | 		u.Props = *patch.Props | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.NotifyProps != nil { | ||||||
|  | 		u.NotifyProps = *patch.NotifyProps | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if patch.Locale != nil { | ||||||
|  | 		u.Locale = *patch.Locale | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // ToJson convert a User to a json string | // ToJson convert a User to a json string | ||||||
| func (u *User) ToJson() string { | func (u *User) ToJson() string { | ||||||
| 	b, err := json.Marshal(u) | 	b, err := json.Marshal(u) | ||||||
| @@ -215,6 +281,15 @@ func (u *User) ToJson() string { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (u *UserPatch) ToJson() string { | ||||||
|  | 	b, err := json.Marshal(u) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} else { | ||||||
|  | 		return string(b) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Generate a valid strong etag so the browser can cache the results | // Generate a valid strong etag so the browser can cache the results | ||||||
| func (u *User) Etag(showFullName, showEmail bool) string { | func (u *User) Etag(showFullName, showEmail bool) string { | ||||||
| 	return Etag(u.Id, u.UpdateAt, showFullName, showEmail) | 	return Etag(u.Id, u.UpdateAt, showFullName, showEmail) | ||||||
| @@ -376,6 +451,13 @@ func IsInRole(userRoles string, inRole string) bool { | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (u *User) IsSSOUser() bool { | ||||||
|  | 	if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
| func (u *User) IsOAuthUser() bool { | func (u *User) IsOAuthUser() bool { | ||||||
| 	if u.AuthService == USER_AUTH_SERVICE_GITLAB { | 	if u.AuthService == USER_AUTH_SERVICE_GITLAB { | ||||||
| 		return true | 		return true | ||||||
| @@ -402,6 +484,17 @@ func UserFromJson(data io.Reader) *User { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func UserPatchFromJson(data io.Reader) *UserPatch { | ||||||
|  | 	decoder := json.NewDecoder(data) | ||||||
|  | 	var user UserPatch | ||||||
|  | 	err := decoder.Decode(&user) | ||||||
|  | 	if err == nil { | ||||||
|  | 		return &user | ||||||
|  | 	} else { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func UserMapToJson(u map[string]*User) string { | func UserMapToJson(u map[string]*User) string { | ||||||
| 	b, err := json.Marshal(u) | 	b, err := json.Marshal(u) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -472,7 +565,7 @@ var restrictedUsernames = []string{ | |||||||
| } | } | ||||||
|  |  | ||||||
| func IsValidUsername(s string) bool { | func IsValidUsername(s string) bool { | ||||||
| 	if len(s) == 0 || len(s) > 64 { | 	if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -480,6 +573,10 @@ func IsValidUsername(s string) bool { | |||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if !unicode.IsLetter(rune(s[0])) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for _, restrictedUsername := range restrictedUsernames { | 	for _, restrictedUsername := range restrictedUsernames { | ||||||
| 		if s == restrictedUsername { | 		if s == restrictedUsername { | ||||||
| 			return false | 			return false | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -34,14 +34,14 @@ type StringArray []string | |||||||
| type EncryptStringMap map[string]string | type EncryptStringMap map[string]string | ||||||
|  |  | ||||||
| type AppError struct { | type AppError struct { | ||||||
| 	Id            string                 `json:"id"` | 	Id            string `json:"id"` | ||||||
| 	Message       string                 `json:"message"`               // Message to be display to the end user without debugging information | 	Message       string `json:"message"`               // Message to be display to the end user without debugging information | ||||||
| 	DetailedError string                 `json:"detailed_error"`        // Internal error string to help the developer | 	DetailedError string `json:"detailed_error"`        // Internal error string to help the developer | ||||||
| 	RequestId     string                 `json:"request_id,omitempty"`  // The RequestId that's also set in the header | 	RequestId     string `json:"request_id,omitempty"`  // The RequestId that's also set in the header | ||||||
| 	StatusCode    int                    `json:"status_code,omitempty"` // The http status code | 	StatusCode    int    `json:"status_code,omitempty"` // The http status code | ||||||
| 	Where         string                 `json:"-"`                     // The function where it happened in the form of Struct.Func | 	Where         string `json:"-"`                     // The function where it happened in the form of Struct.Func | ||||||
| 	IsOAuth       bool                   `json:"is_oauth,omitempty"`    // Whether the error is OAuth specific | 	IsOAuth       bool   `json:"is_oauth,omitempty"`    // Whether the error is OAuth specific | ||||||
| 	params        map[string]interface{} `json:"-"` | 	params        map[string]interface{} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (er *AppError) Error() string { | func (er *AppError) Error() string { | ||||||
| @@ -93,6 +93,18 @@ func AppErrorFromJson(data io.Reader) *AppError { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError { | ||||||
|  | 	ap := &AppError{} | ||||||
|  | 	ap.Id = id | ||||||
|  | 	ap.params = params | ||||||
|  | 	ap.Message = id | ||||||
|  | 	ap.Where = where | ||||||
|  | 	ap.DetailedError = details | ||||||
|  | 	ap.StatusCode = status | ||||||
|  | 	ap.IsOAuth = false | ||||||
|  | 	return ap | ||||||
|  | } | ||||||
|  |  | ||||||
| func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError { | func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError { | ||||||
| 	ap := &AppError{} | 	ap := &AppError{} | ||||||
| 	ap.Id = id | 	ap.Id = id | ||||||
| @@ -268,7 +280,7 @@ func IsValidChannelIdentifier(s string) bool { | |||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(s) < 2 { | 	if len(s) < CHANNEL_NAME_MIN_LENGTH { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -370,7 +382,7 @@ func ClearMentionTags(post string) string { | |||||||
| var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`) | var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`) | ||||||
| var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`) | var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`) | ||||||
|  |  | ||||||
| var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true} | var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true, '^': true, '#': true, '$': true, '&': true} | ||||||
|  |  | ||||||
| func IsValidHttpUrl(rawUrl string) bool { | func IsValidHttpUrl(rawUrl string) bool { | ||||||
| 	if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { | 	if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								vendor/github.com/mattermost/platform/model/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/mattermost/platform/model/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,6 +13,7 @@ import ( | |||||||
| // It should be maitained in chronological order with most current | // It should be maitained in chronological order with most current | ||||||
| // release at the front of the list. | // release at the front of the list. | ||||||
| var versions = []string{ | var versions = []string{ | ||||||
|  | 	"3.7.0", | ||||||
| 	"3.6.0", | 	"3.6.0", | ||||||
| 	"3.5.0", | 	"3.5.0", | ||||||
| 	"3.4.0", | 	"3.4.0", | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								vendor/github.com/mattermost/platform/model/websocket_client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/mattermost/platform/model/websocket_client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,6 +8,10 @@ import ( | |||||||
| 	"github.com/gorilla/websocket" | 	"github.com/gorilla/websocket" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB | ||||||
|  | ) | ||||||
|  |  | ||||||
| type WebSocketClient struct { | type WebSocketClient struct { | ||||||
| 	Url             string          // The location of the server like "ws://localhost:8065" | 	Url             string          // The location of the server like "ws://localhost:8065" | ||||||
| 	ApiUrl          string          // The api location of the server like "ws://localhost:8065/api/v3" | 	ApiUrl          string          // The api location of the server like "ws://localhost:8065/api/v3" | ||||||
| @@ -22,14 +26,14 @@ type WebSocketClient struct { | |||||||
| // NewWebSocketClient constructs a new WebSocket client with convienence | // NewWebSocketClient constructs a new WebSocket client with convienence | ||||||
| // methods for talking to the server. | // methods for talking to the server. | ||||||
| func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { | func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { | ||||||
| 	conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil) | 	conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) | 		return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	client := &WebSocketClient{ | 	client := &WebSocketClient{ | ||||||
| 		url, | 		url, | ||||||
| 		url + API_URL_SUFFIX, | 		url + API_URL_SUFFIX_V3, | ||||||
| 		conn, | 		conn, | ||||||
| 		authToken, | 		authToken, | ||||||
| 		1, | 		1, | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								vendor/github.com/mattermost/platform/model/websocket_message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/mattermost/platform/model/websocket_message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -14,8 +14,9 @@ const ( | |||||||
| 	WEBSOCKET_EVENT_POST_EDITED        = "post_edited" | 	WEBSOCKET_EVENT_POST_EDITED        = "post_edited" | ||||||
| 	WEBSOCKET_EVENT_POST_DELETED       = "post_deleted" | 	WEBSOCKET_EVENT_POST_DELETED       = "post_deleted" | ||||||
| 	WEBSOCKET_EVENT_CHANNEL_DELETED    = "channel_deleted" | 	WEBSOCKET_EVENT_CHANNEL_DELETED    = "channel_deleted" | ||||||
| 	WEBSOCKET_EVENT_CHANNEL_VIEWED     = "channel_viewed" | 	WEBSOCKET_EVENT_CHANNEL_CREATED    = "channel_created" | ||||||
| 	WEBSOCKET_EVENT_DIRECT_ADDED       = "direct_added" | 	WEBSOCKET_EVENT_DIRECT_ADDED       = "direct_added" | ||||||
|  | 	WEBSOCKET_EVENT_GROUP_ADDED        = "group_added" | ||||||
| 	WEBSOCKET_EVENT_NEW_USER           = "new_user" | 	WEBSOCKET_EVENT_NEW_USER           = "new_user" | ||||||
| 	WEBSOCKET_EVENT_LEAVE_TEAM         = "leave_team" | 	WEBSOCKET_EVENT_LEAVE_TEAM         = "leave_team" | ||||||
| 	WEBSOCKET_EVENT_UPDATE_TEAM        = "update_team" | 	WEBSOCKET_EVENT_UPDATE_TEAM        = "update_team" | ||||||
|   | |||||||
							
								
								
									
										897
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										897
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,897 @@ | |||||||
|  | Mattermost Licensing | ||||||
|  |  | ||||||
|  | SOFTWARE LICENSING  | ||||||
|  |  | ||||||
|  | You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE  | ||||||
|  |  | ||||||
|  | -	See MIT-COMPILED-LICENSE.md included in compiled versions for details | ||||||
|  |  | ||||||
|  | You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways: | ||||||
|  |  | ||||||
|  | 1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or  | ||||||
|  | 2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com  | ||||||
|  |  | ||||||
|  | You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,  | ||||||
|  | webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0. | ||||||
|  |  | ||||||
|  | We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not  | ||||||
|  | link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and | ||||||
|  | (b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of  | ||||||
|  | a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license. | ||||||
|  |  | ||||||
|  | MATTERMOST TRADEMARK GUIDELINES | ||||||
|  |  | ||||||
|  | Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark  | ||||||
|  | Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions  | ||||||
|  | you have about using these trademarks, please email trademark@mattermost.com  | ||||||
|  |  | ||||||
|  | ------------------------------------------------------------------------------------------------------------------------------ | ||||||
|  |                                 | ||||||
|  |                                Apache License | ||||||
|  |                            Version 2.0, January 2004 | ||||||
|  |                         http://www.apache.org/licenses/ | ||||||
|  |  | ||||||
|  |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  |  | ||||||
|  |    1. Definitions. | ||||||
|  |  | ||||||
|  |       "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |       and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  |  | ||||||
|  |       "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |       the copyright owner that is granting the License. | ||||||
|  |  | ||||||
|  |       "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |       other entities that control, are controlled by, or are under common | ||||||
|  |       control with that entity. For the purposes of this definition, | ||||||
|  |       "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |       direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |       outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  |  | ||||||
|  |       "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |       exercising permissions granted by this License. | ||||||
|  |  | ||||||
|  |       "Source" form shall mean the preferred form for making modifications, | ||||||
|  |       including but not limited to software source code, documentation | ||||||
|  |       source, and configuration files. | ||||||
|  |  | ||||||
|  |       "Object" form shall mean any form resulting from mechanical | ||||||
|  |       transformation or translation of a Source form, including but | ||||||
|  |       not limited to compiled object code, generated documentation, | ||||||
|  |       and conversions to other media types. | ||||||
|  |  | ||||||
|  |       "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |       Object form, made available under the License, as indicated by a | ||||||
|  |       copyright notice that is included in or attached to the work | ||||||
|  |       (an example is provided in the Appendix below). | ||||||
|  |  | ||||||
|  |       "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |       form, that is based on (or derived from) the Work and for which the | ||||||
|  |       editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |       represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |       of this License, Derivative Works shall not include works that remain | ||||||
|  |       separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |       the Work and Derivative Works thereof. | ||||||
|  |  | ||||||
|  |       "Contribution" shall mean any work of authorship, including | ||||||
|  |       the original version of the Work and any modifications or additions | ||||||
|  |       to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |       submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |       or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |       the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |       means any form of electronic, verbal, or written communication sent | ||||||
|  |       to the Licensor or its representatives, including but not limited to | ||||||
|  |       communication on electronic mailing lists, source code control systems, | ||||||
|  |       and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |       Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |       excluding communication that is conspicuously marked or otherwise | ||||||
|  |       designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  |  | ||||||
|  |       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |       on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |       subsequently incorporated within the Work. | ||||||
|  |  | ||||||
|  |    2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |       publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |       Work and such Derivative Works in Source or Object form. | ||||||
|  |  | ||||||
|  |    3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       (except as stated in this section) patent license to make, have made, | ||||||
|  |       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |       where such license applies only to those patent claims licensable | ||||||
|  |       by such Contributor that are necessarily infringed by their | ||||||
|  |       Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |       with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |       institute patent litigation against any entity (including a | ||||||
|  |       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |       or a Contribution incorporated within the Work constitutes direct | ||||||
|  |       or contributory patent infringement, then any patent licenses | ||||||
|  |       granted to You under this License for that Work shall terminate | ||||||
|  |       as of the date such litigation is filed. | ||||||
|  |  | ||||||
|  |    4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |       Work or Derivative Works thereof in any medium, with or without | ||||||
|  |       modifications, and in Source or Object form, provided that You | ||||||
|  |       meet the following conditions: | ||||||
|  |  | ||||||
|  |       (a) You must give any other recipients of the Work or | ||||||
|  |           Derivative Works a copy of this License; and | ||||||
|  |  | ||||||
|  |       (b) You must cause any modified files to carry prominent notices | ||||||
|  |           stating that You changed the files; and | ||||||
|  |  | ||||||
|  |       (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |           that You distribute, all copyright, patent, trademark, and | ||||||
|  |           attribution notices from the Source form of the Work, | ||||||
|  |           excluding those notices that do not pertain to any part of | ||||||
|  |           the Derivative Works; and | ||||||
|  |  | ||||||
|  |       (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |           distribution, then any Derivative Works that You distribute must | ||||||
|  |           include a readable copy of the attribution notices contained | ||||||
|  |           within such NOTICE file, excluding those notices that do not | ||||||
|  |           pertain to any part of the Derivative Works, in at least one | ||||||
|  |           of the following places: within a NOTICE text file distributed | ||||||
|  |           as part of the Derivative Works; within the Source form or | ||||||
|  |           documentation, if provided along with the Derivative Works; or, | ||||||
|  |           within a display generated by the Derivative Works, if and | ||||||
|  |           wherever such third-party notices normally appear. The contents | ||||||
|  |           of the NOTICE file are for informational purposes only and | ||||||
|  |           do not modify the License. You may add Your own attribution | ||||||
|  |           notices within Derivative Works that You distribute, alongside | ||||||
|  |           or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |           that such additional attribution notices cannot be construed | ||||||
|  |           as modifying the License. | ||||||
|  |  | ||||||
|  |       You may add Your own copyright statement to Your modifications and | ||||||
|  |       may provide additional or different license terms and conditions | ||||||
|  |       for use, reproduction, or distribution of Your modifications, or | ||||||
|  |       for any such Derivative Works as a whole, provided Your use, | ||||||
|  |       reproduction, and distribution of the Work otherwise complies with | ||||||
|  |       the conditions stated in this License. | ||||||
|  |  | ||||||
|  |    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |       any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |       by You to the Licensor shall be under the terms and conditions of | ||||||
|  |       this License, without any additional terms or conditions. | ||||||
|  |       Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |       the terms of any separate license agreement you may have executed | ||||||
|  |       with Licensor regarding such Contributions. | ||||||
|  |  | ||||||
|  |    6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |       names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |       except as required for reasonable and customary use in describing the | ||||||
|  |       origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  |  | ||||||
|  |    7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |       agreed to in writing, Licensor provides the Work (and each | ||||||
|  |       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |       implied, including, without limitation, any warranties or conditions | ||||||
|  |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |       appropriateness of using or redistributing the Work and assume any | ||||||
|  |       risks associated with Your exercise of permissions under this License. | ||||||
|  |  | ||||||
|  |    8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |       whether in tort (including negligence), contract, or otherwise, | ||||||
|  |       unless required by applicable law (such as deliberate and grossly | ||||||
|  |       negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |       liable to You for damages, including any direct, indirect, special, | ||||||
|  |       incidental, or consequential damages of any character arising as a | ||||||
|  |       result of this License or out of the use or inability to use the | ||||||
|  |       Work (including but not limited to damages for loss of goodwill, | ||||||
|  |       work stoppage, computer failure or malfunction, or any and all | ||||||
|  |       other commercial damages or losses), even if such Contributor | ||||||
|  |       has been advised of the possibility of such damages. | ||||||
|  |  | ||||||
|  |    9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |       the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |       and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |       or other liability obligations and/or rights consistent with this | ||||||
|  |       License. However, in accepting such obligations, You may act only | ||||||
|  |       on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |       of any other Contributor, and only if You agree to indemnify, | ||||||
|  |       defend, and hold each Contributor harmless for any liability | ||||||
|  |       incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |       of your accepting any such warranty or additional liability. | ||||||
|  |  | ||||||
|  |    END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |    APPENDIX: How to apply the Apache License to your work. | ||||||
|  |  | ||||||
|  |       To apply the Apache License to your work, attach the following | ||||||
|  |       boilerplate notice, with the fields enclosed by brackets "[]" | ||||||
|  |       replaced with your own identifying information. (Don't include | ||||||
|  |       the brackets!)  The text should be enclosed in the appropriate | ||||||
|  |       comment syntax for the file format. We also recommend that a | ||||||
|  |       file or class name and description of purpose be included on the | ||||||
|  |       same "printed page" as the copyright notice for easier | ||||||
|  |       identification within third-party archives. | ||||||
|  |  | ||||||
|  |    Copyright [yyyy] [name of copyright owner] | ||||||
|  |  | ||||||
|  |    Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |    you may not use this file except in compliance with the License. | ||||||
|  |    You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |        http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  |    Unless required by applicable law or agreed to in writing, software | ||||||
|  |    distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |    See the License for the specific language governing permissions and | ||||||
|  |    limitations under the License. | ||||||
|  |  | ||||||
|  | ------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | The software is released under the terms of the GNU Affero General Public | ||||||
|  | License, version 3. | ||||||
|  |  | ||||||
|  |                     GNU AFFERO GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 19 November 2007 | ||||||
|  |  | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
|  |                             Preamble | ||||||
|  |  | ||||||
|  |   The GNU Affero General Public License is a free, copyleft license for | ||||||
|  | software and other kinds of works, specifically designed to ensure | ||||||
|  | cooperation with the community in the case of network server software. | ||||||
|  |  | ||||||
|  |   The licenses for most software and other practical works are designed | ||||||
|  | to take away your freedom to share and change the works.  By contrast, | ||||||
|  | our General Public Licenses are intended to guarantee your freedom to | ||||||
|  | share and change all versions of a program--to make sure it remains free | ||||||
|  | software for all its users. | ||||||
|  |  | ||||||
|  |   When we speak of free software, we are referring to freedom, not | ||||||
|  | price.  Our General Public Licenses are designed to make sure that you | ||||||
|  | have the freedom to distribute copies of free software (and charge for | ||||||
|  | them if you wish), that you receive source code or can get it if you | ||||||
|  | want it, that you can change the software or use pieces of it in new | ||||||
|  | free programs, and that you know you can do these things. | ||||||
|  |  | ||||||
|  |   Developers that use our General Public Licenses protect your rights | ||||||
|  | with two steps: (1) assert copyright on the software, and (2) offer | ||||||
|  | you this License which gives you legal permission to copy, distribute | ||||||
|  | and/or modify the software. | ||||||
|  |  | ||||||
|  |   A secondary benefit of defending all users' freedom is that | ||||||
|  | improvements made in alternate versions of the program, if they | ||||||
|  | receive widespread use, become available for other developers to | ||||||
|  | incorporate.  Many developers of free software are heartened and | ||||||
|  | encouraged by the resulting cooperation.  However, in the case of | ||||||
|  | software used on network servers, this result may fail to come about. | ||||||
|  | The GNU General Public License permits making a modified version and | ||||||
|  | letting the public access it on a server without ever releasing its | ||||||
|  | source code to the public. | ||||||
|  |  | ||||||
|  |   The GNU Affero General Public License is designed specifically to | ||||||
|  | ensure that, in such cases, the modified source code becomes available | ||||||
|  | to the community.  It requires the operator of a network server to | ||||||
|  | provide the source code of the modified version running there to the | ||||||
|  | users of that server.  Therefore, public use of a modified version, on | ||||||
|  | a publicly accessible server, gives the public access to the source | ||||||
|  | code of the modified version. | ||||||
|  |  | ||||||
|  |   An older license, called the Affero General Public License and | ||||||
|  | published by Affero, was designed to accomplish similar goals.  This is | ||||||
|  | a different license, not a version of the Affero GPL, but Affero has | ||||||
|  | released a new version of the Affero GPL which permits relicensing under | ||||||
|  | this license. | ||||||
|  |  | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow. | ||||||
|  |  | ||||||
|  |                        TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |   0. Definitions. | ||||||
|  |  | ||||||
|  |   "This License" refers to version 3 of the GNU Affero General Public License. | ||||||
|  |  | ||||||
|  |   "Copyright" also means copyright-like laws that apply to other kinds of | ||||||
|  | works, such as semiconductor masks. | ||||||
|  |  | ||||||
|  |   "The Program" refers to any copyrightable work licensed under this | ||||||
|  | License.  Each licensee is addressed as "you".  "Licensees" and | ||||||
|  | "recipients" may be individuals or organizations. | ||||||
|  |  | ||||||
|  |   To "modify" a work means to copy from or adapt all or part of the work | ||||||
|  | in a fashion requiring copyright permission, other than the making of an | ||||||
|  | exact copy.  The resulting work is called a "modified version" of the | ||||||
|  | earlier work or a work "based on" the earlier work. | ||||||
|  |  | ||||||
|  |   A "covered work" means either the unmodified Program or a work based | ||||||
|  | on the Program. | ||||||
|  |  | ||||||
|  |   To "propagate" a work means to do anything with it that, without | ||||||
|  | permission, would make you directly or secondarily liable for | ||||||
|  | infringement under applicable copyright law, except executing it on a | ||||||
|  | computer or modifying a private copy.  Propagation includes copying, | ||||||
|  | distribution (with or without modification), making available to the | ||||||
|  | public, and in some countries other activities as well. | ||||||
|  |  | ||||||
|  |   To "convey" a work means any kind of propagation that enables other | ||||||
|  | parties to make or receive copies.  Mere interaction with a user through | ||||||
|  | a computer network, with no transfer of a copy, is not conveying. | ||||||
|  |  | ||||||
|  |   An interactive user interface displays "Appropriate Legal Notices" | ||||||
|  | to the extent that it includes a convenient and prominently visible | ||||||
|  | feature that (1) displays an appropriate copyright notice, and (2) | ||||||
|  | tells the user that there is no warranty for the work (except to the | ||||||
|  | extent that warranties are provided), that licensees may convey the | ||||||
|  | work under this License, and how to view a copy of this License.  If | ||||||
|  | the interface presents a list of user commands or options, such as a | ||||||
|  | menu, a prominent item in the list meets this criterion. | ||||||
|  |  | ||||||
|  |   1. Source Code. | ||||||
|  |  | ||||||
|  |   The "source code" for a work means the preferred form of the work | ||||||
|  | for making modifications to it.  "Object code" means any non-source | ||||||
|  | form of a work. | ||||||
|  |  | ||||||
|  |   A "Standard Interface" means an interface that either is an official | ||||||
|  | standard defined by a recognized standards body, or, in the case of | ||||||
|  | interfaces specified for a particular programming language, one that | ||||||
|  | is widely used among developers working in that language. | ||||||
|  |  | ||||||
|  |   The "System Libraries" of an executable work include anything, other | ||||||
|  | than the work as a whole, that (a) is included in the normal form of | ||||||
|  | packaging a Major Component, but which is not part of that Major | ||||||
|  | Component, and (b) serves only to enable use of the work with that | ||||||
|  | Major Component, or to implement a Standard Interface for which an | ||||||
|  | implementation is available to the public in source code form.  A | ||||||
|  | "Major Component", in this context, means a major essential component | ||||||
|  | (kernel, window system, and so on) of the specific operating system | ||||||
|  | (if any) on which the executable work runs, or a compiler used to | ||||||
|  | produce the work, or an object code interpreter used to run it. | ||||||
|  |  | ||||||
|  |   The "Corresponding Source" for a work in object code form means all | ||||||
|  | the source code needed to generate, install, and (for an executable | ||||||
|  | work) run the object code and to modify the work, including scripts to | ||||||
|  | control those activities.  However, it does not include the work's | ||||||
|  | System Libraries, or general-purpose tools or generally available free | ||||||
|  | programs which are used unmodified in performing those activities but | ||||||
|  | which are not part of the work.  For example, Corresponding Source | ||||||
|  | includes interface definition files associated with source files for | ||||||
|  | the work, and the source code for shared libraries and dynamically | ||||||
|  | linked subprograms that the work is specifically designed to require, | ||||||
|  | such as by intimate data communication or control flow between those | ||||||
|  | subprograms and other parts of the work. | ||||||
|  |  | ||||||
|  |   The Corresponding Source need not include anything that users | ||||||
|  | can regenerate automatically from other parts of the Corresponding | ||||||
|  | Source. | ||||||
|  |  | ||||||
|  |   The Corresponding Source for a work in source code form is that | ||||||
|  | same work. | ||||||
|  |  | ||||||
|  |   2. Basic Permissions. | ||||||
|  |  | ||||||
|  |   All rights granted under this License are granted for the term of | ||||||
|  | copyright on the Program, and are irrevocable provided the stated | ||||||
|  | conditions are met.  This License explicitly affirms your unlimited | ||||||
|  | permission to run the unmodified Program.  The output from running a | ||||||
|  | covered work is covered by this License only if the output, given its | ||||||
|  | content, constitutes a covered work.  This License acknowledges your | ||||||
|  | rights of fair use or other equivalent, as provided by copyright law. | ||||||
|  |  | ||||||
|  |   You may make, run and propagate covered works that you do not | ||||||
|  | convey, without conditions so long as your license otherwise remains | ||||||
|  | in force.  You may convey covered works to others for the sole purpose | ||||||
|  | of having them make modifications exclusively for you, or provide you | ||||||
|  | with facilities for running those works, provided that you comply with | ||||||
|  | the terms of this License in conveying all material for which you do | ||||||
|  | not control copyright.  Those thus making or running the covered works | ||||||
|  | for you must do so exclusively on your behalf, under your direction | ||||||
|  | and control, on terms that prohibit them from making any copies of | ||||||
|  | your copyrighted material outside their relationship with you. | ||||||
|  |  | ||||||
|  |   Conveying under any other circumstances is permitted solely under | ||||||
|  | the conditions stated below.  Sublicensing is not allowed; section 10 | ||||||
|  | makes it unnecessary. | ||||||
|  |  | ||||||
|  |   3. Protecting Users' Legal Rights From Anti-Circumvention Law. | ||||||
|  |  | ||||||
|  |   No covered work shall be deemed part of an effective technological | ||||||
|  | measure under any applicable law fulfilling obligations under article | ||||||
|  | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | ||||||
|  | similar laws prohibiting or restricting circumvention of such | ||||||
|  | measures. | ||||||
|  |  | ||||||
|  |   When you convey a covered work, you waive any legal power to forbid | ||||||
|  | circumvention of technological measures to the extent such circumvention | ||||||
|  | is effected by exercising rights under this License with respect to | ||||||
|  | the covered work, and you disclaim any intention to limit operation or | ||||||
|  | modification of the work as a means of enforcing, against the work's | ||||||
|  | users, your or third parties' legal rights to forbid circumvention of | ||||||
|  | technological measures. | ||||||
|  |  | ||||||
|  |   4. Conveying Verbatim Copies. | ||||||
|  |  | ||||||
|  |   You may convey verbatim copies of the Program's source code as you | ||||||
|  | receive it, in any medium, provided that you conspicuously and | ||||||
|  | appropriately publish on each copy an appropriate copyright notice; | ||||||
|  | keep intact all notices stating that this License and any | ||||||
|  | non-permissive terms added in accord with section 7 apply to the code; | ||||||
|  | keep intact all notices of the absence of any warranty; and give all | ||||||
|  | recipients a copy of this License along with the Program. | ||||||
|  |  | ||||||
|  |   You may charge any price or no price for each copy that you convey, | ||||||
|  | and you may offer support or warranty protection for a fee. | ||||||
|  |  | ||||||
|  |   5. Conveying Modified Source Versions. | ||||||
|  |  | ||||||
|  |   You may convey a work based on the Program, or the modifications to | ||||||
|  | produce it from the Program, in the form of source code under the | ||||||
|  | terms of section 4, provided that you also meet all of these conditions: | ||||||
|  |  | ||||||
|  |     a) The work must carry prominent notices stating that you modified | ||||||
|  |     it, and giving a relevant date. | ||||||
|  |  | ||||||
|  |     b) The work must carry prominent notices stating that it is | ||||||
|  |     released under this License and any conditions added under section | ||||||
|  |     7.  This requirement modifies the requirement in section 4 to | ||||||
|  |     "keep intact all notices". | ||||||
|  |  | ||||||
|  |     c) You must license the entire work, as a whole, under this | ||||||
|  |     License to anyone who comes into possession of a copy.  This | ||||||
|  |     License will therefore apply, along with any applicable section 7 | ||||||
|  |     additional terms, to the whole of the work, and all its parts, | ||||||
|  |     regardless of how they are packaged.  This License gives no | ||||||
|  |     permission to license the work in any other way, but it does not | ||||||
|  |     invalidate such permission if you have separately received it. | ||||||
|  |  | ||||||
|  |     d) If the work has interactive user interfaces, each must display | ||||||
|  |     Appropriate Legal Notices; however, if the Program has interactive | ||||||
|  |     interfaces that do not display Appropriate Legal Notices, your | ||||||
|  |     work need not make them do so. | ||||||
|  |  | ||||||
|  |   A compilation of a covered work with other separate and independent | ||||||
|  | works, which are not by their nature extensions of the covered work, | ||||||
|  | and which are not combined with it such as to form a larger program, | ||||||
|  | in or on a volume of a storage or distribution medium, is called an | ||||||
|  | "aggregate" if the compilation and its resulting copyright are not | ||||||
|  | used to limit the access or legal rights of the compilation's users | ||||||
|  | beyond what the individual works permit.  Inclusion of a covered work | ||||||
|  | in an aggregate does not cause this License to apply to the other | ||||||
|  | parts of the aggregate. | ||||||
|  |  | ||||||
|  |   6. Conveying Non-Source Forms. | ||||||
|  |  | ||||||
|  |   You may convey a covered work in object code form under the terms | ||||||
|  | of sections 4 and 5, provided that you also convey the | ||||||
|  | machine-readable Corresponding Source under the terms of this License, | ||||||
|  | in one of these ways: | ||||||
|  |  | ||||||
|  |     a) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by the | ||||||
|  |     Corresponding Source fixed on a durable physical medium | ||||||
|  |     customarily used for software interchange. | ||||||
|  |  | ||||||
|  |     b) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by a | ||||||
|  |     written offer, valid for at least three years and valid for as | ||||||
|  |     long as you offer spare parts or customer support for that product | ||||||
|  |     model, to give anyone who possesses the object code either (1) a | ||||||
|  |     copy of the Corresponding Source for all the software in the | ||||||
|  |     product that is covered by this License, on a durable physical | ||||||
|  |     medium customarily used for software interchange, for a price no | ||||||
|  |     more than your reasonable cost of physically performing this | ||||||
|  |     conveying of source, or (2) access to copy the | ||||||
|  |     Corresponding Source from a network server at no charge. | ||||||
|  |  | ||||||
|  |     c) Convey individual copies of the object code with a copy of the | ||||||
|  |     written offer to provide the Corresponding Source.  This | ||||||
|  |     alternative is allowed only occasionally and noncommercially, and | ||||||
|  |     only if you received the object code with such an offer, in accord | ||||||
|  |     with subsection 6b. | ||||||
|  |  | ||||||
|  |     d) Convey the object code by offering access from a designated | ||||||
|  |     place (gratis or for a charge), and offer equivalent access to the | ||||||
|  |     Corresponding Source in the same way through the same place at no | ||||||
|  |     further charge.  You need not require recipients to copy the | ||||||
|  |     Corresponding Source along with the object code.  If the place to | ||||||
|  |     copy the object code is a network server, the Corresponding Source | ||||||
|  |     may be on a different server (operated by you or a third party) | ||||||
|  |     that supports equivalent copying facilities, provided you maintain | ||||||
|  |     clear directions next to the object code saying where to find the | ||||||
|  |     Corresponding Source.  Regardless of what server hosts the | ||||||
|  |     Corresponding Source, you remain obligated to ensure that it is | ||||||
|  |     available for as long as needed to satisfy these requirements. | ||||||
|  |  | ||||||
|  |     e) Convey the object code using peer-to-peer transmission, provided | ||||||
|  |     you inform other peers where the object code and Corresponding | ||||||
|  |     Source of the work are being offered to the general public at no | ||||||
|  |     charge under subsection 6d. | ||||||
|  |  | ||||||
|  |   A separable portion of the object code, whose source code is excluded | ||||||
|  | from the Corresponding Source as a System Library, need not be | ||||||
|  | included in conveying the object code work. | ||||||
|  |  | ||||||
|  |   A "User Product" is either (1) a "consumer product", which means any | ||||||
|  | tangible personal property which is normally used for personal, family, | ||||||
|  | or household purposes, or (2) anything designed or sold for incorporation | ||||||
|  | into a dwelling.  In determining whether a product is a consumer product, | ||||||
|  | doubtful cases shall be resolved in favor of coverage.  For a particular | ||||||
|  | product received by a particular user, "normally used" refers to a | ||||||
|  | typical or common use of that class of product, regardless of the status | ||||||
|  | of the particular user or of the way in which the particular user | ||||||
|  | actually uses, or expects or is expected to use, the product.  A product | ||||||
|  | is a consumer product regardless of whether the product has substantial | ||||||
|  | commercial, industrial or non-consumer uses, unless such uses represent | ||||||
|  | the only significant mode of use of the product. | ||||||
|  |  | ||||||
|  |   "Installation Information" for a User Product means any methods, | ||||||
|  | procedures, authorization keys, or other information required to install | ||||||
|  | and execute modified versions of a covered work in that User Product from | ||||||
|  | a modified version of its Corresponding Source.  The information must | ||||||
|  | suffice to ensure that the continued functioning of the modified object | ||||||
|  | code is in no case prevented or interfered with solely because | ||||||
|  | modification has been made. | ||||||
|  |  | ||||||
|  |   If you convey an object code work under this section in, or with, or | ||||||
|  | specifically for use in, a User Product, and the conveying occurs as | ||||||
|  | part of a transaction in which the right of possession and use of the | ||||||
|  | User Product is transferred to the recipient in perpetuity or for a | ||||||
|  | fixed term (regardless of how the transaction is characterized), the | ||||||
|  | Corresponding Source conveyed under this section must be accompanied | ||||||
|  | by the Installation Information.  But this requirement does not apply | ||||||
|  | if neither you nor any third party retains the ability to install | ||||||
|  | modified object code on the User Product (for example, the work has | ||||||
|  | been installed in ROM). | ||||||
|  |  | ||||||
|  |   The requirement to provide Installation Information does not include a | ||||||
|  | requirement to continue to provide support service, warranty, or updates | ||||||
|  | for a work that has been modified or installed by the recipient, or for | ||||||
|  | the User Product in which it has been modified or installed.  Access to a | ||||||
|  | network may be denied when the modification itself materially and | ||||||
|  | adversely affects the operation of the network or violates the rules and | ||||||
|  | protocols for communication across the network. | ||||||
|  |  | ||||||
|  |   Corresponding Source conveyed, and Installation Information provided, | ||||||
|  | in accord with this section must be in a format that is publicly | ||||||
|  | documented (and with an implementation available to the public in | ||||||
|  | source code form), and must require no special password or key for | ||||||
|  | unpacking, reading or copying. | ||||||
|  |  | ||||||
|  |   7. Additional Terms. | ||||||
|  |  | ||||||
|  |   "Additional permissions" are terms that supplement the terms of this | ||||||
|  | License by making exceptions from one or more of its conditions. | ||||||
|  | Additional permissions that are applicable to the entire Program shall | ||||||
|  | be treated as though they were included in this License, to the extent | ||||||
|  | that they are valid under applicable law.  If additional permissions | ||||||
|  | apply only to part of the Program, that part may be used separately | ||||||
|  | under those permissions, but the entire Program remains governed by | ||||||
|  | this License without regard to the additional permissions. | ||||||
|  |  | ||||||
|  |   When you convey a copy of a covered work, you may at your option | ||||||
|  | remove any additional permissions from that copy, or from any part of | ||||||
|  | it.  (Additional permissions may be written to require their own | ||||||
|  | removal in certain cases when you modify the work.)  You may place | ||||||
|  | additional permissions on material, added by you to a covered work, | ||||||
|  | for which you have or can give appropriate copyright permission. | ||||||
|  |  | ||||||
|  |   Notwithstanding any other provision of this License, for material you | ||||||
|  | add to a covered work, you may (if authorized by the copyright holders of | ||||||
|  | that material) supplement the terms of this License with terms: | ||||||
|  |  | ||||||
|  |     a) Disclaiming warranty or limiting liability differently from the | ||||||
|  |     terms of sections 15 and 16 of this License; or | ||||||
|  |  | ||||||
|  |     b) Requiring preservation of specified reasonable legal notices or | ||||||
|  |     author attributions in that material or in the Appropriate Legal | ||||||
|  |     Notices displayed by works containing it; or | ||||||
|  |  | ||||||
|  |     c) Prohibiting misrepresentation of the origin of that material, or | ||||||
|  |     requiring that modified versions of such material be marked in | ||||||
|  |     reasonable ways as different from the original version; or | ||||||
|  |  | ||||||
|  |     d) Limiting the use for publicity purposes of names of licensors or | ||||||
|  |     authors of the material; or | ||||||
|  |  | ||||||
|  |     e) Declining to grant rights under trademark law for use of some | ||||||
|  |     trade names, trademarks, or service marks; or | ||||||
|  |  | ||||||
|  |     f) Requiring indemnification of licensors and authors of that | ||||||
|  |     material by anyone who conveys the material (or modified versions of | ||||||
|  |     it) with contractual assumptions of liability to the recipient, for | ||||||
|  |     any liability that these contractual assumptions directly impose on | ||||||
|  |     those licensors and authors. | ||||||
|  |  | ||||||
|  |   All other non-permissive additional terms are considered "further | ||||||
|  | restrictions" within the meaning of section 10.  If the Program as you | ||||||
|  | received it, or any part of it, contains a notice stating that it is | ||||||
|  | governed by this License along with a term that is a further | ||||||
|  | restriction, you may remove that term.  If a license document contains | ||||||
|  | a further restriction but permits relicensing or conveying under this | ||||||
|  | License, you may add to a covered work material governed by the terms | ||||||
|  | of that license document, provided that the further restriction does | ||||||
|  | not survive such relicensing or conveying. | ||||||
|  |  | ||||||
|  |   If you add terms to a covered work in accord with this section, you | ||||||
|  | must place, in the relevant source files, a statement of the | ||||||
|  | additional terms that apply to those files, or a notice indicating | ||||||
|  | where to find the applicable terms. | ||||||
|  |  | ||||||
|  |   Additional terms, permissive or non-permissive, may be stated in the | ||||||
|  | form of a separately written license, or stated as exceptions; | ||||||
|  | the above requirements apply either way. | ||||||
|  |  | ||||||
|  |   8. Termination. | ||||||
|  |  | ||||||
|  |   You may not propagate or modify a covered work except as expressly | ||||||
|  | provided under this License.  Any attempt otherwise to propagate or | ||||||
|  | modify it is void, and will automatically terminate your rights under | ||||||
|  | this License (including any patent licenses granted under the third | ||||||
|  | paragraph of section 11). | ||||||
|  |  | ||||||
|  |   However, if you cease all violation of this License, then your | ||||||
|  | license from a particular copyright holder is reinstated (a) | ||||||
|  | provisionally, unless and until the copyright holder explicitly and | ||||||
|  | finally terminates your license, and (b) permanently, if the copyright | ||||||
|  | holder fails to notify you of the violation by some reasonable means | ||||||
|  | prior to 60 days after the cessation. | ||||||
|  |  | ||||||
|  |   Moreover, your license from a particular copyright holder is | ||||||
|  | reinstated permanently if the copyright holder notifies you of the | ||||||
|  | violation by some reasonable means, this is the first time you have | ||||||
|  | received notice of violation of this License (for any work) from that | ||||||
|  | copyright holder, and you cure the violation prior to 30 days after | ||||||
|  | your receipt of the notice. | ||||||
|  |  | ||||||
|  |   Termination of your rights under this section does not terminate the | ||||||
|  | licenses of parties who have received copies or rights from you under | ||||||
|  | this License.  If your rights have been terminated and not permanently | ||||||
|  | reinstated, you do not qualify to receive new licenses for the same | ||||||
|  | material under section 10. | ||||||
|  |  | ||||||
|  |   9. Acceptance Not Required for Having Copies. | ||||||
|  |  | ||||||
|  |   You are not required to accept this License in order to receive or | ||||||
|  | run a copy of the Program.  Ancillary propagation of a covered work | ||||||
|  | occurring solely as a consequence of using peer-to-peer transmission | ||||||
|  | to receive a copy likewise does not require acceptance.  However, | ||||||
|  | nothing other than this License grants you permission to propagate or | ||||||
|  | modify any covered work.  These actions infringe copyright if you do | ||||||
|  | not accept this License.  Therefore, by modifying or propagating a | ||||||
|  | covered work, you indicate your acceptance of this License to do so. | ||||||
|  |  | ||||||
|  |   10. Automatic Licensing of Downstream Recipients. | ||||||
|  |  | ||||||
|  |   Each time you convey a covered work, the recipient automatically | ||||||
|  | receives a license from the original licensors, to run, modify and | ||||||
|  | propagate that work, subject to this License.  You are not responsible | ||||||
|  | for enforcing compliance by third parties with this License. | ||||||
|  |  | ||||||
|  |   An "entity transaction" is a transaction transferring control of an | ||||||
|  | organization, or substantially all assets of one, or subdividing an | ||||||
|  | organization, or merging organizations.  If propagation of a covered | ||||||
|  | work results from an entity transaction, each party to that | ||||||
|  | transaction who receives a copy of the work also receives whatever | ||||||
|  | licenses to the work the party's predecessor in interest had or could | ||||||
|  | give under the previous paragraph, plus a right to possession of the | ||||||
|  | Corresponding Source of the work from the predecessor in interest, if | ||||||
|  | the predecessor has it or can get it with reasonable efforts. | ||||||
|  |  | ||||||
|  |   You may not impose any further restrictions on the exercise of the | ||||||
|  | rights granted or affirmed under this License.  For example, you may | ||||||
|  | not impose a license fee, royalty, or other charge for exercise of | ||||||
|  | rights granted under this License, and you may not initiate litigation | ||||||
|  | (including a cross-claim or counterclaim in a lawsuit) alleging that | ||||||
|  | any patent claim is infringed by making, using, selling, offering for | ||||||
|  | sale, or importing the Program or any portion of it. | ||||||
|  |  | ||||||
|  |   11. Patents. | ||||||
|  |  | ||||||
|  |   A "contributor" is a copyright holder who authorizes use under this | ||||||
|  | License of the Program or a work on which the Program is based.  The | ||||||
|  | work thus licensed is called the contributor's "contributor version". | ||||||
|  |  | ||||||
|  |   A contributor's "essential patent claims" are all patent claims | ||||||
|  | owned or controlled by the contributor, whether already acquired or | ||||||
|  | hereafter acquired, that would be infringed by some manner, permitted | ||||||
|  | by this License, of making, using, or selling its contributor version, | ||||||
|  | but do not include claims that would be infringed only as a | ||||||
|  | consequence of further modification of the contributor version.  For | ||||||
|  | purposes of this definition, "control" includes the right to grant | ||||||
|  | patent sublicenses in a manner consistent with the requirements of | ||||||
|  | this License. | ||||||
|  |  | ||||||
|  |   Each contributor grants you a non-exclusive, worldwide, royalty-free | ||||||
|  | patent license under the contributor's essential patent claims, to | ||||||
|  | make, use, sell, offer for sale, import and otherwise run, modify and | ||||||
|  | propagate the contents of its contributor version. | ||||||
|  |  | ||||||
|  |   In the following three paragraphs, a "patent license" is any express | ||||||
|  | agreement or commitment, however denominated, not to enforce a patent | ||||||
|  | (such as an express permission to practice a patent or covenant not to | ||||||
|  | sue for patent infringement).  To "grant" such a patent license to a | ||||||
|  | party means to make such an agreement or commitment not to enforce a | ||||||
|  | patent against the party. | ||||||
|  |  | ||||||
|  |   If you convey a covered work, knowingly relying on a patent license, | ||||||
|  | and the Corresponding Source of the work is not available for anyone | ||||||
|  | to copy, free of charge and under the terms of this License, through a | ||||||
|  | publicly available network server or other readily accessible means, | ||||||
|  | then you must either (1) cause the Corresponding Source to be so | ||||||
|  | available, or (2) arrange to deprive yourself of the benefit of the | ||||||
|  | patent license for this particular work, or (3) arrange, in a manner | ||||||
|  | consistent with the requirements of this License, to extend the patent | ||||||
|  | license to downstream recipients.  "Knowingly relying" means you have | ||||||
|  | actual knowledge that, but for the patent license, your conveying the | ||||||
|  | covered work in a country, or your recipient's use of the covered work | ||||||
|  | in a country, would infringe one or more identifiable patents in that | ||||||
|  | country that you have reason to believe are valid. | ||||||
|  |  | ||||||
|  |   If, pursuant to or in connection with a single transaction or | ||||||
|  | arrangement, you convey, or propagate by procuring conveyance of, a | ||||||
|  | covered work, and grant a patent license to some of the parties | ||||||
|  | receiving the covered work authorizing them to use, propagate, modify | ||||||
|  | or convey a specific copy of the covered work, then the patent license | ||||||
|  | you grant is automatically extended to all recipients of the covered | ||||||
|  | work and works based on it. | ||||||
|  |  | ||||||
|  |   A patent license is "discriminatory" if it does not include within | ||||||
|  | the scope of its coverage, prohibits the exercise of, or is | ||||||
|  | conditioned on the non-exercise of one or more of the rights that are | ||||||
|  | specifically granted under this License.  You may not convey a covered | ||||||
|  | work if you are a party to an arrangement with a third party that is | ||||||
|  | in the business of distributing software, under which you make payment | ||||||
|  | to the third party based on the extent of your activity of conveying | ||||||
|  | the work, and under which the third party grants, to any of the | ||||||
|  | parties who would receive the covered work from you, a discriminatory | ||||||
|  | patent license (a) in connection with copies of the covered work | ||||||
|  | conveyed by you (or copies made from those copies), or (b) primarily | ||||||
|  | for and in connection with specific products or compilations that | ||||||
|  | contain the covered work, unless you entered into that arrangement, | ||||||
|  | or that patent license was granted, prior to 28 March 2007. | ||||||
|  |  | ||||||
|  |   Nothing in this License shall be construed as excluding or limiting | ||||||
|  | any implied license or other defenses to infringement that may | ||||||
|  | otherwise be available to you under applicable patent law. | ||||||
|  |  | ||||||
|  |   12. No Surrender of Others' Freedom. | ||||||
|  |  | ||||||
|  |   If conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot convey a | ||||||
|  | covered work so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you may | ||||||
|  | not convey it at all.  For example, if you agree to terms that obligate you | ||||||
|  | to collect a royalty for further conveying from those to whom you convey | ||||||
|  | the Program, the only way you could satisfy both those terms and this | ||||||
|  | License would be to refrain entirely from conveying the Program. | ||||||
|  |  | ||||||
|  |   13. Remote Network Interaction; Use with the GNU General Public License. | ||||||
|  |  | ||||||
|  |   Notwithstanding any other provision of this License, if you modify the | ||||||
|  | Program, your modified version must prominently offer all users | ||||||
|  | interacting with it remotely through a computer network (if your version | ||||||
|  | supports such interaction) an opportunity to receive the Corresponding | ||||||
|  | Source of your version by providing access to the Corresponding Source | ||||||
|  | from a network server at no charge, through some standard or customary | ||||||
|  | means of facilitating copying of software.  This Corresponding Source | ||||||
|  | shall include the Corresponding Source for any work covered by version 3 | ||||||
|  | of the GNU General Public License that is incorporated pursuant to the | ||||||
|  | following paragraph. | ||||||
|  |  | ||||||
|  |   Notwithstanding any other provision of this License, you have | ||||||
|  | permission to link or combine any covered work with a work licensed | ||||||
|  | under version 3 of the GNU General Public License into a single | ||||||
|  | combined work, and to convey the resulting work.  The terms of this | ||||||
|  | License will continue to apply to the part which is the covered work, | ||||||
|  | but the work with which it is combined will remain governed by version | ||||||
|  | 3 of the GNU General Public License. | ||||||
|  |  | ||||||
|  |   14. Revised Versions of this License. | ||||||
|  |  | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions of | ||||||
|  | the GNU Affero General Public License from time to time.  Such new versions | ||||||
|  | will be similar in spirit to the present version, but may differ in detail to | ||||||
|  | address new problems or concerns. | ||||||
|  |  | ||||||
|  |   Each version is given a distinguishing version number.  If the | ||||||
|  | Program specifies that a certain numbered version of the GNU Affero General | ||||||
|  | Public License "or any later version" applies to it, you have the | ||||||
|  | option of following the terms and conditions either of that numbered | ||||||
|  | version or of any later version published by the Free Software | ||||||
|  | Foundation.  If the Program does not specify a version number of the | ||||||
|  | GNU Affero General Public License, you may choose any version ever published | ||||||
|  | by the Free Software Foundation. | ||||||
|  |  | ||||||
|  |   If the Program specifies that a proxy can decide which future | ||||||
|  | versions of the GNU Affero General Public License can be used, that proxy's | ||||||
|  | public statement of acceptance of a version permanently authorizes you | ||||||
|  | to choose that version for the Program. | ||||||
|  |  | ||||||
|  |   Later license versions may give you additional or different | ||||||
|  | permissions.  However, no additional obligations are imposed on any | ||||||
|  | author or copyright holder as a result of your choosing to follow a | ||||||
|  | later version. | ||||||
|  |  | ||||||
|  |   15. Disclaimer of Warranty. | ||||||
|  |  | ||||||
|  |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | ||||||
|  | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | ||||||
|  | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | ||||||
|  | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | ||||||
|  | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | ||||||
|  | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | ||||||
|  | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  |  | ||||||
|  |   16. Limitation of Liability. | ||||||
|  |  | ||||||
|  |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||||
|  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | ||||||
|  | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | ||||||
|  | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | ||||||
|  | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | ||||||
|  | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | ||||||
|  | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | ||||||
|  | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | ||||||
|  | SUCH DAMAGES. | ||||||
|  |  | ||||||
|  |   17. Interpretation of Sections 15 and 16. | ||||||
|  |  | ||||||
|  |   If the disclaimer of warranty and limitation of liability provided | ||||||
|  | above cannot be given local legal effect according to their terms, | ||||||
|  | reviewing courts shall apply local law that most closely approximates | ||||||
|  | an absolute waiver of all civil liability in connection with the | ||||||
|  | Program, unless a warranty or assumption of liability accompanies a | ||||||
|  | copy of the Program in return for a fee. | ||||||
|  |  | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |             How to Apply These Terms to Your New Programs | ||||||
|  |  | ||||||
|  |   If you develop a new program, and you want it to be of the greatest | ||||||
|  | possible use to the public, the best way to achieve this is to make it | ||||||
|  | free software which everyone can redistribute and change under these terms. | ||||||
|  |  | ||||||
|  |   To do so, attach the following notices to the program.  It is safest | ||||||
|  | to attach them to the start of each source file to most effectively | ||||||
|  | state the exclusion of warranty; and each file should have at least | ||||||
|  | the "copyright" line and a pointer to where the full notice is found. | ||||||
|  |  | ||||||
|  |     <one line to give the program's name and a brief idea of what it does.> | ||||||
|  |     Copyright (C) <year>  <name of author> | ||||||
|  |  | ||||||
|  |     This program is free software: you can redistribute it and/or modify | ||||||
|  |     it under the terms of the GNU Affero General Public License as published by | ||||||
|  |     the Free Software Foundation, either version 3 of the License, or | ||||||
|  |     (at your option) any later version. | ||||||
|  |  | ||||||
|  |     This program is distributed in the hope that it will be useful, | ||||||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |     GNU Affero General Public License for more details. | ||||||
|  |  | ||||||
|  |     You should have received a copy of the GNU Affero General Public License | ||||||
|  |     along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | Also add information on how to contact you by electronic and paper mail. | ||||||
|  |  | ||||||
|  |   If your software can interact with users remotely through a computer | ||||||
|  | network, you should also make sure that it provides a way for users to | ||||||
|  | get its source.  For example, if your program is a web application, its | ||||||
|  | interface could display a "Source" link that leads users to an archive | ||||||
|  | of the code.  There are many ways you could offer source, and different | ||||||
|  | solutions will be better for different programs; see section 13 for the | ||||||
|  | specific requirements. | ||||||
|  |  | ||||||
|  |   You should also get your employer (if you work as a programmer) or school, | ||||||
|  | if any, to sign a "copyright disclaimer" for the program, if necessary. | ||||||
|  | For more information on this, and how to apply and follow the GNU AGPL, see | ||||||
|  | <http://www.gnu.org/licenses/>. | ||||||
							
								
								
									
										288
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||||
|  |  | ||||||
|  | package log4go | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/xml" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type xmlProperty struct { | ||||||
|  | 	Name  string `xml:"name,attr"` | ||||||
|  | 	Value string `xml:",chardata"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type xmlFilter struct { | ||||||
|  | 	Enabled  string        `xml:"enabled,attr"` | ||||||
|  | 	Tag      string        `xml:"tag"` | ||||||
|  | 	Level    string        `xml:"level"` | ||||||
|  | 	Type     string        `xml:"type"` | ||||||
|  | 	Property []xmlProperty `xml:"property"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type xmlLoggerConfig struct { | ||||||
|  | 	Filter []xmlFilter `xml:"filter"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Load XML configuration; see examples/example.xml for documentation | ||||||
|  | func (log Logger) LoadConfiguration(filename string) { | ||||||
|  | 	log.Close() | ||||||
|  |  | ||||||
|  | 	// Open the configuration file | ||||||
|  | 	fd, err := os.Open(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	contents, err := ioutil.ReadAll(fd) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	xc := new(xmlLoggerConfig) | ||||||
|  | 	if err := xml.Unmarshal(contents, xc); err != nil { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, xmlfilt := range xc.Filter { | ||||||
|  | 		var filt LogWriter | ||||||
|  | 		var lvl Level | ||||||
|  | 		bad, good, enabled := false, true, false | ||||||
|  |  | ||||||
|  | 		// Check required children | ||||||
|  | 		if len(xmlfilt.Enabled) == 0 { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename) | ||||||
|  | 			bad = true | ||||||
|  | 		} else { | ||||||
|  | 			enabled = xmlfilt.Enabled != "false" | ||||||
|  | 		} | ||||||
|  | 		if len(xmlfilt.Tag) == 0 { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename) | ||||||
|  | 			bad = true | ||||||
|  | 		} | ||||||
|  | 		if len(xmlfilt.Type) == 0 { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename) | ||||||
|  | 			bad = true | ||||||
|  | 		} | ||||||
|  | 		if len(xmlfilt.Level) == 0 { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename) | ||||||
|  | 			bad = true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		switch xmlfilt.Level { | ||||||
|  | 		case "FINEST": | ||||||
|  | 			lvl = FINEST | ||||||
|  | 		case "FINE": | ||||||
|  | 			lvl = FINE | ||||||
|  | 		case "DEBUG": | ||||||
|  | 			lvl = DEBUG | ||||||
|  | 		case "TRACE": | ||||||
|  | 			lvl = TRACE | ||||||
|  | 		case "INFO": | ||||||
|  | 			lvl = INFO | ||||||
|  | 		case "WARNING": | ||||||
|  | 			lvl = WARNING | ||||||
|  | 		case "ERROR": | ||||||
|  | 			lvl = ERROR | ||||||
|  | 		case "CRITICAL": | ||||||
|  | 			lvl = CRITICAL | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level) | ||||||
|  | 			bad = true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Just so all of the required attributes are errored at the same time if missing | ||||||
|  | 		if bad { | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		switch xmlfilt.Type { | ||||||
|  | 		case "console": | ||||||
|  | 			filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled) | ||||||
|  | 		case "file": | ||||||
|  | 			filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled) | ||||||
|  | 		case "xml": | ||||||
|  | 			filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled) | ||||||
|  | 		case "socket": | ||||||
|  | 			filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled) | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Just so all of the required params are errored at the same time if wrong | ||||||
|  | 		if !good { | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// If we're disabled (syntax and correctness checks only), don't add to logger | ||||||
|  | 		if !enabled { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		log[xmlfilt.Tag] = &Filter{lvl, filt} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) { | ||||||
|  | 	// Parse properties | ||||||
|  | 	for _, prop := range props { | ||||||
|  | 		switch prop.Name { | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If it's disabled, we're just checking syntax | ||||||
|  | 	if !enabled { | ||||||
|  | 		return nil, true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NewConsoleLogWriter(), true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024) | ||||||
|  | func strToNumSuffix(str string, mult int) int { | ||||||
|  | 	num := 1 | ||||||
|  | 	if len(str) > 1 { | ||||||
|  | 		switch str[len(str)-1] { | ||||||
|  | 		case 'G', 'g': | ||||||
|  | 			num *= mult | ||||||
|  | 			fallthrough | ||||||
|  | 		case 'M', 'm': | ||||||
|  | 			num *= mult | ||||||
|  | 			fallthrough | ||||||
|  | 		case 'K', 'k': | ||||||
|  | 			num *= mult | ||||||
|  | 			str = str[0 : len(str)-1] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	parsed, _ := strconv.Atoi(str) | ||||||
|  | 	return parsed * num | ||||||
|  | } | ||||||
|  | func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { | ||||||
|  | 	file := "" | ||||||
|  | 	format := "[%D %T] [%L] (%S) %M" | ||||||
|  | 	maxlines := 0 | ||||||
|  | 	maxsize := 0 | ||||||
|  | 	daily := false | ||||||
|  | 	rotate := false | ||||||
|  |  | ||||||
|  | 	// Parse properties | ||||||
|  | 	for _, prop := range props { | ||||||
|  | 		switch prop.Name { | ||||||
|  | 		case "filename": | ||||||
|  | 			file = strings.Trim(prop.Value, " \r\n") | ||||||
|  | 		case "format": | ||||||
|  | 			format = strings.Trim(prop.Value, " \r\n") | ||||||
|  | 		case "maxlines": | ||||||
|  | 			maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) | ||||||
|  | 		case "maxsize": | ||||||
|  | 			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) | ||||||
|  | 		case "daily": | ||||||
|  | 			daily = strings.Trim(prop.Value, " \r\n") != "false" | ||||||
|  | 		case "rotate": | ||||||
|  | 			rotate = strings.Trim(prop.Value, " \r\n") != "false" | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check properties | ||||||
|  | 	if len(file) == 0 { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename) | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If it's disabled, we're just checking syntax | ||||||
|  | 	if !enabled { | ||||||
|  | 		return nil, true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	flw := NewFileLogWriter(file, rotate) | ||||||
|  | 	flw.SetFormat(format) | ||||||
|  | 	flw.SetRotateLines(maxlines) | ||||||
|  | 	flw.SetRotateSize(maxsize) | ||||||
|  | 	flw.SetRotateDaily(daily) | ||||||
|  | 	return flw, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) { | ||||||
|  | 	file := "" | ||||||
|  | 	maxrecords := 0 | ||||||
|  | 	maxsize := 0 | ||||||
|  | 	daily := false | ||||||
|  | 	rotate := false | ||||||
|  |  | ||||||
|  | 	// Parse properties | ||||||
|  | 	for _, prop := range props { | ||||||
|  | 		switch prop.Name { | ||||||
|  | 		case "filename": | ||||||
|  | 			file = strings.Trim(prop.Value, " \r\n") | ||||||
|  | 		case "maxrecords": | ||||||
|  | 			maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) | ||||||
|  | 		case "maxsize": | ||||||
|  | 			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) | ||||||
|  | 		case "daily": | ||||||
|  | 			daily = strings.Trim(prop.Value, " \r\n") != "false" | ||||||
|  | 		case "rotate": | ||||||
|  | 			rotate = strings.Trim(prop.Value, " \r\n") != "false" | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check properties | ||||||
|  | 	if len(file) == 0 { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename) | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If it's disabled, we're just checking syntax | ||||||
|  | 	if !enabled { | ||||||
|  | 		return nil, true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	xlw := NewXMLLogWriter(file, rotate) | ||||||
|  | 	xlw.SetRotateLines(maxrecords) | ||||||
|  | 	xlw.SetRotateSize(maxsize) | ||||||
|  | 	xlw.SetRotateDaily(daily) | ||||||
|  | 	return xlw, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) { | ||||||
|  | 	endpoint := "" | ||||||
|  | 	protocol := "udp" | ||||||
|  |  | ||||||
|  | 	// Parse properties | ||||||
|  | 	for _, prop := range props { | ||||||
|  | 		switch prop.Name { | ||||||
|  | 		case "endpoint": | ||||||
|  | 			endpoint = strings.Trim(prop.Value, " \r\n") | ||||||
|  | 		case "protocol": | ||||||
|  | 			protocol = strings.Trim(prop.Value, " \r\n") | ||||||
|  | 		default: | ||||||
|  | 			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Check properties | ||||||
|  | 	if len(endpoint) == 0 { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename) | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If it's disabled, we're just checking syntax | ||||||
|  | 	if !enabled { | ||||||
|  | 		return nil, true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NewSocketLogWriter(protocol, endpoint), true | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | import l4g "code.google.com/p/log4go" | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	log := l4g.NewLogger() | ||||||
|  | 	defer log.Close() | ||||||
|  | 	log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) | ||||||
|  | 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | import l4g "code.google.com/p/log4go" | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	filename = "flw.log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	// Get a new logger instance | ||||||
|  | 	log := l4g.NewLogger() | ||||||
|  |  | ||||||
|  | 	// Create a default logger that is logging messages of FINE or higher | ||||||
|  | 	log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false)) | ||||||
|  | 	log.Close() | ||||||
|  |  | ||||||
|  | 	/* Can also specify manually via the following: (these are the defaults) */ | ||||||
|  | 	flw := l4g.NewFileLogWriter(filename, false) | ||||||
|  | 	flw.SetFormat("[%D %T] [%L] (%S) %M") | ||||||
|  | 	flw.SetRotate(false) | ||||||
|  | 	flw.SetRotateSize(0) | ||||||
|  | 	flw.SetRotateLines(0) | ||||||
|  | 	flw.SetRotateDaily(false) | ||||||
|  | 	log.AddFilter("file", l4g.FINE, flw) | ||||||
|  |  | ||||||
|  | 	// Log some experimental messages | ||||||
|  | 	log.Finest("Everything is created now (notice that I will not be printing to the file)") | ||||||
|  | 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||||
|  | 	log.Critical("Time to close out!") | ||||||
|  |  | ||||||
|  | 	// Close the log | ||||||
|  | 	log.Close() | ||||||
|  |  | ||||||
|  | 	// Print what was logged to the file (yes, I know I'm skipping error checking) | ||||||
|  | 	fd, _ := os.Open(filename) | ||||||
|  | 	in := bufio.NewReader(fd) | ||||||
|  | 	fmt.Print("Messages logged to file were: (line numbers not included)\n") | ||||||
|  | 	for lineno := 1; ; lineno++ { | ||||||
|  | 		line, err := in.ReadString('\n') | ||||||
|  | 		if err == io.EOF { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		fmt.Printf("%3d:\t%s", lineno, line) | ||||||
|  | 	} | ||||||
|  | 	fd.Close() | ||||||
|  |  | ||||||
|  | 	// Remove the file so it's not lying around | ||||||
|  | 	os.Remove(filename) | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	port = flag.String("p", "12124", "Port number to listen on") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func e(err error) { | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Printf("Erroring out: %s\n", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	flag.Parse() | ||||||
|  |  | ||||||
|  | 	// Bind to the port | ||||||
|  | 	bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port) | ||||||
|  | 	e(err) | ||||||
|  |  | ||||||
|  | 	// Create listener | ||||||
|  | 	listener, err := net.ListenUDP("udp", bind) | ||||||
|  | 	e(err) | ||||||
|  |  | ||||||
|  | 	fmt.Printf("Listening to port %s...\n", *port) | ||||||
|  | 	for { | ||||||
|  | 		// read into a new buffer | ||||||
|  | 		buffer := make([]byte, 1024) | ||||||
|  | 		_, _, err := listener.ReadFrom(buffer) | ||||||
|  | 		e(err) | ||||||
|  |  | ||||||
|  | 		// log to standard output | ||||||
|  | 		fmt.Println(string(buffer)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | import l4g "code.google.com/p/log4go" | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	log := l4g.NewLogger() | ||||||
|  | 	log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) | ||||||
|  |  | ||||||
|  | 	// Run `nc -u -l -p 12124` or similar before you run this to see the following message | ||||||
|  | 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||||
|  |  | ||||||
|  | 	// This makes sure the output stream buffer is written | ||||||
|  | 	log.Close() | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import l4g "code.google.com/p/log4go" | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	// Load the configuration (isn't this easy?) | ||||||
|  | 	l4g.LoadConfiguration("example.xml") | ||||||
|  |  | ||||||
|  | 	// And now we're ready! | ||||||
|  | 	l4g.Finest("This will only go to those of you really cool UDP kids!  If you change enabled=true.") | ||||||
|  | 	l4g.Debug("Oh no!  %d + %d = %d!", 2, 2, 2+2) | ||||||
|  | 	l4g.Info("About that time, eh chaps?") | ||||||
|  | } | ||||||
							
								
								
									
										264
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,264 @@ | |||||||
|  | // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||||
|  |  | ||||||
|  | package log4go | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // This log writer sends output to a file | ||||||
|  | type FileLogWriter struct { | ||||||
|  | 	rec chan *LogRecord | ||||||
|  | 	rot chan bool | ||||||
|  |  | ||||||
|  | 	// The opened file | ||||||
|  | 	filename string | ||||||
|  | 	file     *os.File | ||||||
|  |  | ||||||
|  | 	// The logging format | ||||||
|  | 	format string | ||||||
|  |  | ||||||
|  | 	// File header/trailer | ||||||
|  | 	header, trailer string | ||||||
|  |  | ||||||
|  | 	// Rotate at linecount | ||||||
|  | 	maxlines          int | ||||||
|  | 	maxlines_curlines int | ||||||
|  |  | ||||||
|  | 	// Rotate at size | ||||||
|  | 	maxsize         int | ||||||
|  | 	maxsize_cursize int | ||||||
|  |  | ||||||
|  | 	// Rotate daily | ||||||
|  | 	daily          bool | ||||||
|  | 	daily_opendate int | ||||||
|  |  | ||||||
|  | 	// Keep old logfiles (.001, .002, etc) | ||||||
|  | 	rotate    bool | ||||||
|  | 	maxbackup int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // This is the FileLogWriter's output method | ||||||
|  | func (w *FileLogWriter) LogWrite(rec *LogRecord) { | ||||||
|  | 	w.rec <- rec | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w *FileLogWriter) Close() { | ||||||
|  | 	close(w.rec) | ||||||
|  | 	w.file.Sync() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewFileLogWriter creates a new LogWriter which writes to the given file and | ||||||
|  | // has rotation enabled if rotate is true. | ||||||
|  | // | ||||||
|  | // If rotate is true, any time a new log file is opened, the old one is renamed | ||||||
|  | // with a .### extension to preserve it.  The various Set* methods can be used | ||||||
|  | // to configure log rotation based on lines, size, and daily. | ||||||
|  | // | ||||||
|  | // The standard log-line format is: | ||||||
|  | //   [%D %T] [%L] (%S) %M | ||||||
|  | func NewFileLogWriter(fname string, rotate bool) *FileLogWriter { | ||||||
|  | 	w := &FileLogWriter{ | ||||||
|  | 		rec:       make(chan *LogRecord, LogBufferLength), | ||||||
|  | 		rot:       make(chan bool), | ||||||
|  | 		filename:  fname, | ||||||
|  | 		format:    "[%D %T] [%L] (%S) %M", | ||||||
|  | 		rotate:    rotate, | ||||||
|  | 		maxbackup: 999, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// open the file for the first time | ||||||
|  | 	if err := w.intRotate(); err != nil { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		defer func() { | ||||||
|  | 			if w.file != nil { | ||||||
|  | 				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) | ||||||
|  | 				w.file.Close() | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  |  | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case <-w.rot: | ||||||
|  | 				if err := w.intRotate(); err != nil { | ||||||
|  | 					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			case rec, ok := <-w.rec: | ||||||
|  | 				if !ok { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				now := time.Now() | ||||||
|  | 				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || | ||||||
|  | 					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) || | ||||||
|  | 					(w.daily && now.Day() != w.daily_opendate) { | ||||||
|  | 					if err := w.intRotate(); err != nil { | ||||||
|  | 						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) | ||||||
|  | 						return | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// Perform the write | ||||||
|  | 				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// Update the counts | ||||||
|  | 				w.maxlines_curlines++ | ||||||
|  | 				w.maxsize_cursize += n | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Request that the logs rotate | ||||||
|  | func (w *FileLogWriter) Rotate() { | ||||||
|  | 	w.rot <- true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // If this is called in a threaded context, it MUST be synchronized | ||||||
|  | func (w *FileLogWriter) intRotate() error { | ||||||
|  | 	// Close any log file that may be open | ||||||
|  | 	if w.file != nil { | ||||||
|  | 		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) | ||||||
|  | 		w.file.Close() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If we are keeping log files, move it to the next available number | ||||||
|  | 	if w.rotate { | ||||||
|  | 		_, err := os.Lstat(w.filename) | ||||||
|  | 		if err == nil { // file exists | ||||||
|  | 			// Find the next available number | ||||||
|  | 			num := 1 | ||||||
|  | 			fname := "" | ||||||
|  | 			if w.daily && time.Now().Day() != w.daily_opendate { | ||||||
|  | 				yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | ||||||
|  |  | ||||||
|  | 				for ; err == nil && num <= 999; num++ { | ||||||
|  | 					fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num) | ||||||
|  | 					_, err = os.Lstat(fname) | ||||||
|  | 				} | ||||||
|  | 				// return error if the last file checked still existed | ||||||
|  | 				if err == nil { | ||||||
|  | 					return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				num = w.maxbackup - 1 | ||||||
|  | 				for ; num >= 1; num-- { | ||||||
|  | 					fname = w.filename + fmt.Sprintf(".%d", num) | ||||||
|  | 					nfname := w.filename + fmt.Sprintf(".%d", num+1) | ||||||
|  | 					_, err = os.Lstat(fname) | ||||||
|  | 					if err == nil { | ||||||
|  | 						os.Rename(fname, nfname) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			w.file.Close() | ||||||
|  | 			// Rename the file to its newfound home | ||||||
|  | 			err = os.Rename(w.filename, fname) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return fmt.Errorf("Rotate: %s\n", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Open the log file | ||||||
|  | 	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	w.file = fd | ||||||
|  |  | ||||||
|  | 	now := time.Now() | ||||||
|  | 	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) | ||||||
|  |  | ||||||
|  | 	// Set the daily open date to the current date | ||||||
|  | 	w.daily_opendate = now.Day() | ||||||
|  |  | ||||||
|  | 	// initialize rotation values | ||||||
|  | 	w.maxlines_curlines = 0 | ||||||
|  | 	w.maxsize_cursize = 0 | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set the logging format (chainable).  Must be called before the first log | ||||||
|  | // message is written. | ||||||
|  | func (w *FileLogWriter) SetFormat(format string) *FileLogWriter { | ||||||
|  | 	w.format = format | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set the logfile header and footer (chainable).  Must be called before the first log | ||||||
|  | // message is written.  These are formatted similar to the FormatLogRecord (e.g. | ||||||
|  | // you can use %D and %T in your header/footer for date and time). | ||||||
|  | func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter { | ||||||
|  | 	w.header, w.trailer = head, foot | ||||||
|  | 	if w.maxlines_curlines == 0 { | ||||||
|  | 		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()})) | ||||||
|  | 	} | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set rotate at linecount (chainable). Must be called before the first log | ||||||
|  | // message is written. | ||||||
|  | func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter { | ||||||
|  | 	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines) | ||||||
|  | 	w.maxlines = maxlines | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set rotate at size (chainable). Must be called before the first log message | ||||||
|  | // is written. | ||||||
|  | func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter { | ||||||
|  | 	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize) | ||||||
|  | 	w.maxsize = maxsize | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set rotate daily (chainable). Must be called before the first log message is | ||||||
|  | // written. | ||||||
|  | func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter { | ||||||
|  | 	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily) | ||||||
|  | 	w.daily = daily | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set max backup files. Must be called before the first log message | ||||||
|  | // is written. | ||||||
|  | func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter { | ||||||
|  | 	w.maxbackup = maxbackup | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRotate changes whether or not the old logs are kept. (chainable) Must be | ||||||
|  | // called before the first log message is written.  If rotate is false, the | ||||||
|  | // files are overwritten; otherwise, they are rotated to another file before the | ||||||
|  | // new log is opened. | ||||||
|  | func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter { | ||||||
|  | 	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate) | ||||||
|  | 	w.rotate = rotate | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewXMLLogWriter is a utility method for creating a FileLogWriter set up to | ||||||
|  | // output XML record log messages instead of line-based ones. | ||||||
|  | func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter { | ||||||
|  | 	return NewFileLogWriter(fname, rotate).SetFormat( | ||||||
|  | 		`	<record level="%L"> | ||||||
|  | 		<timestamp>%D %T</timestamp> | ||||||
|  | 		<source>%S</source> | ||||||
|  | 		<message>%M</message> | ||||||
|  | 	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>") | ||||||
|  | } | ||||||
							
								
								
									
										484
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,484 @@ | |||||||
|  | // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||||
|  |  | ||||||
|  | // Package log4go provides level-based and highly configurable logging. | ||||||
|  | // | ||||||
|  | // Enhanced Logging | ||||||
|  | // | ||||||
|  | // This is inspired by the logging functionality in Java.  Essentially, you create a Logger | ||||||
|  | // object and create output filters for it.  You can send whatever you want to the Logger, | ||||||
|  | // and it will filter that based on your settings and send it to the outputs.  This way, you | ||||||
|  | // can put as much debug code in your program as you want, and when you're done you can filter | ||||||
|  | // out the mundane messages so only the important ones show up. | ||||||
|  | // | ||||||
|  | // Utility functions are provided to make life easier. Here is some example code to get started: | ||||||
|  | // | ||||||
|  | // log := log4go.NewLogger() | ||||||
|  | // log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter()) | ||||||
|  | // log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true)) | ||||||
|  | // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) | ||||||
|  | // | ||||||
|  | // The first two lines can be combined with the utility NewDefaultLogger: | ||||||
|  | // | ||||||
|  | // log := log4go.NewDefaultLogger(log4go.DEBUG) | ||||||
|  | // log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true)) | ||||||
|  | // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) | ||||||
|  | // | ||||||
|  | // Usage notes: | ||||||
|  | // - The ConsoleLogWriter does not display the source of the message to standard | ||||||
|  | //   output, but the FileLogWriter does. | ||||||
|  | // - The utility functions (Info, Debug, Warn, etc) derive their source from the | ||||||
|  | //   calling function, and this incurs extra overhead. | ||||||
|  | // | ||||||
|  | // Changes from 2.0: | ||||||
|  | // - The external interface has remained mostly stable, but a lot of the | ||||||
|  | //   internals have been changed, so if you depended on any of this or created | ||||||
|  | //   your own LogWriter, then you will probably have to update your code.  In | ||||||
|  | //   particular, Logger is now a map and ConsoleLogWriter is now a channel | ||||||
|  | //   behind-the-scenes, and the LogWrite method no longer has return values. | ||||||
|  | // | ||||||
|  | // Future work: (please let me know if you think I should work on any of these particularly) | ||||||
|  | // - Log file rotation | ||||||
|  | // - Logging configuration files ala log4j | ||||||
|  | // - Have the ability to remove filters? | ||||||
|  | // - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows | ||||||
|  | //   for another method of logging | ||||||
|  | // - Add an XML filter type | ||||||
|  | package log4go | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"runtime" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Version information | ||||||
|  | const ( | ||||||
|  | 	L4G_VERSION = "log4go-v3.0.1" | ||||||
|  | 	L4G_MAJOR   = 3 | ||||||
|  | 	L4G_MINOR   = 0 | ||||||
|  | 	L4G_BUILD   = 1 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /****** Constants ******/ | ||||||
|  |  | ||||||
|  | // These are the integer logging levels used by the logger | ||||||
|  | type Level int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	FINEST Level = iota | ||||||
|  | 	FINE | ||||||
|  | 	DEBUG | ||||||
|  | 	TRACE | ||||||
|  | 	INFO | ||||||
|  | 	WARNING | ||||||
|  | 	ERROR | ||||||
|  | 	CRITICAL | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Logging level strings | ||||||
|  | var ( | ||||||
|  | 	levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (l Level) String() string { | ||||||
|  | 	if l < 0 || int(l) > len(levelStrings) { | ||||||
|  | 		return "UNKNOWN" | ||||||
|  | 	} | ||||||
|  | 	return levelStrings[int(l)] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****** Variables ******/ | ||||||
|  | var ( | ||||||
|  | 	// LogBufferLength specifies how many log messages a particular log4go | ||||||
|  | 	// logger can buffer at a time before writing them. | ||||||
|  | 	LogBufferLength = 32 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /****** LogRecord ******/ | ||||||
|  |  | ||||||
|  | // A LogRecord contains all of the pertinent information for each message | ||||||
|  | type LogRecord struct { | ||||||
|  | 	Level   Level     // The log level | ||||||
|  | 	Created time.Time // The time at which the log message was created (nanoseconds) | ||||||
|  | 	Source  string    // The message source | ||||||
|  | 	Message string    // The log message | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****** LogWriter ******/ | ||||||
|  |  | ||||||
|  | // This is an interface for anything that should be able to write logs | ||||||
|  | type LogWriter interface { | ||||||
|  | 	// This will be called to log a LogRecord message. | ||||||
|  | 	LogWrite(rec *LogRecord) | ||||||
|  |  | ||||||
|  | 	// This should clean up anything lingering about the LogWriter, as it is called before | ||||||
|  | 	// the LogWriter is removed.  LogWrite should not be called after Close. | ||||||
|  | 	Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****** Logger ******/ | ||||||
|  |  | ||||||
|  | // A Filter represents the log level below which no log records are written to | ||||||
|  | // the associated LogWriter. | ||||||
|  | type Filter struct { | ||||||
|  | 	Level Level | ||||||
|  | 	LogWriter | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A Logger represents a collection of Filters through which log messages are | ||||||
|  | // written. | ||||||
|  | type Logger map[string]*Filter | ||||||
|  |  | ||||||
|  | // Create a new logger. | ||||||
|  | // | ||||||
|  | // DEPRECATED: Use make(Logger) instead. | ||||||
|  | func NewLogger() Logger { | ||||||
|  | 	os.Stderr.WriteString("warning: use of deprecated NewLogger\n") | ||||||
|  | 	return make(Logger) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create a new logger with a "stdout" filter configured to send log messages at | ||||||
|  | // or above lvl to standard output. | ||||||
|  | // | ||||||
|  | // DEPRECATED: use NewDefaultLogger instead. | ||||||
|  | func NewConsoleLogger(lvl Level) Logger { | ||||||
|  | 	os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n") | ||||||
|  | 	return Logger{ | ||||||
|  | 		"stdout": &Filter{lvl, NewConsoleLogWriter()}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create a new logger with a "stdout" filter configured to send log messages at | ||||||
|  | // or above lvl to standard output. | ||||||
|  | func NewDefaultLogger(lvl Level) Logger { | ||||||
|  | 	return Logger{ | ||||||
|  | 		"stdout": &Filter{lvl, NewConsoleLogWriter()}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Closes all log writers in preparation for exiting the program or a | ||||||
|  | // reconfiguration of logging.  Calling this is not really imperative, unless | ||||||
|  | // you want to guarantee that all log messages are written.  Close removes | ||||||
|  | // all filters (and thus all LogWriters) from the logger. | ||||||
|  | func (log Logger) Close() { | ||||||
|  | 	// Close all open loggers | ||||||
|  | 	for name, filt := range log { | ||||||
|  | 		filt.Close() | ||||||
|  | 		delete(log, name) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add a new LogWriter to the Logger which will only log messages at lvl or | ||||||
|  | // higher.  This function should not be called from multiple goroutines. | ||||||
|  | // Returns the logger for chaining. | ||||||
|  | func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger { | ||||||
|  | 	log[name] = &Filter{lvl, writer} | ||||||
|  | 	return log | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /******* Logging *******/ | ||||||
|  | // Send a formatted log message internally | ||||||
|  | func (log Logger) intLogf(lvl Level, format string, args ...interface{}) { | ||||||
|  | 	skip := true | ||||||
|  |  | ||||||
|  | 	// Determine if any logging will be done | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl >= filt.Level { | ||||||
|  | 			skip = false | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if skip { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Determine caller func | ||||||
|  | 	pc, _, lineno, ok := runtime.Caller(2) | ||||||
|  | 	src := "" | ||||||
|  | 	if ok { | ||||||
|  | 		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	msg := format | ||||||
|  | 	if len(args) > 0 { | ||||||
|  | 		msg = fmt.Sprintf(format, args...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Make the log record | ||||||
|  | 	rec := &LogRecord{ | ||||||
|  | 		Level:   lvl, | ||||||
|  | 		Created: time.Now(), | ||||||
|  | 		Source:  src, | ||||||
|  | 		Message: msg, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Dispatch the logs | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl < filt.Level { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		filt.LogWrite(rec) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Send a closure log message internally | ||||||
|  | func (log Logger) intLogc(lvl Level, closure func() string) { | ||||||
|  | 	skip := true | ||||||
|  |  | ||||||
|  | 	// Determine if any logging will be done | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl >= filt.Level { | ||||||
|  | 			skip = false | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if skip { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Determine caller func | ||||||
|  | 	pc, _, lineno, ok := runtime.Caller(2) | ||||||
|  | 	src := "" | ||||||
|  | 	if ok { | ||||||
|  | 		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Make the log record | ||||||
|  | 	rec := &LogRecord{ | ||||||
|  | 		Level:   lvl, | ||||||
|  | 		Created: time.Now(), | ||||||
|  | 		Source:  src, | ||||||
|  | 		Message: closure(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Dispatch the logs | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl < filt.Level { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		filt.LogWrite(rec) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Send a log message with manual level, source, and message. | ||||||
|  | func (log Logger) Log(lvl Level, source, message string) { | ||||||
|  | 	skip := true | ||||||
|  |  | ||||||
|  | 	// Determine if any logging will be done | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl >= filt.Level { | ||||||
|  | 			skip = false | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if skip { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Make the log record | ||||||
|  | 	rec := &LogRecord{ | ||||||
|  | 		Level:   lvl, | ||||||
|  | 		Created: time.Now(), | ||||||
|  | 		Source:  source, | ||||||
|  | 		Message: message, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Dispatch the logs | ||||||
|  | 	for _, filt := range log { | ||||||
|  | 		if lvl < filt.Level { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		filt.LogWrite(rec) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Logf logs a formatted log message at the given log level, using the caller as | ||||||
|  | // its source. | ||||||
|  | func (log Logger) Logf(lvl Level, format string, args ...interface{}) { | ||||||
|  | 	log.intLogf(lvl, format, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Logc logs a string returned by the closure at the given log level, using the caller as | ||||||
|  | // its source.  If no log message would be written, the closure is never called. | ||||||
|  | func (log Logger) Logc(lvl Level, closure func() string) { | ||||||
|  | 	log.intLogc(lvl, closure) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Finest logs a message at the finest log level. | ||||||
|  | // See Debug for an explanation of the arguments. | ||||||
|  | func (log Logger) Finest(arg0 interface{}, args ...interface{}) { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = FINEST | ||||||
|  | 	) | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		log.intLogf(lvl, first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		log.intLogc(lvl, first) | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fine logs a message at the fine log level. | ||||||
|  | // See Debug for an explanation of the arguments. | ||||||
|  | func (log Logger) Fine(arg0 interface{}, args ...interface{}) { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = FINE | ||||||
|  | 	) | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		log.intLogf(lvl, first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		log.intLogc(lvl, first) | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Debug is a utility method for debug log messages. | ||||||
|  | // The behavior of Debug depends on the first argument: | ||||||
|  | // - arg0 is a string | ||||||
|  | //   When given a string as the first argument, this behaves like Logf but with | ||||||
|  | //   the DEBUG log level: the first argument is interpreted as a format for the | ||||||
|  | //   latter arguments. | ||||||
|  | // - arg0 is a func()string | ||||||
|  | //   When given a closure of type func()string, this logs the string returned by | ||||||
|  | //   the closure iff it will be logged.  The closure runs at most one time. | ||||||
|  | // - arg0 is interface{} | ||||||
|  | //   When given anything else, the log message will be each of the arguments | ||||||
|  | //   formatted with %v and separated by spaces (ala Sprint). | ||||||
|  | func (log Logger) Debug(arg0 interface{}, args ...interface{}) { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = DEBUG | ||||||
|  | 	) | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		log.intLogf(lvl, first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		log.intLogc(lvl, first) | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Trace logs a message at the trace log level. | ||||||
|  | // See Debug for an explanation of the arguments. | ||||||
|  | func (log Logger) Trace(arg0 interface{}, args ...interface{}) { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = TRACE | ||||||
|  | 	) | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		log.intLogf(lvl, first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		log.intLogc(lvl, first) | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Info logs a message at the info log level. | ||||||
|  | // See Debug for an explanation of the arguments. | ||||||
|  | func (log Logger) Info(arg0 interface{}, args ...interface{}) { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = INFO | ||||||
|  | 	) | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		log.intLogf(lvl, first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		log.intLogc(lvl, first) | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Warn logs a message at the warning log level and returns the formatted error. | ||||||
|  | // At the warning level and higher, there is no performance benefit if the | ||||||
|  | // message is not actually logged, because all formats are processed and all | ||||||
|  | // closures are executed to format the error message. | ||||||
|  | // See Debug for further explanation of the arguments. | ||||||
|  | func (log Logger) Warn(arg0 interface{}, args ...interface{}) error { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = WARNING | ||||||
|  | 	) | ||||||
|  | 	var msg string | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		msg = fmt.Sprintf(first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		msg = first() | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | 	log.intLogf(lvl, msg) | ||||||
|  | 	return errors.New(msg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Error logs a message at the error log level and returns the formatted error, | ||||||
|  | // See Warn for an explanation of the performance and Debug for an explanation | ||||||
|  | // of the parameters. | ||||||
|  | func (log Logger) Error(arg0 interface{}, args ...interface{}) error { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = ERROR | ||||||
|  | 	) | ||||||
|  | 	var msg string | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		msg = fmt.Sprintf(first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		msg = first() | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | 	log.intLogf(lvl, msg) | ||||||
|  | 	return errors.New(msg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Critical logs a message at the critical log level and returns the formatted error, | ||||||
|  | // See Warn for an explanation of the performance and Debug for an explanation | ||||||
|  | // of the parameters. | ||||||
|  | func (log Logger) Critical(arg0 interface{}, args ...interface{}) error { | ||||||
|  | 	const ( | ||||||
|  | 		lvl = CRITICAL | ||||||
|  | 	) | ||||||
|  | 	var msg string | ||||||
|  | 	switch first := arg0.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		// Use the string as a format string | ||||||
|  | 		msg = fmt.Sprintf(first, args...) | ||||||
|  | 	case func() string: | ||||||
|  | 		// Log the closure (no other arguments used) | ||||||
|  | 		msg = first() | ||||||
|  | 	default: | ||||||
|  | 		// Build a format string so that it will be similar to Sprint | ||||||
|  | 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||||
|  | 	} | ||||||
|  | 	log.intLogf(lvl, msg) | ||||||
|  | 	return errors.New(msg) | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user