forked from lug/matterbridge
		
	Compare commits
	
		
			94 Commits
		
	
	
		
			v0.1
			...
			v0.6.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c2c135bca2 | ||
|   | eb20cb237d | ||
|   | 106404d32f | ||
|   | e06efbad9f | ||
|   | 3311c7f923 | ||
|   | 3a6c655dfb | ||
|   | e11d786775 | ||
|   | 889b6debc4 | ||
|   | 9cb3413d9c | ||
|   | 131826e1d1 | ||
|   | 96e21dd051 | ||
|   | 32e5f396e7 | ||
|   | 6c6000dbbd | ||
|   | 24defcb970 | ||
|   | a1a11a88b3 | ||
|   | a997ae29ad | ||
|   | ff94796700 | ||
|   | 1f72ca4c4e | ||
|   | 46faad8b57 | ||
|   | 30f30364d5 | ||
|   | 073d90da88 | ||
|   | c769e23a9a | ||
|   | 9db48f4794 | ||
|   | 911c597377 | ||
|   | 28244ffd9a | ||
|   | 3e38c7945c | ||
|   | 79ffb76f6e | ||
|   | 5fe4b749cf | ||
|   | 6991d85da9 | ||
|   | c1c187a1ab | ||
|   | 055d12e3ef | ||
|   | b49429d722 | ||
|   | 815c7f8d64 | ||
|   | c879f79456 | ||
|   | 3bc25f4707 | ||
|   | 300cfe044a | ||
|   | fb586f4a96 | ||
|   | ced371bece | ||
|   | a87cac1982 | ||
|   | 8fb5c7afa6 | ||
|   | aceb830378 | ||
|   | 0f2976c5ce | ||
|   | 78b17977c5 | ||
|   | 6ec77e06ea | ||
|   | e48db67649 | ||
|   | e03f331f55 | ||
|   | ff5aeeb1e1 | ||
|   | 33844fa60c | ||
|   | 85faa43145 | ||
|   | 59e6abcc11 | ||
|   | 38e3bbe5c9 | ||
|   | 51265d5464 | ||
|   | de4c780410 | ||
|   | 6b18257185 | ||
|   | 4b1ebaf7d5 | ||
|   | 93db74e7e1 | ||
|   | 0e6fe4070a | ||
|   | 69b534ee99 | ||
|   | 71a504945b | ||
|   | 99ac7dc114 | ||
|   | 4984473c1b | ||
|   | 3fcce2d8a0 | ||
|   | a53e699112 | ||
|   | f29822db02 | ||
|   | a63433e41b | ||
|   | e0379ca5af | ||
|   | 4759ee6132 | ||
|   | 5ec94fdb43 | ||
|   | a64deb1238 | ||
|   | f914695801 | ||
|   | 304dc2e25f | ||
|   | fd74dca175 | ||
|   | c7ace91bf6 | ||
|   | 9f07a2cfd5 | ||
|   | 0dc5e042d2 | ||
|   | f0a5d2396f | ||
|   | bdac03f725 | ||
|   | c1f80383f7 | ||
|   | bd7c1e3e3c | ||
|   | 5c1b02c7a3 | ||
|   | 38fce68609 | ||
|   | 90f276863b | ||
|   | 5282cdaccd | ||
|   | 008ea94b53 | ||
|   | 693f1946b7 | ||
|   | 8b6a00d1c5 | ||
|   | 43738dbc89 | ||
|   | 6feccd4c6c | ||
|   | 25d72a7e31 | ||
|   | 523f6ffb80 | ||
|   | b346ac868b | ||
|   | d0cda03478 | ||
|   | 19b3145bd1 | ||
|   | a0d1fc0d6a | 
							
								
								
									
										11
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| FROM alpine:edge | ||||
| ENTRYPOINT ["/bin/matterbridge"] | ||||
|  | ||||
| COPY . /go/src/github.com/42wim/matterbridge | ||||
| RUN apk update && apk add go git gcc musl-dev ca-certificates \ | ||||
|         && cd /go/src/github.com/42wim/matterbridge \ | ||||
|         && export GOPATH=/go \ | ||||
|         && go get \ | ||||
|         && go build -o /bin/matterbridge \ | ||||
|         && rm -rf /go \ | ||||
|         && apk del --purge git go gcc musl-dev | ||||
							
								
								
									
										93
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,16 +1,41 @@ | ||||
| # matterbridge | ||||
|  | ||||
| Simple bridge between mattermost and IRC. Uses the in/outgoing webhooks.   | ||||
| Relays public channel messages between mattermost and IRC.   | ||||
| Simple bridge between mattermost and IRC.  | ||||
|  | ||||
| Work in progress.  | ||||
| * Relays public channel messages between mattermost and IRC. | ||||
| * Supports multiple mattermost and irc channels. | ||||
| * Matterbridge -plus also works with private groups on your mattermost. | ||||
|  | ||||
| This project has now [matterbridge-plus](https://github.com/42wim/matterbridge-plus/) merged in.  | ||||
| Breaking changes for matterbridge can be found in [migration](https://github.com/42wim/matterbridge/blob/master/migration.md) | ||||
| Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for an example. | ||||
|  | ||||
| Configuration changes since v0.5.0 can be found in [changelog.md] (https://github.com/42wim/matterbridge/blob/master/changelog.md) | ||||
|  | ||||
| ## Requirements: | ||||
| * [Mattermost] (https://github.com/mattermost/platform/) | ||||
| ### Compatibility | ||||
| * Matterbridge v0.6.0 works with mattermost 3.3.0 and higher [3.3.0 release](https://github.com/mattermost/platform/releases/tag/v3.3.0) | ||||
| * Matterbridge v0.5.0 works with mattermost 3.0.0 - 3.2.0 [3.2.0 release](https://github.com/mattermost/platform/releases/tag/v3.2.0) | ||||
|  | ||||
|  | ||||
| ### Webhooks version | ||||
| * Configured incoming/outgoing [webhooks](https://www.mattermost.org/webhooks/) on your mattermost instance. | ||||
|  | ||||
| ### Plus (API) version | ||||
| * A dedicated user(bot) on your mattermost instance. | ||||
|  | ||||
| ## binaries | ||||
| Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/) | ||||
| * For use with mattermost 3.3.0 [v0.6.0-beta1](https://github.com/42wim/matterircd/releases/tag/v0.6.0-beta1) | ||||
| * For use with mattermost 3.0.0-3.2.0 [v0.5.0](https://github.com/42wim/matterircd/releases/tag/v0.5.0) | ||||
|  | ||||
| ## building | ||||
| 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) | ||||
|  | ||||
| ``` | ||||
| cd $GOPATH | ||||
| go get https://github.com/42wim/matterbridge | ||||
| go get github.com/42wim/matterbridge | ||||
| ``` | ||||
|  | ||||
| You should now have matterbridge binary in the bin directory: | ||||
| @@ -23,38 +48,29 @@ matterbridge | ||||
| ## running | ||||
| 1) Copy the matterbridge.conf.sample to matterbridge.conf in the same directory as the matterbridge binary.   | ||||
| 2) Edit matterbridge.conf with the settings for your environment. See below for more config information.   | ||||
| 3) Now you can run matterbridge.   | ||||
| 3) Now you can run matterbridge.  | ||||
|  | ||||
| Matterbridge will: | ||||
| * start a webserver listening on the port specified in the configuration. | ||||
| * connect to specified irc server and channel. | ||||
| * send messages from mattermost to irc and vice versa, messages in mattermost will appear with irc-nick | ||||
| ``` | ||||
| Usage of ./matterbridge: | ||||
|   -conf string | ||||
|         config file (default "matterbridge.conf") | ||||
|   -debug | ||||
|         enable debug | ||||
|   -plus | ||||
|         running using API instead of webhooks (deprecated, set Plus flag in [general] config) | ||||
|   -version | ||||
|         show version | ||||
| ``` | ||||
|  | ||||
| ## config | ||||
| ### matterbridge | ||||
| matterbridge looks for matterbridge.conf in current directory. | ||||
| matterbridge looks for matterbridge.conf in current directory. (use -conf to specify another file) | ||||
|  | ||||
| Look at matterbridge.conf.sample for an example | ||||
|  | ||||
|  | ||||
| ``` | ||||
| [IRC] | ||||
| server="irc.freenode.net" | ||||
| port=6667 | ||||
| UseTLS=false | ||||
| SkipTLSVerify=true | ||||
| nick="matterbot" | ||||
| channel="#matterbridge" | ||||
|  | ||||
| [mattermost] | ||||
| #url is your incoming webhook url (account settings - integrations - incoming webhooks) | ||||
| url="http://mattermost.yourdomain.com/hooks/incomingwebhookkey"   | ||||
| #port the bridge webserver will listen on | ||||
| port=9999 | ||||
| ``` | ||||
| Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for an example. | ||||
|  | ||||
| ### mattermost | ||||
| You'll have to configure the incoming en outgoing webhooks.  | ||||
| #### webhooks version | ||||
| You'll have to configure the incoming and outgoing webhooks.  | ||||
|  | ||||
| * incoming webhooks | ||||
| Go to "account settings" - integrations - "incoming webhooks".   | ||||
| @@ -65,5 +81,20 @@ This URL should be set in the matterbridge.conf in the [mattermost] section (see | ||||
| Go to "account settings" - integrations - "outgoing webhooks".   | ||||
| Choose a channel (the same as the one from incoming webhooks) and fill in the address and port of the server matterbridge will run on.   | ||||
|  | ||||
| e.g. http://192.168.1.1:9999 (9999 is the port specified in [mattermost] section of matterbridge.conf) | ||||
| e.g. http://192.168.1.1:9999 (192.168.1.1:9999 is the BindAddress specified in [mattermost] section of matterbridge.conf) | ||||
|  | ||||
| #### plus version | ||||
| You'll have to create a new dedicated user on your mattermost instance. | ||||
| Specify the login and password in [mattermost] section of matterbridge.conf | ||||
|  | ||||
| ## FAQ | ||||
| Please look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for more information first.  | ||||
| ### Mattermost doesn't show the IRC nicks | ||||
| If you're running the webhooks version, this can be fixed by either: | ||||
| * enabling "override usernames". See [mattermost documentation](http://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks) | ||||
| * setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf. | ||||
|  | ||||
| If you're running the plus version you'll need to: | ||||
| * setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf. | ||||
|  | ||||
| Also look at the ```RemoteNickFormat``` setting. | ||||
|   | ||||
							
								
								
									
										134
									
								
								bridge/bridge.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								bridge/bridge.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| package bridge | ||||
|  | ||||
| import ( | ||||
| 	//"fmt" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/irc" | ||||
| 	"github.com/42wim/matterbridge/bridge/mattermost" | ||||
| 	"github.com/42wim/matterbridge/bridge/xmpp" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type Bridge struct { | ||||
| 	*config.Config | ||||
| 	Source      string | ||||
| 	Bridges     []Bridger | ||||
| 	Channels    []map[string]string | ||||
| 	ignoreNicks map[string][]string | ||||
| } | ||||
|  | ||||
| type Bridger interface { | ||||
| 	Send(msg config.Message) error | ||||
| 	Name() string | ||||
| 	Connect() error | ||||
| 	//Command(cmd string) string | ||||
| } | ||||
|  | ||||
| func NewBridge(cfg *config.Config) error { | ||||
| 	c := make(chan config.Message) | ||||
| 	b := &Bridge{} | ||||
| 	b.Config = cfg | ||||
| 	if cfg.IRC.Enable { | ||||
| 		b.Bridges = append(b.Bridges, birc.New(cfg, c)) | ||||
| 	} | ||||
| 	if cfg.Mattermost.Enable { | ||||
| 		b.Bridges = append(b.Bridges, bmattermost.New(cfg, c)) | ||||
| 	} | ||||
| 	if cfg.Xmpp.Enable { | ||||
| 		b.Bridges = append(b.Bridges, bxmpp.New(cfg, c)) | ||||
| 	} | ||||
| 	if len(b.Bridges) < 2 { | ||||
| 		log.Fatalf("only %d sections enabled. Need at least 2 sections enabled (eg [IRC] and [mattermost]", len(b.Bridges)) | ||||
| 	} | ||||
| 	for _, br := range b.Bridges { | ||||
| 		br.Connect() | ||||
| 	} | ||||
| 	b.mapChannels() | ||||
| 	b.mapIgnores() | ||||
| 	b.handleReceive(c) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleReceive(c chan config.Message) { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case msg := <-c: | ||||
| 			for _, br := range b.Bridges { | ||||
| 				b.handleMessage(msg, br) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) mapChannels() error { | ||||
| 	for _, val := range b.Config.Channel { | ||||
| 		m := make(map[string]string) | ||||
| 		m["irc"] = val.IRC | ||||
| 		m["mattermost"] = val.Mattermost | ||||
| 		m["xmpp"] = val.Xmpp | ||||
| 		b.Channels = append(b.Channels, m) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bridge) mapIgnores() { | ||||
| 	m := make(map[string][]string) | ||||
| 	m["irc"] = strings.Fields(b.Config.IRC.IgnoreNicks) | ||||
| 	m["mattermost"] = strings.Fields(b.Config.Mattermost.IgnoreNicks) | ||||
| 	m["xmpp"] = strings.Fields(b.Config.Mattermost.IgnoreNicks) | ||||
| 	b.ignoreNicks = m | ||||
| } | ||||
|  | ||||
| func (b *Bridge) getDestChannel(msg *config.Message, dest string) string { | ||||
| 	for _, v := range b.Channels { | ||||
| 		if v[msg.Origin] == msg.Channel { | ||||
| 			return v[dest] | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleMessage(msg config.Message, dest Bridger) { | ||||
| 	if b.ignoreMessage(&msg) { | ||||
| 		return | ||||
| 	} | ||||
| 	if dest.Name() != msg.Origin { | ||||
| 		msg.Channel = b.getDestChannel(&msg, dest.Name()) | ||||
| 		if msg.Channel == "" { | ||||
| 			return | ||||
| 		} | ||||
| 		b.modifyMessage(&msg, dest.Name()) | ||||
| 		dest.Send(msg) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) ignoreMessage(msg *config.Message) bool { | ||||
| 	// should we discard messages ? | ||||
| 	for _, entry := range b.ignoreNicks[msg.Origin] { | ||||
| 		if msg.Username == entry { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func setNickFormat(msg *config.Message, format string) { | ||||
| 	if format == "" { | ||||
| 		msg.Username = msg.Origin + "-" + msg.Username + ": " | ||||
| 		return | ||||
| 	} | ||||
| 	msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1) | ||||
| 	msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) modifyMessage(msg *config.Message, dest string) { | ||||
| 	switch dest { | ||||
| 	case "irc": | ||||
| 		setNickFormat(msg, b.Config.IRC.RemoteNickFormat) | ||||
| 	case "xmpp": | ||||
| 		setNickFormat(msg, b.Config.Xmpp.RemoteNickFormat) | ||||
| 	case "mattermost": | ||||
| 		setNickFormat(msg, b.Config.Mattermost.RemoteNickFormat) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										84
									
								
								bridge/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								bridge/config/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"gopkg.in/gcfg.v1" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| ) | ||||
|  | ||||
| type Message struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	Origin   string | ||||
| } | ||||
|  | ||||
| type Config struct { | ||||
| 	IRC struct { | ||||
| 		UseTLS           bool | ||||
| 		UseSASL          bool | ||||
| 		SkipTLSVerify    bool | ||||
| 		Server           string | ||||
| 		Nick             string | ||||
| 		Password         string | ||||
| 		Channel          string | ||||
| 		NickServNick     string | ||||
| 		NickServPassword string | ||||
| 		RemoteNickFormat string | ||||
| 		IgnoreNicks      string | ||||
| 		Enable           bool | ||||
| 	} | ||||
| 	Mattermost struct { | ||||
| 		URL                    string | ||||
| 		ShowJoinPart           bool | ||||
| 		IconURL                string | ||||
| 		SkipTLSVerify          bool | ||||
| 		BindAddress            string | ||||
| 		Channel                string | ||||
| 		PrefixMessagesWithNick bool | ||||
| 		NicksPerRow            int | ||||
| 		NickFormatter          string | ||||
| 		Server                 string | ||||
| 		Team                   string | ||||
| 		Login                  string | ||||
| 		Password               string | ||||
| 		RemoteNickFormat       string | ||||
| 		IgnoreNicks            string | ||||
| 		NoTLS                  bool | ||||
| 		Enable                 bool | ||||
| 	} | ||||
| 	Xmpp struct { | ||||
| 		Jid              string | ||||
| 		Password         string | ||||
| 		Server           string | ||||
| 		Muc              string | ||||
| 		Nick             string | ||||
| 		RemoteNickFormat string | ||||
| 		Enable           bool | ||||
| 	} | ||||
| 	Channel map[string]*struct { | ||||
| 		IRC        string | ||||
| 		Mattermost string | ||||
| 		Xmpp       string | ||||
| 	} | ||||
| 	General struct { | ||||
| 		GiphyAPIKey string | ||||
| 		Xmpp        bool | ||||
| 		Irc         bool | ||||
| 		Mattermost  bool | ||||
| 		Plus        bool | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewConfig(cfgfile string) *Config { | ||||
| 	var cfg Config | ||||
| 	content, err := ioutil.ReadFile(cfgfile) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	err = gcfg.ReadStringInto(&cfg, string(content)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Failed to parse "+cfgfile+":", err) | ||||
| 	} | ||||
| 	return &cfg | ||||
| } | ||||
							
								
								
									
										59
									
								
								bridge/irc/helper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								bridge/irc/helper.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| package birc | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func tableformatter(nicks []string, nicksPerRow int, continued bool) string { | ||||
| 	result := "|IRC users" | ||||
| 	if continued { | ||||
| 		result = "|(continued)" | ||||
| 	} | ||||
| 	for i := 0; i < 2; i++ { | ||||
| 		for j := 1; j <= nicksPerRow && j <= len(nicks); j++ { | ||||
| 			if i == 0 { | ||||
| 				result += "|" | ||||
| 			} else { | ||||
| 				result += ":-|" | ||||
| 			} | ||||
| 		} | ||||
| 		result += "\r\n|" | ||||
| 	} | ||||
| 	result += nicks[0] + "|" | ||||
| 	for i := 1; i < len(nicks); i++ { | ||||
| 		if i%nicksPerRow == 0 { | ||||
| 			result += "\r\n|" + nicks[i] + "|" | ||||
| 		} else { | ||||
| 			result += nicks[i] + "|" | ||||
| 		} | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func plainformatter(nicks []string, nicksPerRow int) string { | ||||
| 	return strings.Join(nicks, ", ") + " currently on IRC" | ||||
| } | ||||
|  | ||||
| func IsMarkup(message string) bool { | ||||
| 	switch message[0] { | ||||
| 	case '|': | ||||
| 		fallthrough | ||||
| 	case '#': | ||||
| 		fallthrough | ||||
| 	case '_': | ||||
| 		fallthrough | ||||
| 	case '*': | ||||
| 		fallthrough | ||||
| 	case '~': | ||||
| 		fallthrough | ||||
| 	case '-': | ||||
| 		fallthrough | ||||
| 	case ':': | ||||
| 		fallthrough | ||||
| 	case '>': | ||||
| 		fallthrough | ||||
| 	case '=': | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										194
									
								
								bridge/irc/irc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								bridge/irc/irc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| package birc | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	ircm "github.com/sorcix/irc" | ||||
| 	"github.com/thoj/go-ircevent" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| //type Bridge struct { | ||||
| type Birc struct { | ||||
| 	i              *irc.Connection | ||||
| 	ircNick        string | ||||
| 	ircMap         map[string]string | ||||
| 	names          map[string][]string | ||||
| 	ircIgnoreNicks []string | ||||
| 	*config.Config | ||||
| 	Remote chan config.Message | ||||
| } | ||||
|  | ||||
| type FancyLog struct { | ||||
| 	irc *log.Entry | ||||
| } | ||||
|  | ||||
| var flog FancyLog | ||||
|  | ||||
| func init() { | ||||
| 	flog.irc = log.WithFields(log.Fields{"module": "irc"}) | ||||
| } | ||||
|  | ||||
| func New(config *config.Config, c chan config.Message) *Birc { | ||||
| 	b := &Birc{} | ||||
| 	b.Config = config | ||||
| 	b.Remote = c | ||||
| 	b.ircNick = b.Config.IRC.Nick | ||||
| 	b.ircMap = make(map[string]string) | ||||
| 	b.names = make(map[string][]string) | ||||
| 	b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks) | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Birc) Command(msg *config.Message) string { | ||||
| 	switch msg.Text { | ||||
| 	case "!users": | ||||
| 		b.i.SendRaw("NAMES " + msg.Channel) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (b *Birc) Connect() error { | ||||
| 	flog.irc.Info("Trying IRC connection") | ||||
| 	i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick) | ||||
| 	i.UseTLS = b.Config.IRC.UseTLS | ||||
| 	i.UseSASL = b.Config.IRC.UseSASL | ||||
| 	i.SASLLogin = b.Config.IRC.NickServNick | ||||
| 	i.SASLPassword = b.Config.IRC.NickServPassword | ||||
| 	i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify} | ||||
| 	if b.Config.IRC.Password != "" { | ||||
| 		i.Password = b.Config.IRC.Password | ||||
| 	} | ||||
| 	i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection) | ||||
| 	err := i.Connect(b.Config.IRC.Server) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	flog.irc.Info("Connection succeeded") | ||||
| 	b.i = i | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Birc) Name() string { | ||||
| 	return "irc" | ||||
| } | ||||
|  | ||||
| func (b *Birc) Send(msg config.Message) error { | ||||
| 	if msg.Origin == "irc" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if strings.HasPrefix(msg.Text, "!") { | ||||
| 		b.Command(&msg) | ||||
| 		return nil | ||||
| 	} | ||||
| 	b.i.Privmsg(msg.Channel, msg.Username+msg.Text) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Birc) endNames(event *irc.Event) { | ||||
| 	channel := event.Arguments[1] | ||||
| 	sort.Strings(b.names[channel]) | ||||
| 	maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow() | ||||
| 	continued := false | ||||
| 	for len(b.names[channel]) > maxNamesPerPost { | ||||
| 		b.Remote <- config.Message{Username: b.ircNick, Text: b.formatnicks(b.names[channel][0:maxNamesPerPost], continued), Channel: channel, Origin: "irc"} | ||||
| 		b.names[channel] = b.names[channel][maxNamesPerPost:] | ||||
| 		continued = true | ||||
| 	} | ||||
| 	b.Remote <- config.Message{Username: b.ircNick, Text: b.formatnicks(b.names[channel], continued), Channel: channel, Origin: "irc"} | ||||
| 	b.names[channel] = nil | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleNewConnection(event *irc.Event) { | ||||
| 	flog.irc.Info("Registering callbacks") | ||||
| 	i := b.i | ||||
| 	b.ircNick = event.Arguments[0] | ||||
| 	i.AddCallback("PRIVMSG", b.handlePrivMsg) | ||||
| 	i.AddCallback("CTCP_ACTION", b.handlePrivMsg) | ||||
| 	i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime) | ||||
| 	i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames) | ||||
| 	i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames) | ||||
| 	i.AddCallback(ircm.NOTICE, b.handleNotice) | ||||
| 	i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) }) | ||||
| 	i.AddCallback("PING", func(e *irc.Event) { | ||||
| 		i.SendRaw("PONG :" + e.Message()) | ||||
| 		flog.irc.Debugf("PING/PONG") | ||||
| 	}) | ||||
| 	if b.Config.Mattermost.ShowJoinPart { | ||||
| 		i.AddCallback("JOIN", b.handleJoinPart) | ||||
| 		i.AddCallback("PART", b.handleJoinPart) | ||||
| 	} | ||||
| 	i.AddCallback("*", b.handleOther) | ||||
| 	b.setupChannels() | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleJoinPart(event *irc.Event) { | ||||
| 	//b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0])) | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleNotice(event *irc.Event) { | ||||
| 	if strings.Contains(event.Message(), "This nickname is registered") { | ||||
| 		b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleOther(event *irc.Event) { | ||||
| 	flog.irc.Debugf("%#v", event) | ||||
| } | ||||
|  | ||||
| func (b *Birc) handlePrivMsg(event *irc.Event) { | ||||
| 	flog.irc.Debugf("handlePrivMsg() %s %s", event.Nick, event.Message()) | ||||
| 	msg := "" | ||||
| 	if event.Code == "CTCP_ACTION" { | ||||
| 		msg = event.Nick + " " | ||||
| 	} | ||||
| 	msg += event.Message() | ||||
| 	b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Origin: "irc"} | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleTopicWhoTime(event *irc.Event) { | ||||
| 	parts := strings.Split(event.Arguments[2], "!") | ||||
| 	t, err := strconv.ParseInt(event.Arguments[3], 10, 64) | ||||
| 	if err != nil { | ||||
| 		flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3]) | ||||
| 	} | ||||
| 	user := parts[0] | ||||
| 	if len(parts) > 1 { | ||||
| 		user += " [" + parts[1] + "]" | ||||
| 	} | ||||
| 	flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0)) | ||||
| } | ||||
|  | ||||
| func (b *Birc) nicksPerRow() int { | ||||
| 	if b.Config.Mattermost.NicksPerRow < 1 { | ||||
| 		return 4 | ||||
| 	} | ||||
| 	return b.Config.Mattermost.NicksPerRow | ||||
| } | ||||
|  | ||||
| func (b *Birc) setupChannels() { | ||||
| 	for _, val := range b.Config.Channel { | ||||
| 		flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick) | ||||
| 		b.i.Join(val.IRC) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Birc) storeNames(event *irc.Event) { | ||||
| 	channel := event.Arguments[2] | ||||
| 	b.names[channel] = append( | ||||
| 		b.names[channel], | ||||
| 		strings.Split(strings.TrimSpace(event.Message()), " ")...) | ||||
| } | ||||
|  | ||||
| func (b *Birc) formatnicks(nicks []string, continued bool) string { | ||||
| 	switch b.Config.Mattermost.NickFormatter { | ||||
| 	case "table": | ||||
| 		return tableformatter(nicks, b.nicksPerRow(), continued) | ||||
| 	default: | ||||
| 		return plainformatter(nicks, b.nicksPerRow()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										174
									
								
								bridge/mattermost/mattermost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								bridge/mattermost/mattermost.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| package bmattermost | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/matterclient" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| //type Bridge struct { | ||||
| type MMhook struct { | ||||
| 	mh *matterhook.Client | ||||
| } | ||||
|  | ||||
| type MMapi struct { | ||||
| 	mc            *matterclient.MMClient | ||||
| 	mmMap         map[string]string | ||||
| 	mmIgnoreNicks []string | ||||
| } | ||||
|  | ||||
| type MMMessage struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| } | ||||
|  | ||||
| type Bmattermost struct { | ||||
| 	MMhook | ||||
| 	MMapi | ||||
| 	*config.Config | ||||
| 	Plus   bool | ||||
| 	Remote chan config.Message | ||||
| } | ||||
|  | ||||
| type FancyLog struct { | ||||
| 	irc  *log.Entry | ||||
| 	mm   *log.Entry | ||||
| 	xmpp *log.Entry | ||||
| } | ||||
|  | ||||
| var flog FancyLog | ||||
|  | ||||
| const Legacy = "legacy" | ||||
|  | ||||
| func init() { | ||||
| 	flog.irc = log.WithFields(log.Fields{"module": "irc"}) | ||||
| 	flog.mm = log.WithFields(log.Fields{"module": "mattermost"}) | ||||
| 	flog.xmpp = log.WithFields(log.Fields{"module": "xmpp"}) | ||||
| } | ||||
|  | ||||
| func New(cfg *config.Config, c chan config.Message) *Bmattermost { | ||||
| 	b := &Bmattermost{} | ||||
| 	b.Config = cfg | ||||
| 	b.Remote = c | ||||
| 	b.Plus = cfg.General.Plus | ||||
| 	b.mmMap = make(map[string]string) | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) Command(cmd string) string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) Connect() error { | ||||
| 	if !b.Plus { | ||||
| 		b.mh = matterhook.New(b.Config.Mattermost.URL, | ||||
| 			matterhook.Config{InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify, | ||||
| 				BindAddress: b.Config.Mattermost.BindAddress}) | ||||
| 	} else { | ||||
| 		b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password, | ||||
| 			b.Config.Mattermost.Team, b.Config.Mattermost.Server) | ||||
| 		b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify | ||||
| 		b.mc.NoTLS = b.Config.Mattermost.NoTLS | ||||
| 		flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server) | ||||
| 		err := b.mc.Login() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		flog.mm.Info("Login ok") | ||||
| 		b.mc.JoinChannel(b.Config.Mattermost.Channel) | ||||
| 		for _, val := range b.Config.Channel { | ||||
| 			b.mc.JoinChannel(val.Mattermost) | ||||
| 		} | ||||
| 		go b.mc.WsReceiver() | ||||
| 	} | ||||
| 	go b.handleMatter() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) Name() string { | ||||
| 	return "mattermost" | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) Send(msg config.Message) error { | ||||
| 	flog.mm.Infof("mattermost send %#v", msg) | ||||
| 	if msg.Origin != "mattermost" { | ||||
| 		return b.SendType(msg.Username, msg.Text, msg.Channel, "") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) SendType(nick string, message string, channel string, mtype string) error { | ||||
| 	if b.Config.Mattermost.PrefixMessagesWithNick { | ||||
| 		/*if IsMarkup(message) { | ||||
| 			message = nick + "\n\n" + message | ||||
| 		} else { | ||||
| 		*/ | ||||
| 		message = nick + " " + message | ||||
| 		//} | ||||
| 	} | ||||
| 	if !b.Plus { | ||||
| 		matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL} | ||||
| 		matterMessage.Channel = channel | ||||
| 		matterMessage.UserName = nick | ||||
| 		matterMessage.Type = mtype | ||||
| 		matterMessage.Text = message | ||||
| 		err := b.mh.Send(matterMessage) | ||||
| 		if err != nil { | ||||
| 			flog.mm.Info(err) | ||||
| 			return err | ||||
| 		} | ||||
| 		flog.mm.Debug("->mattermost channel: ", channel, " ", message) | ||||
| 		return nil | ||||
| 	} | ||||
| 	flog.mm.Debug("->mattermost channel plus: ", channel, " ", message) | ||||
| 	b.mc.PostMessage(b.mc.GetChannelId(channel, ""), message) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) handleMatter() { | ||||
| 	flog.mm.Infof("Choosing API based Mattermost connection: %t", b.Plus) | ||||
| 	mchan := make(chan *MMMessage) | ||||
| 	if b.Plus { | ||||
| 		go b.handleMatterClient(mchan) | ||||
| 	} else { | ||||
| 		go b.handleMatterHook(mchan) | ||||
| 	} | ||||
| 	flog.mm.Info("Start listening for Mattermost messages") | ||||
| 	for message := range mchan { | ||||
| 		texts := strings.Split(message.Text, "\n") | ||||
| 		for _, text := range texts { | ||||
| 			flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel) | ||||
| 			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Origin: "mattermost"} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) { | ||||
| 	for message := range b.mc.MessageChan { | ||||
| 		// do not post our own messages back to irc | ||||
| 		if message.Raw.Event == "posted" && b.mc.User.Username != message.Username { | ||||
| 			flog.mm.Debugf("receiving from matterclient %#v", message) | ||||
| 			flog.mm.Debugf("receiving from matterclient %#v", message.Raw) | ||||
| 			m := &MMMessage{} | ||||
| 			m.Username = message.Username | ||||
| 			m.Channel = message.Channel | ||||
| 			m.Text = message.Text | ||||
| 			mchan <- m | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmattermost) handleMatterHook(mchan chan *MMMessage) { | ||||
| 	for { | ||||
| 		message := b.mh.Receive() | ||||
| 		flog.mm.Debugf("receiving from matterhook %#v", message) | ||||
| 		m := &MMMessage{} | ||||
| 		m.Username = message.UserName | ||||
| 		m.Text = message.Text | ||||
| 		m.Channel = message.ChannelName | ||||
| 		mchan <- m | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										133
									
								
								bridge/xmpp/xmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								bridge/xmpp/xmpp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| package bxmpp | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/mattn/go-xmpp" | ||||
|  | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type Bxmpp struct { | ||||
| 	xc      *xmpp.Client | ||||
| 	xmppMap map[string]string | ||||
| 	*config.Config | ||||
| 	Remote chan config.Message | ||||
| } | ||||
|  | ||||
| type FancyLog struct { | ||||
| 	xmpp *log.Entry | ||||
| } | ||||
|  | ||||
| type Message struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| } | ||||
|  | ||||
| var flog FancyLog | ||||
|  | ||||
| func init() { | ||||
| 	flog.xmpp = log.WithFields(log.Fields{"module": "xmpp"}) | ||||
| } | ||||
|  | ||||
| func New(config *config.Config, c chan config.Message) *Bxmpp { | ||||
| 	b := &Bxmpp{} | ||||
| 	b.xmppMap = make(map[string]string) | ||||
| 	b.Config = config | ||||
| 	b.Remote = c | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) Connect() error { | ||||
| 	var err error | ||||
| 	flog.xmpp.Info("Trying XMPP connection") | ||||
| 	b.xc, err = b.createXMPP() | ||||
| 	if err != nil { | ||||
| 		flog.xmpp.Debugf("%#v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	flog.xmpp.Info("Connection succeeded") | ||||
| 	b.setupChannels() | ||||
| 	go b.handleXmpp() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) Name() string { | ||||
| 	return "xmpp" | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) Send(msg config.Message) error { | ||||
| 	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Xmpp.Muc, Text: msg.Username + msg.Text}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) createXMPP() (*xmpp.Client, error) { | ||||
| 	options := xmpp.Options{ | ||||
| 		Host:     b.Config.Xmpp.Server, | ||||
| 		User:     b.Config.Xmpp.Jid, | ||||
| 		Password: b.Config.Xmpp.Password, | ||||
| 		NoTLS:    true, | ||||
| 		StartTLS: true, | ||||
| 		//StartTLS:      false, | ||||
| 		Debug:                        true, | ||||
| 		Session:                      true, | ||||
| 		Status:                       "", | ||||
| 		StatusMessage:                "", | ||||
| 		Resource:                     "", | ||||
| 		InsecureAllowUnencryptedAuth: false, | ||||
| 		//InsecureAllowUnencryptedAuth: true, | ||||
| 	} | ||||
| 	var err error | ||||
| 	b.xc, err = options.NewClient() | ||||
| 	return b.xc, err | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) setupChannels() { | ||||
| 	for _, val := range b.Config.Channel { | ||||
| 		flog.xmpp.Infof("Joining %s as %s", val.Xmpp, b.Xmpp.Nick) | ||||
| 		b.xc.JoinMUCNoHistory(val.Xmpp+"@"+b.Xmpp.Muc, b.Xmpp.Nick) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) xmppKeepAlive() { | ||||
| 	go func() { | ||||
| 		ticker := time.NewTicker(90 * time.Second) | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-ticker.C: | ||||
| 				b.xc.Send(xmpp.Chat{}) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) handleXmpp() error { | ||||
| 	for { | ||||
| 		m, err := b.xc.Recv() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		switch v := m.(type) { | ||||
| 		case xmpp.Chat: | ||||
| 			var channel, nick string | ||||
| 			if v.Type == "groupchat" { | ||||
| 				s := strings.Split(v.Remote, "@") | ||||
| 				if len(s) == 2 { | ||||
| 					channel = s[0] | ||||
| 				} | ||||
| 				s = strings.Split(s[1], "/") | ||||
| 				if len(s) == 2 { | ||||
| 					nick = s[1] | ||||
| 				} | ||||
| 				if nick != b.Xmpp.Nick { | ||||
| 					flog.xmpp.Infof("sending message to remote %s %s %s", nick, v.Text, channel) | ||||
| 					b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Origin: "xmpp"} | ||||
| 				} | ||||
| 			} | ||||
| 		case xmpp.Presence: | ||||
| 			// do nothing | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										19
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # v0.6.0 | ||||
| ## Breaking changes from 0.5 to 0.6 | ||||
| ### commandline | ||||
| * -plus switch deprecated. Use ```Plus=true``` or ```Plus``` in ```[general]``` section | ||||
| ### IRC section | ||||
| * ```Enabled``` added (default false) | ||||
| Add ```Enabled=true``` or ```Enabled``` to the ```[IRC]``` section if you want to enable the IRC bridge | ||||
| ### Mattermost section | ||||
| * ```Enabled``` added (default false) | ||||
| Add ```Enabled=true``` or ```Enabled``` to the ```[mattermost]``` section if you want to enable the mattermostbridge | ||||
| ### General section | ||||
| * Use ```Plus=true``` or ```Plus``` in ```[general]``` section to enable the API version of matterbridge | ||||
|  | ||||
| ## New features | ||||
| * Matterbridge now bridges between any specified protocol (not only mattermost anymore)  | ||||
| * XMPP support added.  See matterbridge.conf.sample for more information | ||||
| * RemoteNickFormat {BRIDGE} variable added | ||||
| You can now add the originating bridge to ```RemoteNickFormat```   | ||||
| eg ```RemoteNickFormat="[{BRIDGE}] <{NICK}> "``` | ||||
							
								
								
									
										35
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								config.go
									
									
									
									
									
								
							| @@ -1,35 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"gopkg.in/gcfg.v1" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	IRC struct { | ||||
| 		UseTLS        bool | ||||
| 		SkipTLSVerify bool | ||||
| 		Server        string | ||||
| 		Port          int | ||||
| 		Nick          string | ||||
| 		Channel       string | ||||
| 	} | ||||
| 	Mattermost struct { | ||||
| 		URL  string | ||||
| 		Port int | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewConfig(cfgfile string) *Config { | ||||
| 	var cfg Config | ||||
| 	content, err := ioutil.ReadFile(cfgfile) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	err = gcfg.ReadStringInto(&cfg, string(content)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Failed to parse "+cfgfile+":", err) | ||||
| 	} | ||||
| 	return &cfg | ||||
| } | ||||
| @@ -1,11 +1,190 @@ | ||||
| #This is configuration for matterbridge. | ||||
| ################################################################### | ||||
| #IRC section | ||||
| ################################################################### | ||||
| [IRC] | ||||
| server="irc.freenode.net" | ||||
| port=6667 | ||||
| #Enable enables this bridge | ||||
| #OPTIONAL (default false) | ||||
| Enable=true | ||||
| #irc server to connect to.  | ||||
| #REQUIRED | ||||
| Server="irc.freenode.net:6667" | ||||
|  | ||||
| #Enable to use TLS connection to your irc server.  | ||||
| #OPTIONAL (default false) | ||||
| UseTLS=false | ||||
|  | ||||
| #Enable SASL (PLAIN) authentication. (freenode requires this from eg AWS hosts) | ||||
| #It uses NickServNick and NickServPassword as login and password | ||||
| #OPTIONAL (default false) | ||||
| UseSASL=false | ||||
|  | ||||
| #Enable to not verify the certificate on your irc server. i | ||||
| #e.g. when using selfsigned certificates | ||||
| #OPTIONAL (default false) | ||||
| SkipTLSVerify=true | ||||
| nick="matterbot" | ||||
| channel="#matterbridge" | ||||
|  | ||||
| #Your nick on irc.  | ||||
| #REQUIRED | ||||
| Nick="matterbot" | ||||
|  | ||||
| #If you registered your bot with a service like Nickserv on freenode.  | ||||
| #Also being used when UseSASL=true | ||||
| #OPTIONAL | ||||
| NickServNick="nickserv" | ||||
| NickServPassword="secret" | ||||
|  | ||||
| #RemoteNickFormat defines how Mattermost users appear on irc | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #OPTIONAL (default NICK:) | ||||
| RemoteNickFormat="{NICK}: " | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to mattermost. | ||||
| #OPTIONAL | ||||
| IgnoreNicks="ircspammer1 ircspammer2" | ||||
|  | ||||
| ################################################################### | ||||
| #XMPP section | ||||
| ################################################################### | ||||
| [XMPP] | ||||
| #Enable enables this bridge | ||||
| #OPTIONAL (default false) | ||||
| Enable=true | ||||
|  | ||||
| #xmpp server to connect to.  | ||||
| #REQUIRED | ||||
| Server="jabber.example.com:5222" | ||||
|  | ||||
| #Jid | ||||
| #REQUIRED | ||||
| Jid="user@example.com" | ||||
|  | ||||
| #Password | ||||
| #REQUIRED | ||||
| Password="yourpass" | ||||
|  | ||||
| #MUC | ||||
| #REQUIRED | ||||
| Muc="conference.jabber.example.com" | ||||
|  | ||||
| #Your nick in the rooms | ||||
| #REQUIRED | ||||
| Nick="xmppbot" | ||||
|  | ||||
|  | ||||
| ################################################################### | ||||
| #mattermost section | ||||
| ################################################################### | ||||
|  | ||||
| [mattermost] | ||||
| url="http://yourdomain/hooks/yourhookkey" | ||||
| port=9999 | ||||
| #Enable enables this bridge | ||||
| #OPTIONAL (default false) | ||||
| Enable=true | ||||
|  | ||||
| #### Settings for webhook matterbridge. | ||||
| #### These settings will not be used when using -plus switch which doesn't use  | ||||
| #### webhooks. | ||||
|  | ||||
| #Url is your incoming webhook url as specified in mattermost.  | ||||
| #See account settings - integrations - incoming webhooks on mattermost. | ||||
| #REQUIRED | ||||
| URL="https://yourdomain/hooks/yourhookkey" | ||||
|  | ||||
| #Address to listen on for outgoing webhook requests from mattermost. | ||||
| #See account settings - integrations - outgoing webhooks on mattermost. | ||||
| #This setting will not be used when using -plus switch which doesn't use  | ||||
| #webhooks | ||||
| #REQUIRED | ||||
| BindAddress="0.0.0.0:9999" | ||||
|  | ||||
| #Icon that will be showed in mattermost.  | ||||
| #OPTIONAL | ||||
| IconURL="http://youricon.png" | ||||
|  | ||||
| #### Settings for matterbridge -plus | ||||
| #### Thse settings will only be used when using the -plus switch. | ||||
|  | ||||
| #The mattermost hostname.  | ||||
| #REQUIRED | ||||
| Server="yourmattermostserver.domain" | ||||
|  | ||||
| #Your team on mattermost.  | ||||
| #REQUIRED | ||||
| Team="yourteam" | ||||
|  | ||||
| #login/pass of your bot.  | ||||
| #Use a dedicated user for this and not your own!  | ||||
| #REQUIRED | ||||
| Login="yourlogin" | ||||
| Password="yourpass" | ||||
|  | ||||
| #Enable this to make a http connection (instead of https) to your mattermost.  | ||||
| #OPTIONAL (default false) | ||||
| NoTLS=false | ||||
|  | ||||
| #### Shared settings for matterbridge and -plus | ||||
|  | ||||
| #Enable to not verify the certificate on your mattermost server.  | ||||
| #e.g. when using selfsigned certificates | ||||
| #OPTIONAL (default false) | ||||
| SkipTLSVerify=true | ||||
|  | ||||
| #Enable to show IRC joins/parts in mattermost.  | ||||
| #OPTIONAL (default false) | ||||
| ShowJoinPart=false | ||||
|  | ||||
| #Whether to prefix messages from IRC to mattermost with the sender's nick.  | ||||
| #Useful if username overrides for incoming webhooks isn't enabled on the  | ||||
| #mattermost server. If you set PrefixMessagesWithNick to true, each message  | ||||
| #from IRC to Mattermost will by default be prefixed by "irc-" + nick. You can,  | ||||
| #however, modify how the messages appear, by setting (and modifying) RemoteNickFormat  | ||||
| #OPTIONAL (default false) | ||||
| PrefixMessagesWithNick=false | ||||
|  | ||||
| #RemoteNickFormat defines how IRC users appear on Mattermost.  | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #OPTIONAL (default irc-NICK) | ||||
| RemoteNickFormat="irc-{NICK}" | ||||
|  | ||||
| #how to format the list of IRC nicks when displayed in mattermost.  | ||||
| #Possible options are "table" and "plain" | ||||
| #OPTIONAL (default plain) | ||||
| NickFormatter=plain | ||||
| #How many nicks to list per row for formatters that support this.  | ||||
| #OPTIONAL (default 4) | ||||
| NicksPerRow=4 | ||||
|  | ||||
| #Nicks you want to ignore. Messages from those users will not be sent to IRC.  | ||||
| #OPTIONAL  | ||||
| IgnoreNicks="mmbot spammer2" | ||||
|  | ||||
| ################################################################### | ||||
| #multiple channel config | ||||
| ################################################################### | ||||
| #You can specify multiple channels.  | ||||
| #The name is just an identifier for you. | ||||
| #REQUIRED (at least 1 channel) | ||||
| [Channel "channel1"]  | ||||
| #Choose the IRC channel to send mattermost messages to. | ||||
| IRC="#off-topic" | ||||
| #Choose the mattermost channel to send IRC messages to. | ||||
| mattermost="off-topic" | ||||
| #Choose the mattermost channel to send IRC messages to. | ||||
| xmpp="off-topic" | ||||
|  | ||||
| [Channel "testchannel"] | ||||
| IRC="#testing" | ||||
| mattermost="testing" | ||||
| xmpp="testing" | ||||
|  | ||||
| ################################################################### | ||||
| #general | ||||
| ################################################################### | ||||
| [general] | ||||
| #request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key.  | ||||
| #OPTIONAL | ||||
| GiphyApiKey="dc6zaTOxFJmzC" | ||||
|  | ||||
| #Enabling plus means you'll use the API version instead of the webhooks one | ||||
| Plus=false | ||||
|   | ||||
| @@ -1,56 +1,41 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	"github.com/thoj/go-ircevent" | ||||
| 	"log" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| type Bridge struct { | ||||
| 	i *irc.Connection | ||||
| 	m *matterhook.Client | ||||
| 	*Config | ||||
| } | ||||
| var version = "0.6.0-beta1" | ||||
|  | ||||
| func NewBridge(name string, config *Config) *Bridge { | ||||
| 	b := &Bridge{} | ||||
| 	b.Config = config | ||||
| 	b.m = matterhook.New(b.Config.Mattermost.URL, matterhook.Config{Port: b.Config.Mattermost.Port}) | ||||
| 	b.i = b.createIRC(name) | ||||
| 	go b.handleMatter() | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bridge) createIRC(name string) *irc.Connection { | ||||
| 	i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick) | ||||
| 	i.UseTLS = b.Config.IRC.UseTLS | ||||
| 	i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify} | ||||
| 	i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port)) | ||||
| 	time.Sleep(time.Second) | ||||
| 	log.Println("Joining", b.Config.IRC.Channel, "as", b.Config.IRC.Nick) | ||||
| 	i.Join(b.Config.IRC.Channel) | ||||
| 	i.AddCallback("PRIVMSG", b.handlePrivMsg) | ||||
| 	return i | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handlePrivMsg(event *irc.Event) { | ||||
| 	matterMessage := matterhook.OMessage{} | ||||
| 	matterMessage.Text = event.Message() | ||||
| 	matterMessage.UserName = "irc-" + event.Nick | ||||
| 	b.m.Send(matterMessage) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleMatter() { | ||||
| 	for { | ||||
| 		message := b.m.Receive() | ||||
| 		b.i.Privmsg(b.Config.IRC.Channel, message.UserName+": "+message.Text) | ||||
| 	} | ||||
| func init() { | ||||
| 	log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	NewBridge("matterbot", NewConfig("matterbridge.conf")) | ||||
| 	select {} | ||||
| 	flagConfig := flag.String("conf", "matterbridge.conf", "config file") | ||||
| 	flagDebug := flag.Bool("debug", false, "enable debug") | ||||
| 	flagVersion := flag.Bool("version", false, "show version") | ||||
| 	flagPlus := flag.Bool("plus", false, "running using API instead of webhooks (deprecated, set Plus flag in [general] config)") | ||||
| 	flag.Parse() | ||||
| 	if *flagVersion { | ||||
| 		fmt.Println("version:", version) | ||||
| 		return | ||||
| 	} | ||||
| 	flag.Parse() | ||||
| 	if *flagDebug { | ||||
| 		log.Info("enabling debug") | ||||
| 		log.SetLevel(log.DebugLevel) | ||||
| 	} | ||||
| 	fmt.Println("running version", version) | ||||
| 	cfg := config.NewConfig(*flagConfig) | ||||
| 	if *flagPlus { | ||||
| 		cfg.General.Plus = true | ||||
| 	} | ||||
| 	err := bridge.NewBridge(cfg) | ||||
| 	if err != nil { | ||||
| 		log.Debugf("starting bridge failed %#v", err) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										642
									
								
								matterclient/matterclient.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										642
									
								
								matterclient/matterclient.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,642 @@ | ||||
| package matterclient | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"net/http/cookiejar" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/jpillora/backoff" | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type Credentials struct { | ||||
| 	Login         string | ||||
| 	Team          string | ||||
| 	Pass          string | ||||
| 	Server        string | ||||
| 	NoTLS         bool | ||||
| 	SkipTLSVerify bool | ||||
| } | ||||
|  | ||||
| type Message struct { | ||||
| 	Raw      *model.WebSocketEvent | ||||
| 	Post     *model.Post | ||||
| 	Team     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	Text     string | ||||
| } | ||||
|  | ||||
| type Team struct { | ||||
| 	Team         *model.Team | ||||
| 	Id           string | ||||
| 	Channels     *model.ChannelList | ||||
| 	MoreChannels *model.ChannelList | ||||
| 	Users        map[string]*model.User | ||||
| } | ||||
|  | ||||
| type MMClient struct { | ||||
| 	sync.RWMutex | ||||
| 	*Credentials | ||||
| 	Team        *Team | ||||
| 	OtherTeams  []*Team | ||||
| 	Client      *model.Client | ||||
| 	User        *model.User | ||||
| 	Users       map[string]*model.User | ||||
| 	MessageChan chan *Message | ||||
| 	log         *log.Entry | ||||
| 	WsClient    *websocket.Conn | ||||
| 	WsQuit      bool | ||||
| 	WsAway      bool | ||||
| 	WsConnected bool | ||||
| 	WsSequence  int64 | ||||
| 	WsPingChan  chan *model.WebSocketResponse | ||||
| } | ||||
|  | ||||
| func New(login, pass, team, server string) *MMClient { | ||||
| 	cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server} | ||||
| 	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)} | ||||
| 	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"}) | ||||
| 	log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) | ||||
| 	return mmclient | ||||
| } | ||||
|  | ||||
| func (m *MMClient) SetLogLevel(level string) { | ||||
| 	l, err := log.ParseLevel(level) | ||||
| 	if err != nil { | ||||
| 		log.SetLevel(log.InfoLevel) | ||||
| 		return | ||||
| 	} | ||||
| 	log.SetLevel(l) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) Login() error { | ||||
| 	m.WsConnected = false | ||||
| 	if m.WsQuit { | ||||
| 		return nil | ||||
| 	} | ||||
| 	b := &backoff.Backoff{ | ||||
| 		Min:    time.Second, | ||||
| 		Max:    5 * time.Minute, | ||||
| 		Jitter: true, | ||||
| 	} | ||||
| 	uriScheme := "https://" | ||||
| 	wsScheme := "wss://" | ||||
| 	if m.NoTLS { | ||||
| 		uriScheme = "http://" | ||||
| 		wsScheme = "ws://" | ||||
| 	} | ||||
| 	// login to mattermost | ||||
| 	m.Client = model.NewClient(uriScheme + m.Credentials.Server) | ||||
| 	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}} | ||||
| 	var myinfo *model.Result | ||||
| 	var appErr *model.AppError | ||||
| 	var logmsg = "trying login" | ||||
| 	for { | ||||
| 		m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server) | ||||
| 		if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) { | ||||
| 			m.log.Debugf(logmsg+" with %s", model.SESSION_COOKIE_TOKEN) | ||||
| 			token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=") | ||||
| 			if len(token) != 2 { | ||||
| 				return errors.New("incorrect MMAUTHTOKEN. valid input is MMAUTHTOKEN=yourtoken") | ||||
| 			} | ||||
| 			m.Client.HttpClient.Jar = m.createCookieJar(token[1]) | ||||
| 			m.Client.MockSession(token[1]) | ||||
| 			myinfo, appErr = m.Client.GetMe("") | ||||
| 			if appErr != nil { | ||||
| 				return errors.New(appErr.DetailedError) | ||||
| 			} | ||||
| 			if myinfo.Data.(*model.User) == nil { | ||||
| 				m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass) | ||||
| 				return errors.New("invalid " + model.SESSION_COOKIE_TOKEN) | ||||
| 			} | ||||
| 		} else { | ||||
| 			myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass) | ||||
| 		} | ||||
| 		if appErr != nil { | ||||
| 			d := b.Duration() | ||||
| 			m.log.Debug(appErr.DetailedError) | ||||
| 			if !strings.Contains(appErr.DetailedError, "connection refused") && | ||||
| 				!strings.Contains(appErr.DetailedError, "invalid character") { | ||||
| 				if appErr.Message == "" { | ||||
| 					return errors.New(appErr.DetailedError) | ||||
| 				} | ||||
| 				return errors.New(appErr.Message) | ||||
| 			} | ||||
| 			m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d) | ||||
| 			time.Sleep(d) | ||||
| 			logmsg = "retrying login" | ||||
| 			continue | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	// reset timer | ||||
| 	b.Reset() | ||||
|  | ||||
| 	err := m.initUser() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if m.Team == nil { | ||||
| 		return errors.New("team not found") | ||||
| 	} | ||||
| 	// set our team id as default route | ||||
| 	m.Client.SetTeamId(m.Team.Id) | ||||
|  | ||||
| 	// setup websocket connection | ||||
| 	wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX + "/users/websocket" | ||||
| 	header := http.Header{} | ||||
| 	header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken) | ||||
|  | ||||
| 	m.log.Debug("WsClient: making connection") | ||||
| 	for { | ||||
| 		wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}} | ||||
| 		m.WsClient, _, err = wsDialer.Dial(wsurl, header) | ||||
| 		if err != nil { | ||||
| 			d := b.Duration() | ||||
| 			m.log.Debugf("WSS: %s, reconnecting in %s", err, d) | ||||
| 			time.Sleep(d) | ||||
| 			continue | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	b.Reset() | ||||
|  | ||||
| 	m.WsSequence = 1 | ||||
| 	m.WsPingChan = make(chan *model.WebSocketResponse) | ||||
| 	// only start to parse WS messages when login is completely done | ||||
| 	m.WsConnected = true | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) Logout() error { | ||||
| 	m.log.Debugf("logout as %s (team: %s) on %s", m.Credentials.Login, m.Credentials.Team, m.Credentials.Server) | ||||
| 	m.WsQuit = true | ||||
| 	m.WsClient.Close() | ||||
| 	m.WsClient.UnderlyingConn().Close() | ||||
| 	m.WsClient = nil | ||||
| 	_, err := m.Client.Logout() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) WsReceiver() { | ||||
| 	for { | ||||
| 		var rawMsg json.RawMessage | ||||
| 		var err error | ||||
|  | ||||
| 		if !m.WsConnected { | ||||
| 			continue | ||||
| 		} | ||||
| 		if m.WsQuit { | ||||
| 			m.log.Debug("exiting WsReceiver") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if _, rawMsg, err = m.WsClient.ReadMessage(); err != nil { | ||||
| 			m.log.Error("error:", err) | ||||
| 			// reconnect | ||||
| 			m.Login() | ||||
| 		} | ||||
|  | ||||
| 		var event model.WebSocketEvent | ||||
| 		if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() { | ||||
| 			m.log.Debugf("WsReceiver: %#v", event) | ||||
| 			msg := &Message{Raw: &event, Team: m.Credentials.Team} | ||||
| 			m.parseMessage(msg) | ||||
| 			m.MessageChan <- msg | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		var response model.WebSocketResponse | ||||
| 		if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() { | ||||
| 			m.log.Debugf("WsReceiver: %#v", response) | ||||
| 			m.parseResponse(response) | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) parseMessage(rmsg *Message) { | ||||
| 	switch rmsg.Raw.Event { | ||||
| 	case model.WEBSOCKET_EVENT_POSTED: | ||||
| 		m.parseActionPost(rmsg) | ||||
| 		/* | ||||
| 			case model.ACTION_USER_REMOVED: | ||||
| 				m.handleWsActionUserRemoved(&rmsg) | ||||
| 			case model.ACTION_USER_ADDED: | ||||
| 				m.handleWsActionUserAdded(&rmsg) | ||||
| 		*/ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) parseResponse(rmsg model.WebSocketResponse) { | ||||
| 	if rmsg.Data != nil { | ||||
| 		// ping reply | ||||
| 		if rmsg.Data["text"].(string) == "pong" { | ||||
| 			m.WsPingChan <- &rmsg | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) parseActionPost(rmsg *Message) { | ||||
| 	data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string))) | ||||
| 	// we don't have the user, refresh the userlist | ||||
| 	if m.GetUser(data.UserId) == nil { | ||||
| 		m.UpdateUsers() | ||||
| 	} | ||||
| 	rmsg.Username = m.GetUser(data.UserId).Username | ||||
| 	rmsg.Channel = m.GetChannelName(data.ChannelId) | ||||
| 	rmsg.Team = m.GetTeamName(rmsg.Raw.TeamId) | ||||
| 	// direct message | ||||
| 	if rmsg.Raw.Data["channel_type"] == "D" { | ||||
| 		rmsg.Channel = m.GetUser(data.UserId).Username | ||||
| 	} | ||||
| 	rmsg.Text = data.Message | ||||
| 	rmsg.Post = data | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateUsers() error { | ||||
| 	mmusers, err := m.Client.GetProfilesForDirectMessageList(m.Team.Id) | ||||
| 	if err != nil { | ||||
| 		return errors.New(err.DetailedError) | ||||
| 	} | ||||
| 	m.Lock() | ||||
| 	m.Users = mmusers.Data.(map[string]*model.User) | ||||
| 	m.Unlock() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateChannels() error { | ||||
| 	mmchannels, err := m.Client.GetChannels("") | ||||
| 	if err != nil { | ||||
| 		return errors.New(err.DetailedError) | ||||
| 	} | ||||
| 	mmchannels2, err := m.Client.GetMoreChannels("") | ||||
| 	if err != nil { | ||||
| 		return errors.New(err.DetailedError) | ||||
| 	} | ||||
| 	m.Lock() | ||||
| 	m.Team.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 	m.Team.MoreChannels = mmchannels2.Data.(*model.ChannelList) | ||||
| 	m.Unlock() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelName(channelId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { | ||||
| 			if channel.Id == channelId { | ||||
| 				return channel.Name | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelId(name string, teamId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	if teamId == "" { | ||||
| 		teamId = m.Team.Id | ||||
| 	} | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		if t.Id == teamId { | ||||
| 			for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { | ||||
| 				if channel.Name == name { | ||||
| 					return channel.Id | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelHeader(channelId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { | ||||
| 			if channel.Id == channelId { | ||||
| 				return channel.Header | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) PostMessage(channelId string, text string) { | ||||
| 	post := &model.Post{ChannelId: channelId, Message: text} | ||||
| 	m.Client.CreatePost(post) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) JoinChannel(channelId string) error { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, c := range m.Team.Channels.Channels { | ||||
| 		if c.Id == channelId { | ||||
| 			m.log.Debug("Not joining ", channelId, " already joined.") | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	m.log.Debug("Joining ", channelId) | ||||
| 	_, err := m.Client.JoinChannel(channelId) | ||||
| 	if err != nil { | ||||
| 		return errors.New("failed to join") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList { | ||||
| 	res, err := m.Client.GetPostsSince(channelId, time) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) SearchPosts(query string) *model.PostList { | ||||
| 	res, err := m.Client.SearchPosts(query, false) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { | ||||
| 	res, err := m.Client.GetPosts(channelId, 0, limit, "") | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPublicLink(filename string) string { | ||||
| 	res, err := m.Client.GetPublicLink(filename) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return res.Data.(string) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPublicLinks(filenames []string) []string { | ||||
| 	var output []string | ||||
| 	for _, f := range filenames { | ||||
| 		res, err := m.Client.GetPublicLink(f) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		output = append(output, res.Data.(string)) | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateChannelHeader(channelId string, header string) { | ||||
| 	data := make(map[string]string) | ||||
| 	data["channel_id"] = channelId | ||||
| 	data["channel_header"] = header | ||||
| 	m.log.Debugf("updating channelheader %#v, %#v", channelId, header) | ||||
| 	_, err := m.Client.UpdateChannelHeader(data) | ||||
| 	if err != nil { | ||||
| 		log.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateLastViewed(channelId string) { | ||||
| 	m.log.Debugf("posting lastview %#v", channelId) | ||||
| 	_, err := m.Client.UpdateLastViewedAt(channelId) | ||||
| 	if err != nil { | ||||
| 		m.log.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UsernamesInChannel(channelId string) []string { | ||||
| 	ceiRes, err := m.Client.GetChannelExtraInfo(channelId, 5000, "") | ||||
| 	if err != nil { | ||||
| 		m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err) | ||||
| 		return []string{} | ||||
| 	} | ||||
| 	extra := ceiRes.Data.(*model.ChannelExtra) | ||||
| 	result := []string{} | ||||
| 	for _, member := range extra.Members { | ||||
| 		result = append(result, member.Username) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func (m *MMClient) createCookieJar(token string) *cookiejar.Jar { | ||||
| 	var cookies []*http.Cookie | ||||
| 	jar, _ := cookiejar.New(nil) | ||||
| 	firstCookie := &http.Cookie{ | ||||
| 		Name:   "MMAUTHTOKEN", | ||||
| 		Value:  token, | ||||
| 		Path:   "/", | ||||
| 		Domain: m.Credentials.Server, | ||||
| 	} | ||||
| 	cookies = append(cookies, firstCookie) | ||||
| 	cookieURL, _ := url.Parse("https://" + m.Credentials.Server) | ||||
| 	jar.SetCookies(cookieURL, cookies) | ||||
| 	return jar | ||||
| } | ||||
|  | ||||
| // SendDirectMessage sends a direct message to specified user | ||||
| func (m *MMClient) SendDirectMessage(toUserId string, msg string) { | ||||
| 	m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg) | ||||
| 	// create DM channel (only happens on first message) | ||||
| 	_, err := m.Client.CreateDirectChannel(toUserId) | ||||
| 	if err != nil { | ||||
| 		m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err) | ||||
| 	} | ||||
| 	channelName := model.GetDMNameFromIds(toUserId, m.User.Id) | ||||
|  | ||||
| 	// update our channels | ||||
| 	mmchannels, _ := m.Client.GetChannels("") | ||||
| 	m.Lock() | ||||
| 	m.Team.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 	m.Unlock() | ||||
|  | ||||
| 	// build & send the message | ||||
| 	msg = strings.Replace(msg, "\r", "", -1) | ||||
| 	post := &model.Post{ChannelId: m.GetChannelId(channelName, ""), Message: msg} | ||||
| 	m.Client.CreatePost(post) | ||||
| } | ||||
|  | ||||
| // GetTeamName returns the name of the specified teamId | ||||
| func (m *MMClient) GetTeamName(teamId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		if t.Id == teamId { | ||||
| 			return t.Team.Name | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // GetChannels returns all channels we're members off | ||||
| func (m *MMClient) GetChannels() []*model.Channel { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	var channels []*model.Channel | ||||
| 	// our primary team channels first | ||||
| 	channels = append(channels, m.Team.Channels.Channels...) | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		if t.Id != m.Team.Id { | ||||
| 			channels = append(channels, t.Channels.Channels...) | ||||
| 		} | ||||
| 	} | ||||
| 	return channels | ||||
| } | ||||
|  | ||||
| // GetMoreChannels returns existing channels where we're not a member off. | ||||
| func (m *MMClient) GetMoreChannels() []*model.Channel { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	var channels []*model.Channel | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		channels = append(channels, t.MoreChannels.Channels...) | ||||
| 	} | ||||
| 	return channels | ||||
| } | ||||
|  | ||||
| // GetTeamFromChannel returns teamId belonging to channel (DM channels have no teamId). | ||||
| func (m *MMClient) GetTeamFromChannel(channelId string) string { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	var channels []*model.Channel | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		channels = append(channels, t.Channels.Channels...) | ||||
| 		for _, c := range channels { | ||||
| 			if c.Id == channelId { | ||||
| 				return t.Id | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetLastViewedAt(channelId string) int64 { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for _, t := range m.OtherTeams { | ||||
| 		if _, ok := t.Channels.Members[channelId]; ok { | ||||
| 			return t.Channels.Members[channelId].LastViewedAt | ||||
| 		} | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetUsers() map[string]*model.User { | ||||
| 	users := make(map[string]*model.User) | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	for k, v := range m.Users { | ||||
| 		users[k] = v | ||||
| 	} | ||||
| 	return users | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetUser(userId string) *model.User { | ||||
| 	m.RLock() | ||||
| 	defer m.RUnlock() | ||||
| 	return m.Users[userId] | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetStatus(userId string) string { | ||||
| 	res, err := m.Client.GetStatuses() | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	status := res.Data.(map[string]string) | ||||
| 	if status[userId] == model.STATUS_AWAY { | ||||
| 		return "away" | ||||
| 	} | ||||
| 	if status[userId] == model.STATUS_ONLINE { | ||||
| 		return "online" | ||||
| 	} | ||||
| 	return "offline" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) StatusLoop() { | ||||
| 	for { | ||||
| 		if m.WsQuit { | ||||
| 			return | ||||
| 		} | ||||
| 		if m.WsConnected { | ||||
| 			m.log.Debug("WS PING") | ||||
| 			m.sendWSRequest("ping", nil) | ||||
| 			select { | ||||
| 			case <-m.WsPingChan: | ||||
| 				m.log.Debug("WS PONG received") | ||||
| 			case <-time.After(time.Second * 5): | ||||
| 				m.Logout() | ||||
| 				m.WsQuit = false | ||||
| 				m.Login() | ||||
| 			} | ||||
| 		} | ||||
| 		time.Sleep(time.Second * 60) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // initialize user and teams | ||||
| func (m *MMClient) initUser() error { | ||||
| 	m.Lock() | ||||
| 	defer m.Unlock() | ||||
| 	m.log.Debug("initUser()") | ||||
| 	initLoad, err := m.Client.GetInitialLoad() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	initData := initLoad.Data.(*model.InitialLoad) | ||||
| 	m.User = initData.User | ||||
| 	// we only load all team data on initial login. | ||||
| 	// all other updates are for channels from our (primary) team only. | ||||
| 	m.log.Debug("initUser(): loading all team data") | ||||
| 	for _, v := range initData.Teams { | ||||
| 		m.Client.SetTeamId(v.Id) | ||||
| 		mmusers, _ := m.Client.GetProfiles(v.Id, "") | ||||
| 		t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id} | ||||
| 		mmchannels, _ := m.Client.GetChannels("") | ||||
| 		t.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 		mmchannels, _ = m.Client.GetMoreChannels("") | ||||
| 		t.MoreChannels = mmchannels.Data.(*model.ChannelList) | ||||
| 		m.OtherTeams = append(m.OtherTeams, t) | ||||
| 		if v.Name == m.Credentials.Team { | ||||
| 			m.Team = t | ||||
| 			m.log.Debugf("initUser(): found our team %s (id: %s)", v.Name, v.Id) | ||||
| 		} | ||||
| 		// add all users | ||||
| 		for k, v := range t.Users { | ||||
| 			m.Users[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) error { | ||||
| 	req := &model.WebSocketRequest{} | ||||
| 	req.Seq = m.WsSequence | ||||
| 	req.Action = action | ||||
| 	req.Data = data | ||||
| 	m.WsSequence++ | ||||
| 	m.log.Debugf("sendWsRequest %#v", req) | ||||
| 	m.WsClient.WriteJSON(req) | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,24 +1,28 @@ | ||||
| //Package matterhook provides interaction with mattermost incoming/outgoing webhooks | ||||
| package matterhook | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/gorilla/schema" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| // OMessage for mattermost incoming webhook. (send to mattermost) | ||||
| type OMessage struct { | ||||
| 	Channel   string `json:"channel,omitempty"` | ||||
| 	IconURL   string `json:"icon_url,omitempty"` | ||||
| 	IconEmoji string `json:"icon_emoji,omitempty"` | ||||
| 	UserName  string `json:"username,omitempty"` | ||||
| 	Text      string `json:"text"` | ||||
| 	Channel     string      `json:"channel,omitempty"` | ||||
| 	IconURL     string      `json:"icon_url,omitempty"` | ||||
| 	IconEmoji   string      `json:"icon_emoji,omitempty"` | ||||
| 	UserName    string      `json:"username,omitempty"` | ||||
| 	Text        string      `json:"text"` | ||||
| 	Attachments interface{} `json:"attachments,omitempty"` | ||||
| 	Type        string      `json:"type,omitempty"` | ||||
| } | ||||
|  | ||||
| // IMessage for mattermost outgoing webhook. (received from mattermost) | ||||
| @@ -27,34 +31,46 @@ type IMessage struct { | ||||
| 	TeamID      string `schema:"team_id"` | ||||
| 	TeamDomain  string `schema:"team_domain"` | ||||
| 	ChannelID   string `schema:"channel_id"` | ||||
| 	ServiceID   string `schema:"service_id"` | ||||
| 	ChannelName string `schema:"channel_name"` | ||||
| 	Timestamp   string `schema:"timestamp"` | ||||
| 	UserID      string `schema:"user_id"` | ||||
| 	UserName    string `schema:"user_name"` | ||||
| 	PostId      string `schema:"post_id"` | ||||
| 	Text        string `schema:"text"` | ||||
| 	TriggerWord string `schema:"trigger_word"` | ||||
| } | ||||
|  | ||||
| // Client for Mattermost. | ||||
| type Client struct { | ||||
| 	url string | ||||
| 	In  chan IMessage | ||||
| 	Out chan OMessage | ||||
| 	Url        string // URL for incoming webhooks on mattermost. | ||||
| 	In         chan IMessage | ||||
| 	Out        chan OMessage | ||||
| 	httpclient *http.Client | ||||
| 	Config | ||||
| } | ||||
|  | ||||
| // Config for client. | ||||
| type Config struct { | ||||
| 	Port int | ||||
| 	BindAddress        string // Address to listen on | ||||
| 	Token              string // Only allow this token from Mattermost. (Allow everything when empty) | ||||
| 	InsecureSkipVerify bool   // disable certificate checking | ||||
| 	DisableServer      bool   // Do not start server for outgoing webhooks from Mattermost. | ||||
| } | ||||
|  | ||||
| // New Mattermost client. | ||||
| func New(url string, config Config) *Client { | ||||
| 	c := &Client{url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config} | ||||
| 	if c.Port == 0 { | ||||
| 		c.Port = 9999 | ||||
| 	c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config} | ||||
| 	tr := &http.Transport{ | ||||
| 		TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}, | ||||
| 	} | ||||
| 	c.httpclient = &http.Client{Transport: tr} | ||||
| 	if !c.DisableServer { | ||||
| 		_, _, err := net.SplitHostPort(c.BindAddress) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("incorrect bindaddress %s", c.BindAddress) | ||||
| 		} | ||||
| 		go c.StartServer() | ||||
| 	} | ||||
| 	go c.StartServer() | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| @@ -62,14 +78,19 @@ func New(url string, config Config) *Client { | ||||
| func (c *Client) StartServer() { | ||||
| 	mux := http.NewServeMux() | ||||
| 	mux.Handle("/", c) | ||||
| 	log.Printf("Listening on http://0.0.0.0:%v...\n", c.Port) | ||||
| 	if err := http.ListenAndServe((":" + strconv.Itoa(c.Port)), mux); err != nil { | ||||
| 	log.Printf("Listening on http://%v...\n", c.BindAddress) | ||||
| 	if err := http.ListenAndServe(c.BindAddress, mux); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ServeHTTP implementation. | ||||
| func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	if r.Method != "POST" { | ||||
| 		log.Println("invalid " + r.Method + " connection from " + r.RemoteAddr) | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 	msg := IMessage{} | ||||
| 	err := r.ParseForm() | ||||
| 	if err != nil { | ||||
| @@ -85,6 +106,18 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 	if msg.Token == "" { | ||||
| 		log.Println("no token from " + r.RemoteAddr) | ||||
| 		http.NotFound(w, r) | ||||
| 		return | ||||
| 	} | ||||
| 	if c.Token != "" { | ||||
| 		if msg.Token != c.Token { | ||||
| 			log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr) | ||||
| 			http.NotFound(w, r) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	c.In <- msg | ||||
| } | ||||
|  | ||||
| @@ -104,7 +137,7 @@ func (c *Client) Send(msg OMessage) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	resp, err := http.Post(c.url, "application/json", bytes.NewReader(buf)) | ||||
| 	resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										50
									
								
								migration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								migration.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| # Breaking changes from 0.4 to 0.5 for matterbridge (webhooks version) | ||||
| ## IRC section | ||||
| ### Server | ||||
| Port removed, added to server | ||||
| ``` | ||||
| server="irc.freenode.net" | ||||
| port=6667 | ||||
| ``` | ||||
| changed to | ||||
| ``` | ||||
| server="irc.freenode.net:6667" | ||||
| ``` | ||||
| ### Channel | ||||
| Removed see Channels section below | ||||
|  | ||||
| ### UseSlackCircumfix=true | ||||
| Removed, can be done by using ```RemoteNickFormat="<{NICK}> "``` | ||||
|  | ||||
| ## Mattermost section | ||||
| ### BindAddress | ||||
| Port removed, added to BindAddress | ||||
|  | ||||
| ``` | ||||
| BindAddress="0.0.0.0" | ||||
| port=9999 | ||||
| ``` | ||||
|  | ||||
| changed to | ||||
|  | ||||
| ``` | ||||
| BindAddress="0.0.0.0:9999" | ||||
| ``` | ||||
|  | ||||
| ### Token | ||||
| Removed | ||||
|  | ||||
| ## Channels section | ||||
| ``` | ||||
| [Token "outgoingwebhooktoken1"]  | ||||
| IRCChannel="#off-topic" | ||||
| MMChannel="off-topic" | ||||
| ``` | ||||
|  | ||||
| changed to | ||||
|  | ||||
| ``` | ||||
| [Channel "channelnameofchoice"]  | ||||
| IRC="#off-topic" | ||||
| Mattermost="off-topic" | ||||
| ``` | ||||
							
								
								
									
										202
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
|                                  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. | ||||
|  | ||||
							
								
								
									
										434
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,434 @@ | ||||
| package bridge | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"github.com/42wim/matterbridge-plus/matterclient" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/peterhellberg/giphy" | ||||
| 	ircm "github.com/sorcix/irc" | ||||
| 	"github.com/thoj/go-ircevent" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| //type Bridge struct { | ||||
| type MMhook struct { | ||||
| 	mh *matterhook.Client | ||||
| } | ||||
|  | ||||
| type MMapi struct { | ||||
| 	mc            *matterclient.MMClient | ||||
| 	mmMap         map[string]string | ||||
| 	mmIgnoreNicks []string | ||||
| } | ||||
|  | ||||
| type MMirc struct { | ||||
| 	i              *irc.Connection | ||||
| 	ircNick        string | ||||
| 	ircMap         map[string]string | ||||
| 	names          map[string][]string | ||||
| 	ircIgnoreNicks []string | ||||
| } | ||||
|  | ||||
| type MMMessage struct { | ||||
| 	Text     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| } | ||||
|  | ||||
| type Bridge struct { | ||||
| 	MMhook | ||||
| 	MMapi | ||||
| 	MMirc | ||||
| 	*Config | ||||
| 	kind string | ||||
| } | ||||
|  | ||||
| type FancyLog struct { | ||||
| 	irc *log.Entry | ||||
| 	mm  *log.Entry | ||||
| } | ||||
|  | ||||
| var flog FancyLog | ||||
|  | ||||
| const Legacy = "legacy" | ||||
|  | ||||
| func initFLog() { | ||||
| 	flog.irc = log.WithFields(log.Fields{"module": "irc"}) | ||||
| 	flog.mm = log.WithFields(log.Fields{"module": "mattermost"}) | ||||
| } | ||||
|  | ||||
| func NewBridge(name string, config *Config, kind string) *Bridge { | ||||
| 	initFLog() | ||||
| 	b := &Bridge{} | ||||
| 	b.Config = config | ||||
| 	b.kind = kind | ||||
| 	b.ircNick = b.Config.IRC.Nick | ||||
| 	b.ircMap = make(map[string]string) | ||||
| 	b.MMirc.names = make(map[string][]string) | ||||
| 	b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks) | ||||
| 	b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks) | ||||
| 	if kind == Legacy { | ||||
| 		if len(b.Config.Token) > 0 { | ||||
| 			for _, val := range b.Config.Token { | ||||
| 				b.ircMap[val.IRCChannel] = val.MMChannel | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		b.mh = matterhook.New(b.Config.Mattermost.URL, | ||||
| 			matterhook.Config{Port: b.Config.Mattermost.Port, Token: b.Config.Mattermost.Token, | ||||
| 				InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify, | ||||
| 				BindAddress:        b.Config.Mattermost.BindAddress}) | ||||
| 	} else { | ||||
| 		b.mmMap = make(map[string]string) | ||||
| 		if len(b.Config.Channel) > 0 { | ||||
| 			for _, val := range b.Config.Channel { | ||||
| 				b.ircMap[val.IRC] = val.Mattermost | ||||
| 				b.mmMap[val.Mattermost] = val.IRC | ||||
| 			} | ||||
| 		} | ||||
| 		b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password, | ||||
| 			b.Config.Mattermost.Team, b.Config.Mattermost.Server) | ||||
| 		b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify | ||||
| 		b.mc.NoTLS = b.Config.Mattermost.NoTLS | ||||
| 		flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server) | ||||
| 		err := b.mc.Login() | ||||
| 		if err != nil { | ||||
| 			flog.mm.Fatal("Can not connect", err) | ||||
| 		} | ||||
| 		flog.mm.Info("Login ok") | ||||
| 		b.mc.JoinChannel(b.Config.Mattermost.Channel) | ||||
| 		if len(b.Config.Channel) > 0 { | ||||
| 			for _, val := range b.Config.Channel { | ||||
| 				b.mc.JoinChannel(val.Mattermost) | ||||
| 			} | ||||
| 		} | ||||
| 		go b.mc.WsReceiver() | ||||
| 	} | ||||
| 	flog.irc.Info("Trying IRC connection") | ||||
| 	b.i = b.createIRC(name) | ||||
| 	flog.irc.Info("Connection succeeded") | ||||
| 	go b.handleMatter() | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bridge) createIRC(name string) *irc.Connection { | ||||
| 	i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick) | ||||
| 	i.UseTLS = b.Config.IRC.UseTLS | ||||
| 	i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify} | ||||
| 	if b.Config.IRC.Password != "" { | ||||
| 		i.Password = b.Config.IRC.Password | ||||
| 	} | ||||
| 	i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection) | ||||
| 	i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port)) | ||||
| 	return i | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleNewConnection(event *irc.Event) { | ||||
| 	flog.irc.Info("Registering callbacks") | ||||
| 	i := b.i | ||||
| 	b.ircNick = event.Arguments[0] | ||||
| 	i.AddCallback("PRIVMSG", b.handlePrivMsg) | ||||
| 	i.AddCallback("CTCP_ACTION", b.handlePrivMsg) | ||||
| 	i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames) | ||||
| 	i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames) | ||||
| 	i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime) | ||||
| 	i.AddCallback(ircm.NOTICE, b.handleNotice) | ||||
| 	i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) }) | ||||
| 	i.AddCallback("PING", func(e *irc.Event) { | ||||
| 		i.SendRaw("PONG :" + e.Message()) | ||||
| 		flog.irc.Debugf("PING/PONG") | ||||
| 	}) | ||||
| 	if b.Config.Mattermost.ShowJoinPart { | ||||
| 		i.AddCallback("JOIN", b.handleJoinPart) | ||||
| 		i.AddCallback("PART", b.handleJoinPart) | ||||
| 	} | ||||
| 	i.AddCallback("*", b.handleOther) | ||||
| 	b.setupChannels() | ||||
| } | ||||
|  | ||||
| func (b *Bridge) setupChannels() { | ||||
| 	i := b.i | ||||
| 	if b.Config.IRC.Channel != "" { | ||||
| 		flog.irc.Infof("Joining %s as %s", b.Config.IRC.Channel, b.ircNick) | ||||
| 		i.Join(b.Config.IRC.Channel) | ||||
| 	} | ||||
| 	if b.kind == Legacy { | ||||
| 		for _, val := range b.Config.Token { | ||||
| 			flog.irc.Infof("Joining %s as %s", val.IRCChannel, b.ircNick) | ||||
| 			i.Join(val.IRCChannel) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, val := range b.Config.Channel { | ||||
| 			flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick) | ||||
| 			i.Join(val.IRC) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool { | ||||
| 	parts := strings.Fields(event.Message()) | ||||
| 	exp, _ := regexp.Compile("[:,]+$") | ||||
| 	channel := event.Arguments[0] | ||||
| 	command := "" | ||||
| 	if len(parts) == 2 { | ||||
| 		command = parts[1] | ||||
| 	} | ||||
| 	if exp.ReplaceAllString(parts[0], "") == b.ircNick { | ||||
| 		switch command { | ||||
| 		case "users": | ||||
| 			usernames := b.mc.UsernamesInChannel(b.getMMChannel(channel)) | ||||
| 			sort.Strings(usernames) | ||||
| 			b.i.Privmsg(channel, "Users on Mattermost: "+strings.Join(usernames, ", ")) | ||||
| 		default: | ||||
| 			b.i.Privmsg(channel, "Valid commands are: [users, help]") | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (b *Bridge) ircNickFormat(nick string) string { | ||||
| 	if nick == b.ircNick { | ||||
| 		return nick | ||||
| 	} | ||||
| 	if b.Config.Mattermost.RemoteNickFormat == nil { | ||||
| 		return "irc-" + nick | ||||
| 	} | ||||
| 	return strings.Replace(*b.Config.Mattermost.RemoteNickFormat, "{NICK}", nick, -1) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handlePrivMsg(event *irc.Event) { | ||||
| 	if b.ignoreMessage(event.Nick, event.Message(), "irc") { | ||||
| 		return | ||||
| 	} | ||||
| 	if b.handleIrcBotCommand(event) { | ||||
| 		return | ||||
| 	} | ||||
| 	msg := "" | ||||
| 	if event.Code == "CTCP_ACTION" { | ||||
| 		msg = event.Nick + " " | ||||
| 	} | ||||
| 	msg += event.Message() | ||||
| 	b.Send(b.ircNickFormat(event.Nick), msg, b.getMMChannel(event.Arguments[0])) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleJoinPart(event *irc.Event) { | ||||
| 	b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0])) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleNotice(event *irc.Event) { | ||||
| 	if strings.Contains(event.Message(), "This nickname is registered") { | ||||
| 		b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) nicksPerRow() int { | ||||
| 	if b.Config.Mattermost.NicksPerRow < 1 { | ||||
| 		return 4 | ||||
| 	} | ||||
| 	return b.Config.Mattermost.NicksPerRow | ||||
| } | ||||
|  | ||||
| func (b *Bridge) formatnicks(nicks []string, continued bool) string { | ||||
| 	switch b.Config.Mattermost.NickFormatter { | ||||
| 	case "table": | ||||
| 		return tableformatter(nicks, b.nicksPerRow(), continued) | ||||
| 	default: | ||||
| 		return plainformatter(nicks, b.nicksPerRow()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) storeNames(event *irc.Event) { | ||||
| 	channel := event.Arguments[2] | ||||
| 	b.MMirc.names[channel] = append( | ||||
| 		b.MMirc.names[channel], | ||||
| 		strings.Split(strings.TrimSpace(event.Message()), " ")...) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) endNames(event *irc.Event) { | ||||
| 	channel := event.Arguments[1] | ||||
| 	sort.Strings(b.MMirc.names[channel]) | ||||
| 	maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow() | ||||
| 	continued := false | ||||
| 	for len(b.MMirc.names[channel]) > maxNamesPerPost { | ||||
| 		b.Send( | ||||
| 			b.ircNick, | ||||
| 			b.formatnicks(b.MMirc.names[channel][0:maxNamesPerPost], continued), | ||||
| 			b.getMMChannel(channel)) | ||||
| 		b.MMirc.names[channel] = b.MMirc.names[channel][maxNamesPerPost:] | ||||
| 		continued = true | ||||
| 	} | ||||
| 	b.Send(b.ircNick, b.formatnicks(b.MMirc.names[channel], continued), b.getMMChannel(channel)) | ||||
| 	b.MMirc.names[channel] = nil | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleTopicWhoTime(event *irc.Event) { | ||||
| 	parts := strings.Split(event.Arguments[2], "!") | ||||
| 	t, err := strconv.ParseInt(event.Arguments[3], 10, 64) | ||||
| 	if err != nil { | ||||
| 		flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3]) | ||||
| 	} | ||||
| 	user := parts[0] | ||||
| 	if len(parts) > 1 { | ||||
| 		user += " [" + parts[1] + "]" | ||||
| 	} | ||||
| 	flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0)) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleOther(event *irc.Event) { | ||||
| 	flog.irc.Debugf("%#v", event) | ||||
| } | ||||
|  | ||||
| func (b *Bridge) Send(nick string, message string, channel string) error { | ||||
| 	return b.SendType(nick, message, channel, "") | ||||
| } | ||||
|  | ||||
| func (b *Bridge) SendType(nick string, message string, channel string, mtype string) error { | ||||
| 	if b.Config.Mattermost.PrefixMessagesWithNick { | ||||
| 		if IsMarkup(message) { | ||||
| 			message = nick + "\n\n" + message | ||||
| 		} else { | ||||
| 			message = nick + " " + message | ||||
| 		} | ||||
| 	} | ||||
| 	if b.kind == Legacy { | ||||
| 		matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL} | ||||
| 		matterMessage.Channel = channel | ||||
| 		matterMessage.UserName = nick | ||||
| 		matterMessage.Type = mtype | ||||
| 		matterMessage.Text = message | ||||
| 		err := b.mh.Send(matterMessage) | ||||
| 		if err != nil { | ||||
| 			flog.mm.Info(err) | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 	flog.mm.Debug("->mattermost channel: ", channel, " ", message) | ||||
| 	b.mc.PostMessage(channel, message) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleMatterHook(mchan chan *MMMessage) { | ||||
| 	for { | ||||
| 		message := b.mh.Receive() | ||||
| 		m := &MMMessage{} | ||||
| 		m.Username = message.UserName | ||||
| 		m.Text = message.Text | ||||
| 		m.Channel = message.Token | ||||
| 		mchan <- m | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleMatterClient(mchan chan *MMMessage) { | ||||
| 	for message := range b.mc.MessageChan { | ||||
| 		// do not post our own messages back to irc | ||||
| 		if message.Raw.Action == "posted" && b.mc.User.Username != message.Username { | ||||
| 			m := &MMMessage{} | ||||
| 			m.Username = message.Username | ||||
| 			m.Channel = message.Channel | ||||
| 			m.Text = message.Text | ||||
| 			flog.mm.Debugf("<-mattermost channel: %s %#v %#v", message.Channel, message.Post, message.Raw) | ||||
| 			mchan <- m | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) handleMatter() { | ||||
| 	flog.mm.Infof("Choosing Mattermost connection type %s", b.kind) | ||||
| 	mchan := make(chan *MMMessage) | ||||
| 	if b.kind == Legacy { | ||||
| 		go b.handleMatterHook(mchan) | ||||
| 	} else { | ||||
| 		go b.handleMatterClient(mchan) | ||||
| 	} | ||||
| 	flog.mm.Info("Start listening for Mattermost messages") | ||||
| 	for message := range mchan { | ||||
| 		var username string | ||||
| 		if b.ignoreMessage(message.Username, message.Text, "mattermost") { | ||||
| 			continue | ||||
| 		} | ||||
| 		username = message.Username + ": " | ||||
| 		if b.Config.IRC.RemoteNickFormat != "" { | ||||
| 			username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1) | ||||
| 		} else if b.Config.IRC.UseSlackCircumfix { | ||||
| 			username = "<" + message.Username + "> " | ||||
| 		} | ||||
| 		cmds := strings.Fields(message.Text) | ||||
| 		// empty message | ||||
| 		if len(cmds) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		cmd := cmds[0] | ||||
| 		switch cmd { | ||||
| 		case "!users": | ||||
| 			flog.mm.Info("Received !users from ", message.Username) | ||||
| 			b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel)) | ||||
| 			continue | ||||
| 		case "!gif": | ||||
| 			message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1))) | ||||
| 			b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel)) | ||||
| 			continue | ||||
| 		} | ||||
| 		texts := strings.Split(message.Text, "\n") | ||||
| 		for _, text := range texts { | ||||
| 			flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel) | ||||
| 			b.i.Privmsg(b.getIRCChannel(message.Channel), username+text) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bridge) giphyRandom(query []string) string { | ||||
| 	g := giphy.DefaultClient | ||||
| 	if b.Config.General.GiphyAPIKey != "" { | ||||
| 		g.APIKey = b.Config.General.GiphyAPIKey | ||||
| 	} | ||||
| 	res, err := g.Random(query) | ||||
| 	if err != nil { | ||||
| 		return "error" | ||||
| 	} | ||||
| 	return res.Data.FixedHeightDownsampledURL | ||||
| } | ||||
|  | ||||
| func (b *Bridge) getMMChannel(ircChannel string) string { | ||||
| 	mmchannel, ok := b.ircMap[ircChannel] | ||||
| 	if !ok { | ||||
| 		mmchannel = b.Config.Mattermost.Channel | ||||
| 	} | ||||
| 	return mmchannel | ||||
| } | ||||
|  | ||||
| func (b *Bridge) getIRCChannel(channel string) string { | ||||
| 	if b.kind == Legacy { | ||||
| 		ircchannel := b.Config.IRC.Channel | ||||
| 		_, ok := b.Config.Token[channel] | ||||
| 		if ok { | ||||
| 			ircchannel = b.Config.Token[channel].IRCChannel | ||||
| 		} | ||||
| 		return ircchannel | ||||
| 	} | ||||
| 	ircchannel, ok := b.mmMap[channel] | ||||
| 	if !ok { | ||||
| 		ircchannel = b.Config.IRC.Channel | ||||
| 	} | ||||
| 	return ircchannel | ||||
| } | ||||
|  | ||||
| func (b *Bridge) ignoreMessage(nick string, message string, protocol string) bool { | ||||
| 	var ignoreNicks = b.mmIgnoreNicks | ||||
| 	if protocol == "irc" { | ||||
| 		ignoreNicks = b.ircIgnoreNicks | ||||
| 	} | ||||
| 	// should we discard messages ? | ||||
| 	for _, entry := range ignoreNicks { | ||||
| 		if nick == entry { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										68
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| package bridge | ||||
|  | ||||
| import ( | ||||
| 	"gopkg.in/gcfg.v1" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	IRC struct { | ||||
| 		UseTLS            bool | ||||
| 		SkipTLSVerify     bool | ||||
| 		Server            string | ||||
| 		Port              int | ||||
| 		Nick              string | ||||
| 		Password          string | ||||
| 		Channel           string | ||||
| 		UseSlackCircumfix bool | ||||
| 		NickServNick      string | ||||
| 		NickServPassword  string | ||||
| 		RemoteNickFormat  string | ||||
| 		IgnoreNicks       string | ||||
| 	} | ||||
| 	Mattermost struct { | ||||
| 		URL                    string | ||||
| 		Port                   int | ||||
| 		ShowJoinPart           bool | ||||
| 		Token                  string | ||||
| 		IconURL                string | ||||
| 		SkipTLSVerify          bool | ||||
| 		BindAddress            string | ||||
| 		Channel                string | ||||
| 		PrefixMessagesWithNick bool | ||||
| 		NicksPerRow            int | ||||
| 		NickFormatter          string | ||||
| 		Server                 string | ||||
| 		Team                   string | ||||
| 		Login                  string | ||||
| 		Password               string | ||||
| 		RemoteNickFormat       *string | ||||
| 		IgnoreNicks            string | ||||
| 		NoTLS                  bool | ||||
| 	} | ||||
| 	Token map[string]*struct { | ||||
| 		IRCChannel string | ||||
| 		MMChannel  string | ||||
| 	} | ||||
| 	Channel map[string]*struct { | ||||
| 		IRC        string | ||||
| 		Mattermost string | ||||
| 	} | ||||
| 	General struct { | ||||
| 		GiphyAPIKey string | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewConfig(cfgfile string) *Config { | ||||
| 	var cfg Config | ||||
| 	content, err := ioutil.ReadFile(cfgfile) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	err = gcfg.ReadStringInto(&cfg, string(content)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("Failed to parse "+cfgfile+":", err) | ||||
| 	} | ||||
| 	return &cfg | ||||
| } | ||||
							
								
								
									
										59
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| package bridge | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func tableformatter(nicks []string, nicksPerRow int, continued bool) string { | ||||
| 	result := "|IRC users" | ||||
| 	if continued { | ||||
| 		result = "|(continued)" | ||||
| 	} | ||||
| 	for i := 0; i < 2; i++ { | ||||
| 		for j := 1; j <= nicksPerRow && j <= len(nicks); j++ { | ||||
| 			if i == 0 { | ||||
| 				result += "|" | ||||
| 			} else { | ||||
| 				result += ":-|" | ||||
| 			} | ||||
| 		} | ||||
| 		result += "\r\n|" | ||||
| 	} | ||||
| 	result += nicks[0] + "|" | ||||
| 	for i := 1; i < len(nicks); i++ { | ||||
| 		if i%nicksPerRow == 0 { | ||||
| 			result += "\r\n|" + nicks[i] + "|" | ||||
| 		} else { | ||||
| 			result += nicks[i] + "|" | ||||
| 		} | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func plainformatter(nicks []string, nicksPerRow int) string { | ||||
| 	return strings.Join(nicks, ", ") + " currently on IRC" | ||||
| } | ||||
|  | ||||
| func IsMarkup(message string) bool { | ||||
| 	switch message[0] { | ||||
| 	case '|': | ||||
| 		fallthrough | ||||
| 	case '#': | ||||
| 		fallthrough | ||||
| 	case '_': | ||||
| 		fallthrough | ||||
| 	case '*': | ||||
| 		fallthrough | ||||
| 	case '~': | ||||
| 		fallthrough | ||||
| 	case '-': | ||||
| 		fallthrough | ||||
| 	case ':': | ||||
| 		fallthrough | ||||
| 	case '>': | ||||
| 		fallthrough | ||||
| 	case '=': | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										202
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
|                                  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. | ||||
|  | ||||
							
								
								
									
										441
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,441 @@ | ||||
| package matterclient | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"errors" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"net/http" | ||||
| 	"net/http/cookiejar" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/jpillora/backoff" | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type Credentials struct { | ||||
| 	Login         string | ||||
| 	Team          string | ||||
| 	Pass          string | ||||
| 	Server        string | ||||
| 	NoTLS         bool | ||||
| 	SkipTLSVerify bool | ||||
| } | ||||
|  | ||||
| type Message struct { | ||||
| 	Raw      *model.Message | ||||
| 	Post     *model.Post | ||||
| 	Team     string | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	Text     string | ||||
| } | ||||
|  | ||||
| type MMClient struct { | ||||
| 	*Credentials | ||||
| 	Client       *model.Client | ||||
| 	WsClient     *websocket.Conn | ||||
| 	WsQuit       bool | ||||
| 	WsAway       bool | ||||
| 	Channels     *model.ChannelList | ||||
| 	MoreChannels *model.ChannelList | ||||
| 	User         *model.User | ||||
| 	Users        map[string]*model.User | ||||
| 	MessageChan  chan *Message | ||||
| 	Team         *model.Team | ||||
| 	log          *log.Entry | ||||
| } | ||||
|  | ||||
| func New(login, pass, team, server string) *MMClient { | ||||
| 	cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server} | ||||
| 	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100)} | ||||
| 	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"}) | ||||
| 	log.SetFormatter(&log.TextFormatter{FullTimestamp: true}) | ||||
| 	return mmclient | ||||
| } | ||||
|  | ||||
| func (m *MMClient) SetLogLevel(level string) { | ||||
| 	l, err := log.ParseLevel(level) | ||||
| 	if err != nil { | ||||
| 		log.SetLevel(log.InfoLevel) | ||||
| 		return | ||||
| 	} | ||||
| 	log.SetLevel(l) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) Login() error { | ||||
| 	if m.WsQuit { | ||||
| 		return nil | ||||
| 	} | ||||
| 	b := &backoff.Backoff{ | ||||
| 		Min:    time.Second, | ||||
| 		Max:    5 * time.Minute, | ||||
| 		Jitter: true, | ||||
| 	} | ||||
| 	uriScheme := "https://" | ||||
| 	wsScheme := "wss://" | ||||
| 	if m.NoTLS { | ||||
| 		uriScheme = "http://" | ||||
| 		wsScheme = "ws://" | ||||
| 	} | ||||
| 	// login to mattermost | ||||
| 	m.Client = model.NewClient(uriScheme + m.Credentials.Server) | ||||
| 	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}} | ||||
| 	var myinfo *model.Result | ||||
| 	var appErr *model.AppError | ||||
| 	var logmsg = "trying login" | ||||
| 	for { | ||||
| 		m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server) | ||||
| 		if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) { | ||||
| 			m.log.Debugf(logmsg+" with ", model.SESSION_COOKIE_TOKEN) | ||||
| 			token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=") | ||||
| 			m.Client.HttpClient.Jar = m.createCookieJar(token[1]) | ||||
| 			m.Client.MockSession(token[1]) | ||||
| 			myinfo, appErr = m.Client.GetMe("") | ||||
| 			if myinfo.Data.(*model.User) == nil { | ||||
| 				m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass) | ||||
| 				return errors.New("invalid " + model.SESSION_COOKIE_TOKEN) | ||||
| 			} | ||||
| 		} else { | ||||
| 			myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass) | ||||
| 		} | ||||
| 		if appErr != nil { | ||||
| 			d := b.Duration() | ||||
| 			m.log.Debug(appErr.DetailedError) | ||||
| 			if !strings.Contains(appErr.DetailedError, "connection refused") && | ||||
| 				!strings.Contains(appErr.DetailedError, "invalid character") { | ||||
| 				if appErr.Message == "" { | ||||
| 					return errors.New(appErr.DetailedError) | ||||
| 				} | ||||
| 				return errors.New(appErr.Message) | ||||
| 			} | ||||
| 			m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d) | ||||
| 			time.Sleep(d) | ||||
| 			logmsg = "retrying login" | ||||
| 			continue | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	// reset timer | ||||
| 	b.Reset() | ||||
|  | ||||
| 	initLoad, _ := m.Client.GetInitialLoad() | ||||
| 	initData := initLoad.Data.(*model.InitialLoad) | ||||
| 	m.User = initData.User | ||||
| 	for _, v := range initData.Teams { | ||||
| 		m.log.Debugf("trying %s (id: %s)", v.Name, v.Id) | ||||
| 		if v.Name == m.Credentials.Team { | ||||
| 			m.Client.SetTeamId(v.Id) | ||||
| 			m.Team = v | ||||
| 			m.log.Debugf("GetallTeamListings: found id %s for team %s", v.Id, v.Name) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if m.Team == nil { | ||||
| 		return errors.New("team not found") | ||||
| 	} | ||||
|  | ||||
| 	// setup websocket connection | ||||
| 	wsurl := wsScheme + m.Credentials.Server + "/api/v3/users/websocket" | ||||
| 	header := http.Header{} | ||||
| 	header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken) | ||||
|  | ||||
| 	m.log.Debug("WsClient: making connection") | ||||
| 	var err error | ||||
| 	for { | ||||
| 		wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}} | ||||
| 		m.WsClient, _, err = wsDialer.Dial(wsurl, header) | ||||
| 		if err != nil { | ||||
| 			d := b.Duration() | ||||
| 			m.log.Debugf("WSS: %s, reconnecting in %s", err, d) | ||||
| 			time.Sleep(d) | ||||
| 			continue | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	b.Reset() | ||||
|  | ||||
| 	// populating users | ||||
| 	m.UpdateUsers() | ||||
|  | ||||
| 	// populating channels | ||||
| 	m.UpdateChannels() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) WsReceiver() { | ||||
| 	var rmsg model.Message | ||||
| 	for { | ||||
| 		if m.WsQuit { | ||||
| 			m.log.Debug("exiting WsReceiver") | ||||
| 			return | ||||
| 		} | ||||
| 		if err := m.WsClient.ReadJSON(&rmsg); err != nil { | ||||
| 			m.log.Error("error:", err) | ||||
| 			// reconnect | ||||
| 			m.Login() | ||||
| 		} | ||||
| 		if rmsg.Action == "ping" { | ||||
| 			m.handleWsPing() | ||||
| 			continue | ||||
| 		} | ||||
| 		msg := &Message{Raw: &rmsg, Team: m.Credentials.Team} | ||||
| 		m.parseMessage(msg) | ||||
| 		m.MessageChan <- msg | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func (m *MMClient) handleWsPing() { | ||||
| 	m.log.Debug("Ws PING") | ||||
| 	if !m.WsQuit && !m.WsAway { | ||||
| 		m.log.Debug("Ws PONG") | ||||
| 		m.WsClient.WriteMessage(websocket.PongMessage, []byte{}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) parseMessage(rmsg *Message) { | ||||
| 	switch rmsg.Raw.Action { | ||||
| 	case model.ACTION_POSTED: | ||||
| 		m.parseActionPost(rmsg) | ||||
| 		/* | ||||
| 			case model.ACTION_USER_REMOVED: | ||||
| 				m.handleWsActionUserRemoved(&rmsg) | ||||
| 			case model.ACTION_USER_ADDED: | ||||
| 				m.handleWsActionUserAdded(&rmsg) | ||||
| 		*/ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) parseActionPost(rmsg *Message) { | ||||
| 	data := model.PostFromJson(strings.NewReader(rmsg.Raw.Props["post"])) | ||||
| 	//	log.Println("receiving userid", data.UserId) | ||||
| 	// we don't have the user, refresh the userlist | ||||
| 	if m.Users[data.UserId] == nil { | ||||
| 		m.UpdateUsers() | ||||
| 	} | ||||
| 	rmsg.Username = m.Users[data.UserId].Username | ||||
| 	rmsg.Channel = m.GetChannelName(data.ChannelId) | ||||
| 	// direct message | ||||
| 	if strings.Contains(rmsg.Channel, "__") { | ||||
| 		//log.Println("direct message") | ||||
| 		rcvusers := strings.Split(rmsg.Channel, "__") | ||||
| 		if rcvusers[0] != m.User.Id { | ||||
| 			rmsg.Channel = m.Users[rcvusers[0]].Username | ||||
| 		} else { | ||||
| 			rmsg.Channel = m.Users[rcvusers[1]].Username | ||||
| 		} | ||||
| 	} | ||||
| 	rmsg.Text = data.Message | ||||
| 	rmsg.Post = data | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateUsers() error { | ||||
| 	mmusers, _ := m.Client.GetProfilesForDirectMessageList(m.Team.Id) | ||||
| 	m.Users = mmusers.Data.(map[string]*model.User) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateChannels() error { | ||||
| 	mmchannels, _ := m.Client.GetChannels("") | ||||
| 	m.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 	mmchannels, _ = m.Client.GetMoreChannels("") | ||||
| 	m.MoreChannels = mmchannels.Data.(*model.ChannelList) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelName(id string) string { | ||||
| 	for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) { | ||||
| 		if channel.Id == id { | ||||
| 			return channel.Name | ||||
| 		} | ||||
| 	} | ||||
| 	// not found? could be a new direct message from mattermost. Try to update and check again | ||||
| 	m.UpdateChannels() | ||||
| 	for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) { | ||||
| 		if channel.Id == id { | ||||
| 			return channel.Name | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelId(name string) string { | ||||
| 	for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) { | ||||
| 		if channel.Name == name { | ||||
| 			return channel.Id | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetChannelHeader(id string) string { | ||||
| 	for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) { | ||||
| 		if channel.Id == id { | ||||
| 			return channel.Header | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *MMClient) PostMessage(channel string, text string) { | ||||
| 	post := &model.Post{ChannelId: m.GetChannelId(channel), Message: text} | ||||
| 	m.Client.CreatePost(post) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) JoinChannel(channel string) error { | ||||
| 	cleanChan := strings.Replace(channel, "#", "", 1) | ||||
| 	if m.GetChannelId(cleanChan) == "" { | ||||
| 		return errors.New("failed to join") | ||||
| 	} | ||||
| 	for _, c := range m.Channels.Channels { | ||||
| 		if c.Name == cleanChan { | ||||
| 			m.log.Debug("Not joining ", cleanChan, " already joined.") | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	m.log.Debug("Joining ", cleanChan) | ||||
| 	_, err := m.Client.JoinChannel(m.GetChannelId(cleanChan)) | ||||
| 	if err != nil { | ||||
| 		return errors.New("failed to join") | ||||
| 	} | ||||
| 	//	m.SyncChannel(m.getMMChannelId(strings.Replace(channel, "#", "", 1)), strings.Replace(channel, "#", "", 1)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList { | ||||
| 	res, err := m.Client.GetPostsSince(channelId, time) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) SearchPosts(query string) *model.PostList { | ||||
| 	res, err := m.Client.SearchPosts(query, false) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { | ||||
| 	res, err := m.Client.GetPosts(channelId, 0, limit, "") | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return res.Data.(*model.PostList) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPublicLink(filename string) string { | ||||
| 	res, err := m.Client.GetPublicLink(filename) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return res.Data.(string) | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetPublicLinks(filenames []string) []string { | ||||
| 	var output []string | ||||
| 	for _, f := range filenames { | ||||
| 		res, err := m.Client.GetPublicLink(f) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		output = append(output, res.Data.(string)) | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateChannelHeader(channelId string, header string) { | ||||
| 	data := make(map[string]string) | ||||
| 	data["channel_id"] = channelId | ||||
| 	data["channel_header"] = header | ||||
| 	m.log.Debugf("updating channelheader %#v, %#v", channelId, header) | ||||
| 	_, err := m.Client.UpdateChannelHeader(data) | ||||
| 	if err != nil { | ||||
| 		log.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateLastViewed(channelId string) { | ||||
| 	m.log.Debugf("posting lastview %#v", channelId) | ||||
| 	_, err := m.Client.UpdateLastViewedAt(channelId) | ||||
| 	if err != nil { | ||||
| 		m.log.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UsernamesInChannel(channelName string) []string { | ||||
| 	ceiRes, err := m.Client.GetChannelExtraInfo(m.GetChannelId(channelName), 5000, "") | ||||
| 	if err != nil { | ||||
| 		m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelName, err) | ||||
| 		return []string{} | ||||
| 	} | ||||
| 	extra := ceiRes.Data.(*model.ChannelExtra) | ||||
| 	result := []string{} | ||||
| 	for _, member := range extra.Members { | ||||
| 		result = append(result, member.Username) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func (m *MMClient) createCookieJar(token string) *cookiejar.Jar { | ||||
| 	var cookies []*http.Cookie | ||||
| 	jar, _ := cookiejar.New(nil) | ||||
| 	firstCookie := &http.Cookie{ | ||||
| 		Name:   "MMAUTHTOKEN", | ||||
| 		Value:  token, | ||||
| 		Path:   "/", | ||||
| 		Domain: m.Credentials.Server, | ||||
| 	} | ||||
| 	cookies = append(cookies, firstCookie) | ||||
| 	cookieURL, _ := url.Parse("https://" + m.Credentials.Server) | ||||
| 	jar.SetCookies(cookieURL, cookies) | ||||
| 	return jar | ||||
| } | ||||
|  | ||||
| func (m *MMClient) GetOtherUserDM(channel string) *model.User { | ||||
| 	m.UpdateUsers() | ||||
| 	var rcvuser *model.User | ||||
| 	if strings.Contains(channel, "__") { | ||||
| 		rcvusers := strings.Split(channel, "__") | ||||
| 		if rcvusers[0] != m.User.Id { | ||||
| 			rcvuser = m.Users[rcvusers[0]] | ||||
| 		} else { | ||||
| 			rcvuser = m.Users[rcvusers[1]] | ||||
| 		} | ||||
| 	} | ||||
| 	return rcvuser | ||||
| } | ||||
|  | ||||
| func (m *MMClient) SendDirectMessage(toUserId string, msg string) { | ||||
| 	m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg) | ||||
| 	var channel string | ||||
| 	// We don't have a DM with this user yet. | ||||
| 	if m.GetChannelId(toUserId+"__"+m.User.Id) == "" && m.GetChannelId(m.User.Id+"__"+toUserId) == "" { | ||||
| 		// create DM channel | ||||
| 		_, err := m.Client.CreateDirectChannel(toUserId) | ||||
| 		if err != nil { | ||||
| 			m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err) | ||||
| 		} | ||||
| 		// update our channels | ||||
| 		mmchannels, _ := m.Client.GetChannels("") | ||||
| 		m.Channels = mmchannels.Data.(*model.ChannelList) | ||||
| 	} | ||||
|  | ||||
| 	// build the channel name | ||||
| 	if toUserId > m.User.Id { | ||||
| 		channel = m.User.Id + "__" + toUserId | ||||
| 	} else { | ||||
| 		channel = toUserId + "__" + m.User.Id | ||||
| 	} | ||||
| 	// build & send the message | ||||
| 	msg = strings.Replace(msg, "\r", "", -1) | ||||
| 	post := &model.Post{ChannelId: m.GetChannelId(channel), Message: msg} | ||||
| 	m.Client.CreatePost(post) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2014 Simon Eskildsen | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										26
									
								
								vendor/github.com/Sirupsen/logrus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/Sirupsen/logrus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| /* | ||||
| Package logrus is a structured logger for Go, completely API compatible with the standard library logger. | ||||
|  | ||||
|  | ||||
| The simplest way to use Logrus is simply the package-level exported logger: | ||||
|  | ||||
|   package main | ||||
|  | ||||
|   import ( | ||||
|     log "github.com/Sirupsen/logrus" | ||||
|   ) | ||||
|  | ||||
|   func main() { | ||||
|     log.WithFields(log.Fields{ | ||||
|       "animal": "walrus", | ||||
|       "number": 1, | ||||
|       "size":   10, | ||||
|     }).Info("A walrus appears") | ||||
|   } | ||||
|  | ||||
| Output: | ||||
|   time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 | ||||
|  | ||||
| For a full guide visit https://github.com/Sirupsen/logrus | ||||
| */ | ||||
| package logrus | ||||
							
								
								
									
										264
									
								
								vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,264 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Defines the key when adding errors using WithError. | ||||
| var ErrorKey = "error" | ||||
|  | ||||
| // An entry is the final or intermediate Logrus logging entry. It contains all | ||||
| // the fields passed with WithField{,s}. It's finally logged when Debug, Info, | ||||
| // Warn, Error, Fatal or Panic is called on it. These objects can be reused and | ||||
| // passed around as much as you wish to avoid field duplication. | ||||
| type Entry struct { | ||||
| 	Logger *Logger | ||||
|  | ||||
| 	// Contains all the fields set by the user. | ||||
| 	Data Fields | ||||
|  | ||||
| 	// Time at which the log entry was created | ||||
| 	Time time.Time | ||||
|  | ||||
| 	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic | ||||
| 	Level Level | ||||
|  | ||||
| 	// Message passed to Debug, Info, Warn, Error, Fatal or Panic | ||||
| 	Message string | ||||
| } | ||||
|  | ||||
| func NewEntry(logger *Logger) *Entry { | ||||
| 	return &Entry{ | ||||
| 		Logger: logger, | ||||
| 		// Default is three fields, give a little extra room | ||||
| 		Data: make(Fields, 5), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // formatter. | ||||
| func (entry *Entry) String() (string, error) { | ||||
| 	reader, err := entry.Reader() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return reader.String(), err | ||||
| } | ||||
|  | ||||
| // Add an error as single field (using the key defined in ErrorKey) to the Entry. | ||||
| func (entry *Entry) WithError(err error) *Entry { | ||||
| 	return entry.WithField(ErrorKey, err) | ||||
| } | ||||
|  | ||||
| // Add a single field to the Entry. | ||||
| func (entry *Entry) WithField(key string, value interface{}) *Entry { | ||||
| 	return entry.WithFields(Fields{key: value}) | ||||
| } | ||||
|  | ||||
| // Add a map of fields to the Entry. | ||||
| func (entry *Entry) WithFields(fields Fields) *Entry { | ||||
| 	data := make(Fields, len(entry.Data)+len(fields)) | ||||
| 	for k, v := range entry.Data { | ||||
| 		data[k] = v | ||||
| 	} | ||||
| 	for k, v := range fields { | ||||
| 		data[k] = v | ||||
| 	} | ||||
| 	return &Entry{Logger: entry.Logger, Data: data} | ||||
| } | ||||
|  | ||||
| // This function is not declared with a pointer value because otherwise | ||||
| // race conditions will occur when using multiple goroutines | ||||
| func (entry Entry) log(level Level, msg string) { | ||||
| 	entry.Time = time.Now() | ||||
| 	entry.Level = level | ||||
| 	entry.Message = msg | ||||
|  | ||||
| 	if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { | ||||
| 		entry.Logger.mu.Lock() | ||||
| 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | ||||
| 		entry.Logger.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	reader, err := entry.Reader() | ||||
| 	if err != nil { | ||||
| 		entry.Logger.mu.Lock() | ||||
| 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | ||||
| 		entry.Logger.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	entry.Logger.mu.Lock() | ||||
| 	defer entry.Logger.mu.Unlock() | ||||
|  | ||||
| 	_, err = io.Copy(entry.Logger.Out, reader) | ||||
| 	if err != nil { | ||||
| 		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 | ||||
| 	// panic() to use in Entry#Panic(), we avoid the allocation by checking | ||||
| 	// directly here. | ||||
| 	if level <= PanicLevel { | ||||
| 		panic(&entry) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Debug(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= DebugLevel { | ||||
| 		entry.log(DebugLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Print(args ...interface{}) { | ||||
| 	entry.Info(args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Info(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= InfoLevel { | ||||
| 		entry.log(InfoLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warn(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= WarnLevel { | ||||
| 		entry.log(WarnLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warning(args ...interface{}) { | ||||
| 	entry.Warn(args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Error(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= ErrorLevel { | ||||
| 		entry.log(ErrorLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Fatal(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.log(FatalLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Panic(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= PanicLevel { | ||||
| 		entry.log(PanicLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| 	panic(fmt.Sprint(args...)) | ||||
| } | ||||
|  | ||||
| // Entry Printf family functions | ||||
|  | ||||
| func (entry *Entry) Debugf(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= DebugLevel { | ||||
| 		entry.Debug(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Infof(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= InfoLevel { | ||||
| 		entry.Info(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Printf(format string, args ...interface{}) { | ||||
| 	entry.Infof(format, args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warnf(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= WarnLevel { | ||||
| 		entry.Warn(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warningf(format string, args ...interface{}) { | ||||
| 	entry.Warnf(format, args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Errorf(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= ErrorLevel { | ||||
| 		entry.Error(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Fatalf(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.Fatal(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Panicf(format string, args ...interface{}) { | ||||
| 	if entry.Logger.Level >= PanicLevel { | ||||
| 		entry.Panic(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Entry Println family functions | ||||
|  | ||||
| func (entry *Entry) Debugln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= DebugLevel { | ||||
| 		entry.Debug(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Infoln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= InfoLevel { | ||||
| 		entry.Info(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Println(args ...interface{}) { | ||||
| 	entry.Infoln(args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warnln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= WarnLevel { | ||||
| 		entry.Warn(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Warningln(args ...interface{}) { | ||||
| 	entry.Warnln(args...) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Errorln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= ErrorLevel { | ||||
| 		entry.Error(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Fatalln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.Fatal(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (entry *Entry) Panicln(args ...interface{}) { | ||||
| 	if entry.Logger.Level >= PanicLevel { | ||||
| 		entry.Panic(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Sprintlnn => Sprint no newline. This is to get the behavior of how | ||||
| // fmt.Sprintln where spaces are always added between operands, regardless of | ||||
| // their type. Instead of vendoring the Sprintln implementation to spare a | ||||
| // string allocation, we do the simplest thing. | ||||
| func (entry *Entry) sprintlnn(args ...interface{}) string { | ||||
| 	msg := fmt.Sprintln(args...) | ||||
| 	return msg[:len(msg)-1] | ||||
| } | ||||
							
								
								
									
										50
									
								
								vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| var log = logrus.New() | ||||
|  | ||||
| func init() { | ||||
| 	log.Formatter = new(logrus.JSONFormatter) | ||||
| 	log.Formatter = new(logrus.TextFormatter) // default | ||||
| 	log.Level = logrus.DebugLevel | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	defer func() { | ||||
| 		err := recover() | ||||
| 		if err != nil { | ||||
| 			log.WithFields(logrus.Fields{ | ||||
| 				"omg":    true, | ||||
| 				"err":    err, | ||||
| 				"number": 100, | ||||
| 			}).Fatal("The ice breaks!") | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"animal": "walrus", | ||||
| 		"number": 8, | ||||
| 	}).Debug("Started observing beach") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"animal": "walrus", | ||||
| 		"size":   10, | ||||
| 	}).Info("A group of walrus emerges from the ocean") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"omg":    true, | ||||
| 		"number": 122, | ||||
| 	}).Warn("The group's number increased tremendously!") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"temperature": -4, | ||||
| 	}).Debug("Temperature changes") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"animal": "orca", | ||||
| 		"size":   9009, | ||||
| 	}).Panic("It's over 9000!") | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"gopkg.in/gemnasium/logrus-airbrake-hook.v2" | ||||
| ) | ||||
|  | ||||
| var log = logrus.New() | ||||
|  | ||||
| func init() { | ||||
| 	log.Formatter = new(logrus.TextFormatter) // default | ||||
| 	log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"animal": "walrus", | ||||
| 		"size":   10, | ||||
| 	}).Info("A group of walrus emerges from the ocean") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"omg":    true, | ||||
| 		"number": 122, | ||||
| 	}).Warn("The group's number increased tremendously!") | ||||
|  | ||||
| 	log.WithFields(logrus.Fields{ | ||||
| 		"omg":    true, | ||||
| 		"number": 100, | ||||
| 	}).Fatal("The ice breaks!") | ||||
| } | ||||
							
								
								
									
										193
									
								
								vendor/github.com/Sirupsen/logrus/exported.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								vendor/github.com/Sirupsen/logrus/exported.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// std is the name of the standard logger in stdlib `log` | ||||
| 	std = New() | ||||
| ) | ||||
|  | ||||
| func StandardLogger() *Logger { | ||||
| 	return std | ||||
| } | ||||
|  | ||||
| // SetOutput sets the standard logger output. | ||||
| func SetOutput(out io.Writer) { | ||||
| 	std.mu.Lock() | ||||
| 	defer std.mu.Unlock() | ||||
| 	std.Out = out | ||||
| } | ||||
|  | ||||
| // SetFormatter sets the standard logger formatter. | ||||
| func SetFormatter(formatter Formatter) { | ||||
| 	std.mu.Lock() | ||||
| 	defer std.mu.Unlock() | ||||
| 	std.Formatter = formatter | ||||
| } | ||||
|  | ||||
| // SetLevel sets the standard logger level. | ||||
| func SetLevel(level Level) { | ||||
| 	std.mu.Lock() | ||||
| 	defer std.mu.Unlock() | ||||
| 	std.Level = level | ||||
| } | ||||
|  | ||||
| // GetLevel returns the standard logger level. | ||||
| func GetLevel() Level { | ||||
| 	std.mu.Lock() | ||||
| 	defer std.mu.Unlock() | ||||
| 	return std.Level | ||||
| } | ||||
|  | ||||
| // AddHook adds a hook to the standard logger hooks. | ||||
| func AddHook(hook Hook) { | ||||
| 	std.mu.Lock() | ||||
| 	defer std.mu.Unlock() | ||||
| 	std.Hooks.Add(hook) | ||||
| } | ||||
|  | ||||
| // WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. | ||||
| func WithError(err error) *Entry { | ||||
| 	return std.WithField(ErrorKey, err) | ||||
| } | ||||
|  | ||||
| // WithField creates an entry from the standard logger and adds a field to | ||||
| // it. If you want multiple fields, use `WithFields`. | ||||
| // | ||||
| // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal | ||||
| // or Panic on the Entry it returns. | ||||
| func WithField(key string, value interface{}) *Entry { | ||||
| 	return std.WithField(key, value) | ||||
| } | ||||
|  | ||||
| // WithFields creates an entry from the standard logger and adds multiple | ||||
| // fields to it. This is simply a helper for `WithField`, invoking it | ||||
| // once for each field. | ||||
| // | ||||
| // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal | ||||
| // or Panic on the Entry it returns. | ||||
| func WithFields(fields Fields) *Entry { | ||||
| 	return std.WithFields(fields) | ||||
| } | ||||
|  | ||||
| // Debug logs a message at level Debug on the standard logger. | ||||
| func Debug(args ...interface{}) { | ||||
| 	std.Debug(args...) | ||||
| } | ||||
|  | ||||
| // Print logs a message at level Info on the standard logger. | ||||
| func Print(args ...interface{}) { | ||||
| 	std.Print(args...) | ||||
| } | ||||
|  | ||||
| // Info logs a message at level Info on the standard logger. | ||||
| func Info(args ...interface{}) { | ||||
| 	std.Info(args...) | ||||
| } | ||||
|  | ||||
| // Warn logs a message at level Warn on the standard logger. | ||||
| func Warn(args ...interface{}) { | ||||
| 	std.Warn(args...) | ||||
| } | ||||
|  | ||||
| // Warning logs a message at level Warn on the standard logger. | ||||
| func Warning(args ...interface{}) { | ||||
| 	std.Warning(args...) | ||||
| } | ||||
|  | ||||
| // Error logs a message at level Error on the standard logger. | ||||
| func Error(args ...interface{}) { | ||||
| 	std.Error(args...) | ||||
| } | ||||
|  | ||||
| // Panic logs a message at level Panic on the standard logger. | ||||
| func Panic(args ...interface{}) { | ||||
| 	std.Panic(args...) | ||||
| } | ||||
|  | ||||
| // Fatal logs a message at level Fatal on the standard logger. | ||||
| func Fatal(args ...interface{}) { | ||||
| 	std.Fatal(args...) | ||||
| } | ||||
|  | ||||
| // Debugf logs a message at level Debug on the standard logger. | ||||
| func Debugf(format string, args ...interface{}) { | ||||
| 	std.Debugf(format, args...) | ||||
| } | ||||
|  | ||||
| // Printf logs a message at level Info on the standard logger. | ||||
| func Printf(format string, args ...interface{}) { | ||||
| 	std.Printf(format, args...) | ||||
| } | ||||
|  | ||||
| // Infof logs a message at level Info on the standard logger. | ||||
| func Infof(format string, args ...interface{}) { | ||||
| 	std.Infof(format, args...) | ||||
| } | ||||
|  | ||||
| // Warnf logs a message at level Warn on the standard logger. | ||||
| func Warnf(format string, args ...interface{}) { | ||||
| 	std.Warnf(format, args...) | ||||
| } | ||||
|  | ||||
| // Warningf logs a message at level Warn on the standard logger. | ||||
| func Warningf(format string, args ...interface{}) { | ||||
| 	std.Warningf(format, args...) | ||||
| } | ||||
|  | ||||
| // Errorf logs a message at level Error on the standard logger. | ||||
| func Errorf(format string, args ...interface{}) { | ||||
| 	std.Errorf(format, args...) | ||||
| } | ||||
|  | ||||
| // Panicf logs a message at level Panic on the standard logger. | ||||
| func Panicf(format string, args ...interface{}) { | ||||
| 	std.Panicf(format, args...) | ||||
| } | ||||
|  | ||||
| // Fatalf logs a message at level Fatal on the standard logger. | ||||
| func Fatalf(format string, args ...interface{}) { | ||||
| 	std.Fatalf(format, args...) | ||||
| } | ||||
|  | ||||
| // Debugln logs a message at level Debug on the standard logger. | ||||
| func Debugln(args ...interface{}) { | ||||
| 	std.Debugln(args...) | ||||
| } | ||||
|  | ||||
| // Println logs a message at level Info on the standard logger. | ||||
| func Println(args ...interface{}) { | ||||
| 	std.Println(args...) | ||||
| } | ||||
|  | ||||
| // Infoln logs a message at level Info on the standard logger. | ||||
| func Infoln(args ...interface{}) { | ||||
| 	std.Infoln(args...) | ||||
| } | ||||
|  | ||||
| // Warnln logs a message at level Warn on the standard logger. | ||||
| func Warnln(args ...interface{}) { | ||||
| 	std.Warnln(args...) | ||||
| } | ||||
|  | ||||
| // Warningln logs a message at level Warn on the standard logger. | ||||
| func Warningln(args ...interface{}) { | ||||
| 	std.Warningln(args...) | ||||
| } | ||||
|  | ||||
| // Errorln logs a message at level Error on the standard logger. | ||||
| func Errorln(args ...interface{}) { | ||||
| 	std.Errorln(args...) | ||||
| } | ||||
|  | ||||
| // Panicln logs a message at level Panic on the standard logger. | ||||
| func Panicln(args ...interface{}) { | ||||
| 	std.Panicln(args...) | ||||
| } | ||||
|  | ||||
| // Fatalln logs a message at level Fatal on the standard logger. | ||||
| func Fatalln(args ...interface{}) { | ||||
| 	std.Fatalln(args...) | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/Sirupsen/logrus/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/Sirupsen/logrus/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| package logrus | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| const DefaultTimestampFormat = time.RFC3339 | ||||
|  | ||||
| // The Formatter interface is used to implement a custom Formatter. It takes an | ||||
| // `Entry`. It exposes all the fields, including the default ones: | ||||
| // | ||||
| // * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. | ||||
| // * `entry.Data["time"]`. The timestamp. | ||||
| // * `entry.Data["level"]. The level the entry was logged at. | ||||
| // | ||||
| // Any additional fields added with `WithField` or `WithFields` are also in | ||||
| // `entry.Data`. Format is expected to return an array of bytes which are then | ||||
| // logged to `logger.Out`. | ||||
| type Formatter interface { | ||||
| 	Format(*Entry) ([]byte, error) | ||||
| } | ||||
|  | ||||
| // This is to not silently overwrite `time`, `msg` and `level` fields when | ||||
| // dumping it. If this code wasn't there doing: | ||||
| // | ||||
| //  logrus.WithField("level", 1).Info("hello") | ||||
| // | ||||
| // Would just silently drop the user provided level. Instead with this code | ||||
| // it'll logged as: | ||||
| // | ||||
| //  {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} | ||||
| // | ||||
| // 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. | ||||
| func prefixFieldClashes(data Fields) { | ||||
| 	_, ok := data["time"] | ||||
| 	if ok { | ||||
| 		data["fields.time"] = data["time"] | ||||
| 	} | ||||
|  | ||||
| 	_, ok = data["msg"] | ||||
| 	if ok { | ||||
| 		data["fields.msg"] = data["msg"] | ||||
| 	} | ||||
|  | ||||
| 	_, ok = data["level"] | ||||
| 	if ok { | ||||
| 		data["fields.level"] = data["level"] | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										61
									
								
								vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/github.com/Sirupsen/logrus/hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/Sirupsen/logrus/hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| package logrus | ||||
|  | ||||
| // A hook to be fired when logging on the logging levels returned from | ||||
| // `Levels()` on your implementation of the interface. Note that this is not | ||||
| // fired in a goroutine or a channel with workers, you should handle such | ||||
| // functionality yourself if your call is non-blocking and you don't wish for | ||||
| // the logging calls for levels returned from `Levels()` to block. | ||||
| type Hook interface { | ||||
| 	Levels() []Level | ||||
| 	Fire(*Entry) error | ||||
| } | ||||
|  | ||||
| // Internal type for storing the hooks on a logger instance. | ||||
| type LevelHooks map[Level][]Hook | ||||
|  | ||||
| // Add a hook to an instance of logger. This is called with | ||||
| // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. | ||||
| func (hooks LevelHooks) Add(hook Hook) { | ||||
| 	for _, level := range hook.Levels() { | ||||
| 		hooks[level] = append(hooks[level], hook) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Fire all the hooks for the passed level. Used by `entry.log` to fire | ||||
| // appropriate hooks for a log entry. | ||||
| func (hooks LevelHooks) Fire(level Level, entry *Entry) error { | ||||
| 	for _, hook := range hooks[level] { | ||||
| 		if err := hook.Fire(entry); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| // +build !windows,!nacl,!plan9 | ||||
|  | ||||
| package logrus_syslog | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"log/syslog" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| // SyslogHook to send logs via syslog. | ||||
| type SyslogHook struct { | ||||
| 	Writer        *syslog.Writer | ||||
| 	SyslogNetwork string | ||||
| 	SyslogRaddr   string | ||||
| } | ||||
|  | ||||
| // Creates a hook to be added to an instance of logger. This is called with | ||||
| // `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` | ||||
| // `if err == nil { log.Hooks.Add(hook) }` | ||||
| func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { | ||||
| 	w, err := syslog.Dial(network, raddr, priority, tag) | ||||
| 	return &SyslogHook{w, network, raddr}, err | ||||
| } | ||||
|  | ||||
| func (hook *SyslogHook) Fire(entry *logrus.Entry) error { | ||||
| 	line, err := entry.String() | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	switch entry.Level { | ||||
| 	case logrus.PanicLevel: | ||||
| 		return hook.Writer.Crit(line) | ||||
| 	case logrus.FatalLevel: | ||||
| 		return hook.Writer.Crit(line) | ||||
| 	case logrus.ErrorLevel: | ||||
| 		return hook.Writer.Err(line) | ||||
| 	case logrus.WarnLevel: | ||||
| 		return hook.Writer.Warning(line) | ||||
| 	case logrus.InfoLevel: | ||||
| 		return hook.Writer.Info(line) | ||||
| 	case logrus.DebugLevel: | ||||
| 		return hook.Writer.Debug(line) | ||||
| 	default: | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (hook *SyslogHook) Levels() []logrus.Level { | ||||
| 	return logrus.AllLevels | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| package test | ||||
|  | ||||
| import ( | ||||
| 	"io/ioutil" | ||||
|  | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // test.Hook is a hook designed for dealing with logs in test scenarios. | ||||
| type Hook struct { | ||||
| 	Entries []*logrus.Entry | ||||
| } | ||||
|  | ||||
| // Installs a test hook for the global logger. | ||||
| func NewGlobal() *Hook { | ||||
|  | ||||
| 	hook := new(Hook) | ||||
| 	logrus.AddHook(hook) | ||||
|  | ||||
| 	return hook | ||||
|  | ||||
| } | ||||
|  | ||||
| // Installs a test hook for a given local logger. | ||||
| func NewLocal(logger *logrus.Logger) *Hook { | ||||
|  | ||||
| 	hook := new(Hook) | ||||
| 	logger.Hooks.Add(hook) | ||||
|  | ||||
| 	return hook | ||||
|  | ||||
| } | ||||
|  | ||||
| // Creates a discarding logger and installs the test hook. | ||||
| func NewNullLogger() (*logrus.Logger, *Hook) { | ||||
|  | ||||
| 	logger := logrus.New() | ||||
| 	logger.Out = ioutil.Discard | ||||
|  | ||||
| 	return logger, NewLocal(logger) | ||||
|  | ||||
| } | ||||
|  | ||||
| func (t *Hook) Fire(e *logrus.Entry) error { | ||||
| 	t.Entries = append(t.Entries, e) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *Hook) Levels() []logrus.Level { | ||||
| 	return logrus.AllLevels | ||||
| } | ||||
|  | ||||
| // LastEntry returns the last entry that was logged or nil. | ||||
| func (t *Hook) LastEntry() (l *logrus.Entry) { | ||||
|  | ||||
| 	if i := len(t.Entries) - 1; i < 0 { | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		return t.Entries[i] | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // Reset removes all Entries from this test hook. | ||||
| func (t *Hook) Reset() { | ||||
| 	t.Entries = make([]*logrus.Entry, 0) | ||||
| } | ||||
							
								
								
									
										41
									
								
								vendor/github.com/Sirupsen/logrus/json_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/Sirupsen/logrus/json_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| type JSONFormatter struct { | ||||
| 	// TimestampFormat sets the format used for marshaling timestamps. | ||||
| 	TimestampFormat string | ||||
| } | ||||
|  | ||||
| func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | ||||
| 	data := make(Fields, len(entry.Data)+3) | ||||
| 	for k, v := range entry.Data { | ||||
| 		switch v := v.(type) { | ||||
| 		case error: | ||||
| 			// Otherwise errors are ignored by `encoding/json` | ||||
| 			// https://github.com/Sirupsen/logrus/issues/137 | ||||
| 			data[k] = v.Error() | ||||
| 		default: | ||||
| 			data[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 	prefixFieldClashes(data) | ||||
|  | ||||
| 	timestampFormat := f.TimestampFormat | ||||
| 	if timestampFormat == "" { | ||||
| 		timestampFormat = DefaultTimestampFormat | ||||
| 	} | ||||
|  | ||||
| 	data["time"] = entry.Time.Format(timestampFormat) | ||||
| 	data["msg"] = entry.Message | ||||
| 	data["level"] = entry.Level.String() | ||||
|  | ||||
| 	serialized, err := json.Marshal(data) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) | ||||
| 	} | ||||
| 	return append(serialized, '\n'), nil | ||||
| } | ||||
							
								
								
									
										212
									
								
								vendor/github.com/Sirupsen/logrus/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								vendor/github.com/Sirupsen/logrus/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| type Logger struct { | ||||
| 	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a | ||||
| 	// file, or leave it default which is `os.Stderr`. You can also set this to | ||||
| 	// something more adventorous, such as logging to Kafka. | ||||
| 	Out io.Writer | ||||
| 	// Hooks for the logger instance. These allow firing events based on logging | ||||
| 	// levels and log entries. For example, to send errors to an error tracking | ||||
| 	// service, log to StatsD or dump the core on fatal errors. | ||||
| 	Hooks LevelHooks | ||||
| 	// All log entries pass through the formatter before logged to Out. The | ||||
| 	// included formatters are `TextFormatter` and `JSONFormatter` for which | ||||
| 	// TextFormatter is the default. In development (when a TTY is attached) it | ||||
| 	// logs with colors, but to a file it wouldn't. You can easily implement your | ||||
| 	// own that implements the `Formatter` interface, see the `README` or included | ||||
| 	// formatters for examples. | ||||
| 	Formatter Formatter | ||||
| 	// The logging level the logger should log at. This is typically (and defaults | ||||
| 	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be | ||||
| 	// logged. `logrus.Debug` is useful in | ||||
| 	Level Level | ||||
| 	// Used to sync writing to the log. | ||||
| 	mu sync.Mutex | ||||
| } | ||||
|  | ||||
| // Creates a new logger. Configuration should be set by changing `Formatter`, | ||||
| // `Out` and `Hooks` directly on the default logger instance. You can also just | ||||
| // instantiate your own: | ||||
| // | ||||
| //    var log = &Logger{ | ||||
| //      Out: os.Stderr, | ||||
| //      Formatter: new(JSONFormatter), | ||||
| //      Hooks: make(LevelHooks), | ||||
| //      Level: logrus.DebugLevel, | ||||
| //    } | ||||
| // | ||||
| // It's recommended to make this a global instance called `log`. | ||||
| func New() *Logger { | ||||
| 	return &Logger{ | ||||
| 		Out:       os.Stderr, | ||||
| 		Formatter: new(TextFormatter), | ||||
| 		Hooks:     make(LevelHooks), | ||||
| 		Level:     InfoLevel, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Adds a field to the log entry, note that you it doesn't log until you call | ||||
| // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. | ||||
| // If you want multiple fields, use `WithFields`. | ||||
| func (logger *Logger) WithField(key string, value interface{}) *Entry { | ||||
| 	return NewEntry(logger).WithField(key, value) | ||||
| } | ||||
|  | ||||
| // Adds a struct of fields to the log entry. All it does is call `WithField` for | ||||
| // each `Field`. | ||||
| func (logger *Logger) WithFields(fields Fields) *Entry { | ||||
| 	return NewEntry(logger).WithFields(fields) | ||||
| } | ||||
|  | ||||
| // Add an error as single field to the log entry.  All it does is call | ||||
| // `WithError` for the given `error`. | ||||
| func (logger *Logger) WithError(err error) *Entry { | ||||
| 	return NewEntry(logger).WithError(err) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Debugf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debugf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Infof(format string, args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Infof(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Printf(format string, args ...interface{}) { | ||||
| 	NewEntry(logger).Printf(format, args...) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warnf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warningf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Errorf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Errorf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Fatalf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatalf(format, args...) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Panicf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panicf(format, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Debug(args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debug(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Info(args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Info(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Print(args ...interface{}) { | ||||
| 	NewEntry(logger).Info(args...) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warn(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warn(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warning(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warn(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Error(args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Error(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Fatal(args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatal(args...) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Panic(args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panic(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Debugln(args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debugln(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Infoln(args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Infoln(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Println(args ...interface{}) { | ||||
| 	NewEntry(logger).Println(args...) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warnln(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnln(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Warningln(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnln(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Errorln(args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Errorln(args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Fatalln(args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatalln(args...) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func (logger *Logger) Panicln(args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panicln(args...) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/github.com/Sirupsen/logrus/logrus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/Sirupsen/logrus/logrus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Fields type, used to pass to `WithFields`. | ||||
| type Fields map[string]interface{} | ||||
|  | ||||
| // Level type | ||||
| type Level uint8 | ||||
|  | ||||
| // Convert the Level to a string. E.g. PanicLevel becomes "panic". | ||||
| func (level Level) String() string { | ||||
| 	switch level { | ||||
| 	case DebugLevel: | ||||
| 		return "debug" | ||||
| 	case InfoLevel: | ||||
| 		return "info" | ||||
| 	case WarnLevel: | ||||
| 		return "warning" | ||||
| 	case ErrorLevel: | ||||
| 		return "error" | ||||
| 	case FatalLevel: | ||||
| 		return "fatal" | ||||
| 	case PanicLevel: | ||||
| 		return "panic" | ||||
| 	} | ||||
|  | ||||
| 	return "unknown" | ||||
| } | ||||
|  | ||||
| // ParseLevel takes a string level and returns the Logrus log level constant. | ||||
| func ParseLevel(lvl string) (Level, error) { | ||||
| 	switch strings.ToLower(lvl) { | ||||
| 	case "panic": | ||||
| 		return PanicLevel, nil | ||||
| 	case "fatal": | ||||
| 		return FatalLevel, nil | ||||
| 	case "error": | ||||
| 		return ErrorLevel, nil | ||||
| 	case "warn", "warning": | ||||
| 		return WarnLevel, nil | ||||
| 	case "info": | ||||
| 		return InfoLevel, nil | ||||
| 	case "debug": | ||||
| 		return DebugLevel, nil | ||||
| 	} | ||||
|  | ||||
| 	var l Level | ||||
| 	return l, fmt.Errorf("not a valid logrus Level: %q", lvl) | ||||
| } | ||||
|  | ||||
| // A constant exposing all logging levels | ||||
| var AllLevels = []Level{ | ||||
| 	PanicLevel, | ||||
| 	FatalLevel, | ||||
| 	ErrorLevel, | ||||
| 	WarnLevel, | ||||
| 	InfoLevel, | ||||
| 	DebugLevel, | ||||
| } | ||||
|  | ||||
| // These are the different logging levels. You can set the logging level to log | ||||
| // on your instance of logger, obtained with `logrus.New()`. | ||||
| const ( | ||||
| 	// PanicLevel level, highest level of severity. Logs and then calls panic with the | ||||
| 	// message passed to Debug, Info, ... | ||||
| 	PanicLevel Level = iota | ||||
| 	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the | ||||
| 	// logging level is set to Panic. | ||||
| 	FatalLevel | ||||
| 	// ErrorLevel level. Logs. Used for errors that should definitely be noted. | ||||
| 	// Commonly used for hooks to send errors to an error tracking service. | ||||
| 	ErrorLevel | ||||
| 	// WarnLevel level. Non-critical entries that deserve eyes. | ||||
| 	WarnLevel | ||||
| 	// InfoLevel level. General operational entries about what's going on inside the | ||||
| 	// application. | ||||
| 	InfoLevel | ||||
| 	// DebugLevel level. Usually only enabled when debugging. Very verbose logging. | ||||
| 	DebugLevel | ||||
| ) | ||||
|  | ||||
| // Won't compile if StdLogger can't be realized by a log.Logger | ||||
| var ( | ||||
| 	_ StdLogger = &log.Logger{} | ||||
| 	_ StdLogger = &Entry{} | ||||
| 	_ StdLogger = &Logger{} | ||||
| ) | ||||
|  | ||||
| // StdLogger is what your logrus-enabled library should take, that way | ||||
| // it'll accept a stdlib logger and a logrus logger. There's no standard | ||||
| // interface, this is the closest we get, unfortunately. | ||||
| type StdLogger interface { | ||||
| 	Print(...interface{}) | ||||
| 	Printf(string, ...interface{}) | ||||
| 	Println(...interface{}) | ||||
|  | ||||
| 	Fatal(...interface{}) | ||||
| 	Fatalf(string, ...interface{}) | ||||
| 	Fatalln(...interface{}) | ||||
|  | ||||
| 	Panic(...interface{}) | ||||
| 	Panicf(string, ...interface{}) | ||||
| 	Panicln(...interface{}) | ||||
| } | ||||
|  | ||||
| // The FieldLogger interface generalizes the Entry and Logger types | ||||
| type FieldLogger interface { | ||||
| 	WithField(key string, value interface{}) *Entry | ||||
| 	WithFields(fields Fields) *Entry | ||||
| 	WithError(err error) *Entry | ||||
|  | ||||
| 	Debugf(format string, args ...interface{}) | ||||
| 	Infof(format string, args ...interface{}) | ||||
| 	Printf(format string, args ...interface{}) | ||||
| 	Warnf(format string, args ...interface{}) | ||||
| 	Warningf(format string, args ...interface{}) | ||||
| 	Errorf(format string, args ...interface{}) | ||||
| 	Fatalf(format string, args ...interface{}) | ||||
| 	Panicf(format string, args ...interface{}) | ||||
|  | ||||
| 	Debug(args ...interface{}) | ||||
| 	Info(args ...interface{}) | ||||
| 	Print(args ...interface{}) | ||||
| 	Warn(args ...interface{}) | ||||
| 	Warning(args ...interface{}) | ||||
| 	Error(args ...interface{}) | ||||
| 	Fatal(args ...interface{}) | ||||
| 	Panic(args ...interface{}) | ||||
|  | ||||
| 	Debugln(args ...interface{}) | ||||
| 	Infoln(args ...interface{}) | ||||
| 	Println(args ...interface{}) | ||||
| 	Warnln(args ...interface{}) | ||||
| 	Warningln(args ...interface{}) | ||||
| 	Errorln(args ...interface{}) | ||||
| 	Fatalln(args ...interface{}) | ||||
| 	Panicln(args ...interface{}) | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| // +build darwin freebsd openbsd netbsd dragonfly | ||||
|  | ||||
| package logrus | ||||
|  | ||||
| import "syscall" | ||||
|  | ||||
| const ioctlReadTermios = syscall.TIOCGETA | ||||
|  | ||||
| type Termios syscall.Termios | ||||
							
								
								
									
										12
									
								
								vendor/github.com/Sirupsen/logrus/terminal_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/Sirupsen/logrus/terminal_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| // Based on ssh/terminal: | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package logrus | ||||
|  | ||||
| import "syscall" | ||||
|  | ||||
| const ioctlReadTermios = syscall.TCGETS | ||||
|  | ||||
| type Termios syscall.Termios | ||||
							
								
								
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // Based on ssh/terminal: | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build linux darwin freebsd openbsd netbsd dragonfly | ||||
|  | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // IsTerminal returns true if stderr's file descriptor is a terminal. | ||||
| func IsTerminal() bool { | ||||
| 	fd := syscall.Stderr | ||||
| 	var termios Termios | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | ||||
| 	return err == 0 | ||||
| } | ||||
							
								
								
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // +build solaris | ||||
|  | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // IsTerminal returns true if the given file descriptor is a terminal. | ||||
| func IsTerminal() bool { | ||||
| 	_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) | ||||
| 	return err == nil | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| // Based on ssh/terminal: | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build windows | ||||
|  | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| var kernel32 = syscall.NewLazyDLL("kernel32.dll") | ||||
|  | ||||
| var ( | ||||
| 	procGetConsoleMode = kernel32.NewProc("GetConsoleMode") | ||||
| ) | ||||
|  | ||||
| // IsTerminal returns true if stderr's file descriptor is a terminal. | ||||
| func IsTerminal() bool { | ||||
| 	fd := syscall.Stderr | ||||
| 	var st uint32 | ||||
| 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	return r != 0 && e == 0 | ||||
| } | ||||
							
								
								
									
										161
									
								
								vendor/github.com/Sirupsen/logrus/text_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/Sirupsen/logrus/text_formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	nocolor = 0 | ||||
| 	red     = 31 | ||||
| 	green   = 32 | ||||
| 	yellow  = 33 | ||||
| 	blue    = 34 | ||||
| 	gray    = 37 | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	baseTimestamp time.Time | ||||
| 	isTerminal    bool | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	baseTimestamp = time.Now() | ||||
| 	isTerminal = IsTerminal() | ||||
| } | ||||
|  | ||||
| func miniTS() int { | ||||
| 	return int(time.Since(baseTimestamp) / time.Second) | ||||
| } | ||||
|  | ||||
| type TextFormatter struct { | ||||
| 	// Set to true to bypass checking for a TTY before outputting colors. | ||||
| 	ForceColors bool | ||||
|  | ||||
| 	// Force disabling colors. | ||||
| 	DisableColors bool | ||||
|  | ||||
| 	// Disable timestamp logging. useful when output is redirected to logging | ||||
| 	// system that already adds timestamps. | ||||
| 	DisableTimestamp bool | ||||
|  | ||||
| 	// Enable logging the full timestamp when a TTY is attached instead of just | ||||
| 	// the time passed since beginning of execution. | ||||
| 	FullTimestamp bool | ||||
|  | ||||
| 	// TimestampFormat to use for display when a full timestamp is printed | ||||
| 	TimestampFormat string | ||||
|  | ||||
| 	// The fields are sorted by default for a consistent output. For applications | ||||
| 	// that log extremely frequently and don't use the JSON formatter this may not | ||||
| 	// be desired. | ||||
| 	DisableSorting bool | ||||
| } | ||||
|  | ||||
| func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||
| 	var keys []string = make([]string, 0, len(entry.Data)) | ||||
| 	for k := range entry.Data { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
|  | ||||
| 	if !f.DisableSorting { | ||||
| 		sort.Strings(keys) | ||||
| 	} | ||||
|  | ||||
| 	b := &bytes.Buffer{} | ||||
|  | ||||
| 	prefixFieldClashes(entry.Data) | ||||
|  | ||||
| 	isColorTerminal := isTerminal && (runtime.GOOS != "windows") | ||||
| 	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors | ||||
|  | ||||
| 	timestampFormat := f.TimestampFormat | ||||
| 	if timestampFormat == "" { | ||||
| 		timestampFormat = DefaultTimestampFormat | ||||
| 	} | ||||
| 	if isColored { | ||||
| 		f.printColored(b, entry, keys, timestampFormat) | ||||
| 	} else { | ||||
| 		if !f.DisableTimestamp { | ||||
| 			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) | ||||
| 		} | ||||
| 		f.appendKeyValue(b, "level", entry.Level.String()) | ||||
| 		if entry.Message != "" { | ||||
| 			f.appendKeyValue(b, "msg", entry.Message) | ||||
| 		} | ||||
| 		for _, key := range keys { | ||||
| 			f.appendKeyValue(b, key, entry.Data[key]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.WriteByte('\n') | ||||
| 	return b.Bytes(), nil | ||||
| } | ||||
|  | ||||
| func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { | ||||
| 	var levelColor int | ||||
| 	switch entry.Level { | ||||
| 	case DebugLevel: | ||||
| 		levelColor = gray | ||||
| 	case WarnLevel: | ||||
| 		levelColor = yellow | ||||
| 	case ErrorLevel, FatalLevel, PanicLevel: | ||||
| 		levelColor = red | ||||
| 	default: | ||||
| 		levelColor = blue | ||||
| 	} | ||||
|  | ||||
| 	levelText := strings.ToUpper(entry.Level.String())[0:4] | ||||
|  | ||||
| 	if !f.FullTimestamp { | ||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | ||||
| 	} else { | ||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) | ||||
| 	} | ||||
| 	for _, k := range keys { | ||||
| 		v := entry.Data[k] | ||||
| 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func needsQuoting(text string) bool { | ||||
| 	for _, ch := range text { | ||||
| 		if !((ch >= 'a' && ch <= 'z') || | ||||
| 			(ch >= 'A' && ch <= 'Z') || | ||||
| 			(ch >= '0' && ch <= '9') || | ||||
| 			ch == '-' || ch == '.') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | ||||
|  | ||||
| 	b.WriteString(key) | ||||
| 	b.WriteByte('=') | ||||
|  | ||||
| 	switch value := value.(type) { | ||||
| 	case string: | ||||
| 		if needsQuoting(value) { | ||||
| 			b.WriteString(value) | ||||
| 		} else { | ||||
| 			fmt.Fprintf(b, "%q", value) | ||||
| 		} | ||||
| 	case error: | ||||
| 		errmsg := value.Error() | ||||
| 		if needsQuoting(errmsg) { | ||||
| 			b.WriteString(errmsg) | ||||
| 		} else { | ||||
| 			fmt.Fprintf(b, "%q", value) | ||||
| 		} | ||||
| 	default: | ||||
| 		fmt.Fprint(b, value) | ||||
| 	} | ||||
|  | ||||
| 	b.WriteByte(' ') | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/github.com/Sirupsen/logrus/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/Sirupsen/logrus/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| package logrus | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"runtime" | ||||
| ) | ||||
|  | ||||
| func (logger *Logger) Writer() *io.PipeWriter { | ||||
| 	reader, writer := io.Pipe() | ||||
|  | ||||
| 	go logger.writerScanner(reader) | ||||
| 	runtime.SetFinalizer(writer, writerFinalizer) | ||||
|  | ||||
| 	return writer | ||||
| } | ||||
|  | ||||
| func (logger *Logger) writerScanner(reader *io.PipeReader) { | ||||
| 	scanner := bufio.NewScanner(reader) | ||||
| 	for scanner.Scan() { | ||||
| 		logger.Print(scanner.Text()) | ||||
| 	} | ||||
| 	if err := scanner.Err(); err != nil { | ||||
| 		logger.Errorf("Error while reading from Writer: %s", err) | ||||
| 	} | ||||
| 	reader.Close() | ||||
| } | ||||
|  | ||||
| func writerFinalizer(writer *io.PipeWriter) { | ||||
| 	writer.Close() | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/github.com/alecthomas/log4go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/alecthomas/log4go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | ||||
|  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | ||||
|  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the | ||||
|    documentation and/or other materials provided with the distribution. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
| THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										288
									
								
								vendor/github.com/alecthomas/log4go/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								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/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								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/alecthomas/log4go/examples/FileLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								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/alecthomas/log4go/examples/SimpleNetLogServer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								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/alecthomas/log4go/examples/SocketLogWriter_Manual.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								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/alecthomas/log4go/examples/XMLConfigurationExample.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								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/alecthomas/log4go/filelog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								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/alecthomas/log4go/log4go.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								vendor/github.com/alecthomas/log4go/log4go.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,484 @@ | ||||
| // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||
|  | ||||
| // Package log4go provides level-based and highly configurable logging. | ||||
| // | ||||
| // Enhanced Logging | ||||
| // | ||||
| // This is inspired by the logging functionality in Java.  Essentially, you create a Logger | ||||
| // object and create output filters for it.  You can send whatever you want to the Logger, | ||||
| // and it will filter that based on your settings and send it to the outputs.  This way, you | ||||
| // can put as much debug code in your program as you want, and when you're done you can filter | ||||
| // out the mundane messages so only the important ones show up. | ||||
| // | ||||
| // Utility functions are provided to make life easier. Here is some example code to get started: | ||||
| // | ||||
| // log := log4go.NewLogger() | ||||
| // log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter()) | ||||
| // log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true)) | ||||
| // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) | ||||
| // | ||||
| // The first two lines can be combined with the utility NewDefaultLogger: | ||||
| // | ||||
| // log := log4go.NewDefaultLogger(log4go.DEBUG) | ||||
| // log.AddFilter("log",    log4go.FINE,  log4go.NewFileLogWriter("example.log", true)) | ||||
| // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) | ||||
| // | ||||
| // Usage notes: | ||||
| // - The ConsoleLogWriter does not display the source of the message to standard | ||||
| //   output, but the FileLogWriter does. | ||||
| // - The utility functions (Info, Debug, Warn, etc) derive their source from the | ||||
| //   calling function, and this incurs extra overhead. | ||||
| // | ||||
| // Changes from 2.0: | ||||
| // - The external interface has remained mostly stable, but a lot of the | ||||
| //   internals have been changed, so if you depended on any of this or created | ||||
| //   your own LogWriter, then you will probably have to update your code.  In | ||||
| //   particular, Logger is now a map and ConsoleLogWriter is now a channel | ||||
| //   behind-the-scenes, and the LogWrite method no longer has return values. | ||||
| // | ||||
| // Future work: (please let me know if you think I should work on any of these particularly) | ||||
| // - Log file rotation | ||||
| // - Logging configuration files ala log4j | ||||
| // - Have the ability to remove filters? | ||||
| // - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows | ||||
| //   for another method of logging | ||||
| // - Add an XML filter type | ||||
| package log4go | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Version information | ||||
| const ( | ||||
| 	L4G_VERSION = "log4go-v3.0.1" | ||||
| 	L4G_MAJOR   = 3 | ||||
| 	L4G_MINOR   = 0 | ||||
| 	L4G_BUILD   = 1 | ||||
| ) | ||||
|  | ||||
| /****** Constants ******/ | ||||
|  | ||||
| // These are the integer logging levels used by the logger | ||||
| type Level int | ||||
|  | ||||
| const ( | ||||
| 	FINEST Level = iota | ||||
| 	FINE | ||||
| 	DEBUG | ||||
| 	TRACE | ||||
| 	INFO | ||||
| 	WARNING | ||||
| 	ERROR | ||||
| 	CRITICAL | ||||
| ) | ||||
|  | ||||
| // Logging level strings | ||||
| var ( | ||||
| 	levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} | ||||
| ) | ||||
|  | ||||
| func (l Level) String() string { | ||||
| 	if l < 0 || int(l) > len(levelStrings) { | ||||
| 		return "UNKNOWN" | ||||
| 	} | ||||
| 	return levelStrings[int(l)] | ||||
| } | ||||
|  | ||||
| /****** Variables ******/ | ||||
| var ( | ||||
| 	// LogBufferLength specifies how many log messages a particular log4go | ||||
| 	// logger can buffer at a time before writing them. | ||||
| 	LogBufferLength = 32 | ||||
| ) | ||||
|  | ||||
| /****** LogRecord ******/ | ||||
|  | ||||
| // A LogRecord contains all of the pertinent information for each message | ||||
| type LogRecord struct { | ||||
| 	Level   Level     // The log level | ||||
| 	Created time.Time // The time at which the log message was created (nanoseconds) | ||||
| 	Source  string    // The message source | ||||
| 	Message string    // The log message | ||||
| } | ||||
|  | ||||
| /****** LogWriter ******/ | ||||
|  | ||||
| // This is an interface for anything that should be able to write logs | ||||
| type LogWriter interface { | ||||
| 	// This will be called to log a LogRecord message. | ||||
| 	LogWrite(rec *LogRecord) | ||||
|  | ||||
| 	// This should clean up anything lingering about the LogWriter, as it is called before | ||||
| 	// the LogWriter is removed.  LogWrite should not be called after Close. | ||||
| 	Close() | ||||
| } | ||||
|  | ||||
| /****** Logger ******/ | ||||
|  | ||||
| // A Filter represents the log level below which no log records are written to | ||||
| // the associated LogWriter. | ||||
| type Filter struct { | ||||
| 	Level Level | ||||
| 	LogWriter | ||||
| } | ||||
|  | ||||
| // A Logger represents a collection of Filters through which log messages are | ||||
| // written. | ||||
| type Logger map[string]*Filter | ||||
|  | ||||
| // Create a new logger. | ||||
| // | ||||
| // DEPRECATED: Use make(Logger) instead. | ||||
| func NewLogger() Logger { | ||||
| 	os.Stderr.WriteString("warning: use of deprecated NewLogger\n") | ||||
| 	return make(Logger) | ||||
| } | ||||
|  | ||||
| // Create a new logger with a "stdout" filter configured to send log messages at | ||||
| // or above lvl to standard output. | ||||
| // | ||||
| // DEPRECATED: use NewDefaultLogger instead. | ||||
| func NewConsoleLogger(lvl Level) Logger { | ||||
| 	os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n") | ||||
| 	return Logger{ | ||||
| 		"stdout": &Filter{lvl, NewConsoleLogWriter()}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create a new logger with a "stdout" filter configured to send log messages at | ||||
| // or above lvl to standard output. | ||||
| func NewDefaultLogger(lvl Level) Logger { | ||||
| 	return Logger{ | ||||
| 		"stdout": &Filter{lvl, NewConsoleLogWriter()}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Closes all log writers in preparation for exiting the program or a | ||||
| // reconfiguration of logging.  Calling this is not really imperative, unless | ||||
| // you want to guarantee that all log messages are written.  Close removes | ||||
| // all filters (and thus all LogWriters) from the logger. | ||||
| func (log Logger) Close() { | ||||
| 	// Close all open loggers | ||||
| 	for name, filt := range log { | ||||
| 		filt.Close() | ||||
| 		delete(log, name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add a new LogWriter to the Logger which will only log messages at lvl or | ||||
| // higher.  This function should not be called from multiple goroutines. | ||||
| // Returns the logger for chaining. | ||||
| func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger { | ||||
| 	log[name] = &Filter{lvl, writer} | ||||
| 	return log | ||||
| } | ||||
|  | ||||
| /******* Logging *******/ | ||||
| // Send a formatted log message internally | ||||
| func (log Logger) intLogf(lvl Level, format string, args ...interface{}) { | ||||
| 	skip := true | ||||
|  | ||||
| 	// Determine if any logging will be done | ||||
| 	for _, filt := range log { | ||||
| 		if lvl >= filt.Level { | ||||
| 			skip = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if skip { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Determine caller func | ||||
| 	pc, _, lineno, ok := runtime.Caller(2) | ||||
| 	src := "" | ||||
| 	if ok { | ||||
| 		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) | ||||
| 	} | ||||
|  | ||||
| 	msg := format | ||||
| 	if len(args) > 0 { | ||||
| 		msg = fmt.Sprintf(format, args...) | ||||
| 	} | ||||
|  | ||||
| 	// Make the log record | ||||
| 	rec := &LogRecord{ | ||||
| 		Level:   lvl, | ||||
| 		Created: time.Now(), | ||||
| 		Source:  src, | ||||
| 		Message: msg, | ||||
| 	} | ||||
|  | ||||
| 	// Dispatch the logs | ||||
| 	for _, filt := range log { | ||||
| 		if lvl < filt.Level { | ||||
| 			continue | ||||
| 		} | ||||
| 		filt.LogWrite(rec) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Send a closure log message internally | ||||
| func (log Logger) intLogc(lvl Level, closure func() string) { | ||||
| 	skip := true | ||||
|  | ||||
| 	// Determine if any logging will be done | ||||
| 	for _, filt := range log { | ||||
| 		if lvl >= filt.Level { | ||||
| 			skip = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if skip { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Determine caller func | ||||
| 	pc, _, lineno, ok := runtime.Caller(2) | ||||
| 	src := "" | ||||
| 	if ok { | ||||
| 		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) | ||||
| 	} | ||||
|  | ||||
| 	// Make the log record | ||||
| 	rec := &LogRecord{ | ||||
| 		Level:   lvl, | ||||
| 		Created: time.Now(), | ||||
| 		Source:  src, | ||||
| 		Message: closure(), | ||||
| 	} | ||||
|  | ||||
| 	// Dispatch the logs | ||||
| 	for _, filt := range log { | ||||
| 		if lvl < filt.Level { | ||||
| 			continue | ||||
| 		} | ||||
| 		filt.LogWrite(rec) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Send a log message with manual level, source, and message. | ||||
| func (log Logger) Log(lvl Level, source, message string) { | ||||
| 	skip := true | ||||
|  | ||||
| 	// Determine if any logging will be done | ||||
| 	for _, filt := range log { | ||||
| 		if lvl >= filt.Level { | ||||
| 			skip = false | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if skip { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Make the log record | ||||
| 	rec := &LogRecord{ | ||||
| 		Level:   lvl, | ||||
| 		Created: time.Now(), | ||||
| 		Source:  source, | ||||
| 		Message: message, | ||||
| 	} | ||||
|  | ||||
| 	// Dispatch the logs | ||||
| 	for _, filt := range log { | ||||
| 		if lvl < filt.Level { | ||||
| 			continue | ||||
| 		} | ||||
| 		filt.LogWrite(rec) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Logf logs a formatted log message at the given log level, using the caller as | ||||
| // its source. | ||||
| func (log Logger) Logf(lvl Level, format string, args ...interface{}) { | ||||
| 	log.intLogf(lvl, format, args...) | ||||
| } | ||||
|  | ||||
| // Logc logs a string returned by the closure at the given log level, using the caller as | ||||
| // its source.  If no log message would be written, the closure is never called. | ||||
| func (log Logger) Logc(lvl Level, closure func() string) { | ||||
| 	log.intLogc(lvl, closure) | ||||
| } | ||||
|  | ||||
| // Finest logs a message at the finest log level. | ||||
| // See Debug for an explanation of the arguments. | ||||
| func (log Logger) Finest(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = FINEST | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		log.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		log.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Fine logs a message at the fine log level. | ||||
| // See Debug for an explanation of the arguments. | ||||
| func (log Logger) Fine(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = FINE | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		log.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		log.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Debug is a utility method for debug log messages. | ||||
| // The behavior of Debug depends on the first argument: | ||||
| // - arg0 is a string | ||||
| //   When given a string as the first argument, this behaves like Logf but with | ||||
| //   the DEBUG log level: the first argument is interpreted as a format for the | ||||
| //   latter arguments. | ||||
| // - arg0 is a func()string | ||||
| //   When given a closure of type func()string, this logs the string returned by | ||||
| //   the closure iff it will be logged.  The closure runs at most one time. | ||||
| // - arg0 is interface{} | ||||
| //   When given anything else, the log message will be each of the arguments | ||||
| //   formatted with %v and separated by spaces (ala Sprint). | ||||
| func (log Logger) Debug(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = DEBUG | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		log.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		log.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Trace logs a message at the trace log level. | ||||
| // See Debug for an explanation of the arguments. | ||||
| func (log Logger) Trace(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = TRACE | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		log.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		log.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Info logs a message at the info log level. | ||||
| // See Debug for an explanation of the arguments. | ||||
| func (log Logger) Info(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = INFO | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		log.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		log.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Warn logs a message at the warning log level and returns the formatted error. | ||||
| // At the warning level and higher, there is no performance benefit if the | ||||
| // message is not actually logged, because all formats are processed and all | ||||
| // closures are executed to format the error message. | ||||
| // See Debug for further explanation of the arguments. | ||||
| func (log Logger) Warn(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = WARNING | ||||
| 	) | ||||
| 	var msg string | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		msg = fmt.Sprintf(first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		msg = first() | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| 	log.intLogf(lvl, msg) | ||||
| 	return errors.New(msg) | ||||
| } | ||||
|  | ||||
| // Error logs a message at the error log level and returns the formatted error, | ||||
| // See Warn for an explanation of the performance and Debug for an explanation | ||||
| // of the parameters. | ||||
| func (log Logger) Error(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = ERROR | ||||
| 	) | ||||
| 	var msg string | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		msg = fmt.Sprintf(first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		msg = first() | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| 	log.intLogf(lvl, msg) | ||||
| 	return errors.New(msg) | ||||
| } | ||||
|  | ||||
| // Critical logs a message at the critical log level and returns the formatted error, | ||||
| // See Warn for an explanation of the performance and Debug for an explanation | ||||
| // of the parameters. | ||||
| func (log Logger) Critical(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = CRITICAL | ||||
| 	) | ||||
| 	var msg string | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		msg = fmt.Sprintf(first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		msg = first() | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| 	log.intLogf(lvl, msg) | ||||
| 	return errors.New(msg) | ||||
| } | ||||
							
								
								
									
										126
									
								
								vendor/github.com/alecthomas/log4go/pattlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/alecthomas/log4go/pattlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||
|  | ||||
| package log4go | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M" | ||||
| 	FORMAT_SHORT   = "[%t %d] [%L] %M" | ||||
| 	FORMAT_ABBREV  = "[%L] %M" | ||||
| ) | ||||
|  | ||||
| type formatCacheType struct { | ||||
| 	LastUpdateSeconds    int64 | ||||
| 	shortTime, shortDate string | ||||
| 	longTime, longDate   string | ||||
| } | ||||
|  | ||||
| var formatCache = &formatCacheType{} | ||||
|  | ||||
| // Known format codes: | ||||
| // %T - Time (15:04:05 MST) | ||||
| // %t - Time (15:04) | ||||
| // %D - Date (2006/01/02) | ||||
| // %d - Date (01/02/06) | ||||
| // %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) | ||||
| // %S - Source | ||||
| // %M - Message | ||||
| // Ignores unknown formats | ||||
| // Recommended: "[%D %T] [%L] (%S) %M" | ||||
| func FormatLogRecord(format string, rec *LogRecord) string { | ||||
| 	if rec == nil { | ||||
| 		return "<nil>" | ||||
| 	} | ||||
| 	if len(format) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	out := bytes.NewBuffer(make([]byte, 0, 64)) | ||||
| 	secs := rec.Created.UnixNano() / 1e9 | ||||
|  | ||||
| 	cache := *formatCache | ||||
| 	if cache.LastUpdateSeconds != secs { | ||||
| 		month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year() | ||||
| 		hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second() | ||||
| 		zone, _ := rec.Created.Zone() | ||||
| 		updated := &formatCacheType{ | ||||
| 			LastUpdateSeconds: secs, | ||||
| 			shortTime:         fmt.Sprintf("%02d:%02d", hour, minute), | ||||
| 			shortDate:         fmt.Sprintf("%02d/%02d/%02d", day, month, year%100), | ||||
| 			longTime:          fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone), | ||||
| 			longDate:          fmt.Sprintf("%04d/%02d/%02d", year, month, day), | ||||
| 		} | ||||
| 		cache = *updated | ||||
| 		formatCache = updated | ||||
| 	} | ||||
|  | ||||
| 	// Split the string into pieces by % signs | ||||
| 	pieces := bytes.Split([]byte(format), []byte{'%'}) | ||||
|  | ||||
| 	// Iterate over the pieces, replacing known formats | ||||
| 	for i, piece := range pieces { | ||||
| 		if i > 0 && len(piece) > 0 { | ||||
| 			switch piece[0] { | ||||
| 			case 'T': | ||||
| 				out.WriteString(cache.longTime) | ||||
| 			case 't': | ||||
| 				out.WriteString(cache.shortTime) | ||||
| 			case 'D': | ||||
| 				out.WriteString(cache.longDate) | ||||
| 			case 'd': | ||||
| 				out.WriteString(cache.shortDate) | ||||
| 			case 'L': | ||||
| 				out.WriteString(levelStrings[rec.Level]) | ||||
| 			case 'S': | ||||
| 				out.WriteString(rec.Source) | ||||
| 			case 's': | ||||
| 				slice := strings.Split(rec.Source, "/") | ||||
| 				out.WriteString(slice[len(slice)-1]) | ||||
| 			case 'M': | ||||
| 				out.WriteString(rec.Message) | ||||
| 			} | ||||
| 			if len(piece) > 1 { | ||||
| 				out.Write(piece[1:]) | ||||
| 			} | ||||
| 		} else if len(piece) > 0 { | ||||
| 			out.Write(piece) | ||||
| 		} | ||||
| 	} | ||||
| 	out.WriteByte('\n') | ||||
|  | ||||
| 	return out.String() | ||||
| } | ||||
|  | ||||
| // This is the standard writer that prints to standard output. | ||||
| type FormatLogWriter chan *LogRecord | ||||
|  | ||||
| // This creates a new FormatLogWriter | ||||
| func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter { | ||||
| 	records := make(FormatLogWriter, LogBufferLength) | ||||
| 	go records.run(out, format) | ||||
| 	return records | ||||
| } | ||||
|  | ||||
| func (w FormatLogWriter) run(out io.Writer, format string) { | ||||
| 	for rec := range w { | ||||
| 		fmt.Fprint(out, FormatLogRecord(format, rec)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // This is the FormatLogWriter's output method.  This will block if the output | ||||
| // buffer is full. | ||||
| func (w FormatLogWriter) LogWrite(rec *LogRecord) { | ||||
| 	w <- rec | ||||
| } | ||||
|  | ||||
| // Close stops the logger from sending messages to standard output.  Attempts to | ||||
| // send log messages to this logger after a Close have undefined behavior. | ||||
| func (w FormatLogWriter) Close() { | ||||
| 	close(w) | ||||
| } | ||||
							
								
								
									
										57
									
								
								vendor/github.com/alecthomas/log4go/socklog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/alecthomas/log4go/socklog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||
|  | ||||
| package log4go | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| // This log writer sends output to a socket | ||||
| type SocketLogWriter chan *LogRecord | ||||
|  | ||||
| // This is the SocketLogWriter's output method | ||||
| func (w SocketLogWriter) LogWrite(rec *LogRecord) { | ||||
| 	w <- rec | ||||
| } | ||||
|  | ||||
| func (w SocketLogWriter) Close() { | ||||
| 	close(w) | ||||
| } | ||||
|  | ||||
| func NewSocketLogWriter(proto, hostport string) SocketLogWriter { | ||||
| 	sock, err := net.Dial(proto, hostport) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	w := SocketLogWriter(make(chan *LogRecord, LogBufferLength)) | ||||
|  | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			if sock != nil && proto == "tcp" { | ||||
| 				sock.Close() | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		for rec := range w { | ||||
| 			// Marshall into JSON | ||||
| 			js, err := json.Marshal(rec) | ||||
| 			if err != nil { | ||||
| 				fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			_, err = sock.Write(js) | ||||
| 			if err != nil { | ||||
| 				fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return w | ||||
| } | ||||
							
								
								
									
										49
									
								
								vendor/github.com/alecthomas/log4go/termlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/alecthomas/log4go/termlog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||
|  | ||||
| package log4go | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| var stdout io.Writer = os.Stdout | ||||
|  | ||||
| // This is the standard writer that prints to standard output. | ||||
| type ConsoleLogWriter struct { | ||||
| 	format string | ||||
| 	w      chan *LogRecord | ||||
| } | ||||
|  | ||||
| // This creates a new ConsoleLogWriter | ||||
| func NewConsoleLogWriter() *ConsoleLogWriter { | ||||
| 	consoleWriter := &ConsoleLogWriter{ | ||||
| 		format: "[%T %D] [%L] (%S) %M", | ||||
| 		w:      make(chan *LogRecord, LogBufferLength), | ||||
| 	} | ||||
| 	go consoleWriter.run(stdout) | ||||
| 	return consoleWriter | ||||
| } | ||||
| func (c *ConsoleLogWriter) SetFormat(format string) { | ||||
| 	c.format = format | ||||
| } | ||||
| func (c *ConsoleLogWriter) run(out io.Writer) { | ||||
| 	for rec := range c.w { | ||||
| 		fmt.Fprint(out, FormatLogRecord(c.format, rec)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // This is the ConsoleLogWriter's output method.  This will block if the output | ||||
| // buffer is full. | ||||
| func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) { | ||||
| 	c.w <- rec | ||||
| } | ||||
|  | ||||
| // Close stops the logger from sending messages to standard output.  Attempts to | ||||
| // send log messages to this logger after a Close have undefined behavior. | ||||
| func (c *ConsoleLogWriter) Close() { | ||||
| 	close(c.w) | ||||
| 	time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete | ||||
| } | ||||
							
								
								
									
										278
									
								
								vendor/github.com/alecthomas/log4go/wrapper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/github.com/alecthomas/log4go/wrapper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| // Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved. | ||||
|  | ||||
| package log4go | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	Global Logger | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	Global = NewDefaultLogger(DEBUG) | ||||
| } | ||||
|  | ||||
| // Wrapper for (*Logger).LoadConfiguration | ||||
| func LoadConfiguration(filename string) { | ||||
| 	Global.LoadConfiguration(filename) | ||||
| } | ||||
|  | ||||
| // Wrapper for (*Logger).AddFilter | ||||
| func AddFilter(name string, lvl Level, writer LogWriter) { | ||||
| 	Global.AddFilter(name, lvl, writer) | ||||
| } | ||||
|  | ||||
| // Wrapper for (*Logger).Close (closes and removes all logwriters) | ||||
| func Close() { | ||||
| 	Global.Close() | ||||
| } | ||||
|  | ||||
| func Crash(args ...interface{}) { | ||||
| 	if len(args) > 0 { | ||||
| 		Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...) | ||||
| 	} | ||||
| 	panic(args) | ||||
| } | ||||
|  | ||||
| // Logs the given message and crashes the program | ||||
| func Crashf(format string, args ...interface{}) { | ||||
| 	Global.intLogf(CRITICAL, format, args...) | ||||
| 	Global.Close() // so that hopefully the messages get logged | ||||
| 	panic(fmt.Sprintf(format, args...)) | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Exit(args ...interface{}) { | ||||
| 	if len(args) > 0 { | ||||
| 		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) | ||||
| 	} | ||||
| 	Global.Close() // so that hopefully the messages get logged | ||||
| 	os.Exit(0) | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Exitf(format string, args ...interface{}) { | ||||
| 	Global.intLogf(ERROR, format, args...) | ||||
| 	Global.Close() // so that hopefully the messages get logged | ||||
| 	os.Exit(0) | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Stderr(args ...interface{}) { | ||||
| 	if len(args) > 0 { | ||||
| 		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Stderrf(format string, args ...interface{}) { | ||||
| 	Global.intLogf(ERROR, format, args...) | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Stdout(args ...interface{}) { | ||||
| 	if len(args) > 0 { | ||||
| 		Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Compatibility with `log` | ||||
| func Stdoutf(format string, args ...interface{}) { | ||||
| 	Global.intLogf(INFO, format, args...) | ||||
| } | ||||
|  | ||||
| // Send a log message manually | ||||
| // Wrapper for (*Logger).Log | ||||
| func Log(lvl Level, source, message string) { | ||||
| 	Global.Log(lvl, source, message) | ||||
| } | ||||
|  | ||||
| // Send a formatted log message easily | ||||
| // Wrapper for (*Logger).Logf | ||||
| func Logf(lvl Level, format string, args ...interface{}) { | ||||
| 	Global.intLogf(lvl, format, args...) | ||||
| } | ||||
|  | ||||
| // Send a closure log message | ||||
| // Wrapper for (*Logger).Logc | ||||
| func Logc(lvl Level, closure func() string) { | ||||
| 	Global.intLogc(lvl, closure) | ||||
| } | ||||
|  | ||||
| // Utility for finest log messages (see Debug() for parameter explanation) | ||||
| // Wrapper for (*Logger).Finest | ||||
| func Finest(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = FINEST | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		Global.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Utility for fine log messages (see Debug() for parameter explanation) | ||||
| // Wrapper for (*Logger).Fine | ||||
| func Fine(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = FINE | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		Global.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Utility for debug log messages | ||||
| // When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments) | ||||
| // When given a closure of type func()string, this logs the string returned by the closure iff it will be logged.  The closure runs at most one time. | ||||
| // When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint). | ||||
| // Wrapper for (*Logger).Debug | ||||
| func Debug(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = DEBUG | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		Global.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Utility for trace log messages (see Debug() for parameter explanation) | ||||
| // Wrapper for (*Logger).Trace | ||||
| func Trace(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = TRACE | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		Global.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Utility for info log messages (see Debug() for parameter explanation) | ||||
| // Wrapper for (*Logger).Info | ||||
| func Info(arg0 interface{}, args ...interface{}) { | ||||
| 	const ( | ||||
| 		lvl = INFO | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		Global.intLogc(lvl, first) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation) | ||||
| // These functions will execute a closure exactly once, to build the error message for the return | ||||
| // Wrapper for (*Logger).Warn | ||||
| func Warn(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = WARNING | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 		return errors.New(fmt.Sprintf(first, args...)) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		str := first() | ||||
| 		Global.intLogf(lvl, "%s", str) | ||||
| 		return errors.New(str) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) | ||||
| // These functions will execute a closure exactly once, to build the error message for the return | ||||
| // Wrapper for (*Logger).Error | ||||
| func Error(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = ERROR | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 		return errors.New(fmt.Sprintf(first, args...)) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		str := first() | ||||
| 		Global.intLogf(lvl, "%s", str) | ||||
| 		return errors.New(str) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation) | ||||
| // These functions will execute a closure exactly once, to build the error message for the return | ||||
| // Wrapper for (*Logger).Critical | ||||
| func Critical(arg0 interface{}, args ...interface{}) error { | ||||
| 	const ( | ||||
| 		lvl = CRITICAL | ||||
| 	) | ||||
| 	switch first := arg0.(type) { | ||||
| 	case string: | ||||
| 		// Use the string as a format string | ||||
| 		Global.intLogf(lvl, first, args...) | ||||
| 		return errors.New(fmt.Sprintf(first, args...)) | ||||
| 	case func() string: | ||||
| 		// Log the closure (no other arguments used) | ||||
| 		str := first() | ||||
| 		Global.intLogf(lvl, "%s", str) | ||||
| 		return errors.New(str) | ||||
| 	default: | ||||
| 		// Build a format string so that it will be similar to Sprint | ||||
| 		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) | ||||
| 		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/github.com/gorilla/schema/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/gorilla/schema/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| Copyright (c) 2012 Rodrigo Moraes. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
|  | ||||
| 	 * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
| 	 * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
| 	 * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										245
									
								
								vendor/github.com/gorilla/schema/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								vendor/github.com/gorilla/schema/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,245 @@ | ||||
| // Copyright 2012 The Gorilla 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 schema | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| var invalidPath = errors.New("schema: invalid path") | ||||
|  | ||||
| // newCache returns a new cache. | ||||
| func newCache() *cache { | ||||
| 	c := cache{ | ||||
| 		m:       make(map[reflect.Type]*structInfo), | ||||
| 		conv:    make(map[reflect.Kind]Converter), | ||||
| 		regconv: make(map[reflect.Type]Converter), | ||||
| 		tag:     "schema", | ||||
| 	} | ||||
| 	for k, v := range converters { | ||||
| 		c.conv[k] = v | ||||
| 	} | ||||
| 	return &c | ||||
| } | ||||
|  | ||||
| // cache caches meta-data about a struct. | ||||
| type cache struct { | ||||
| 	l       sync.RWMutex | ||||
| 	m       map[reflect.Type]*structInfo | ||||
| 	conv    map[reflect.Kind]Converter | ||||
| 	regconv map[reflect.Type]Converter | ||||
| 	tag     string | ||||
| } | ||||
|  | ||||
| // parsePath parses a path in dotted notation verifying that it is a valid | ||||
| // path to a struct field. | ||||
| // | ||||
| // It returns "path parts" which contain indices to fields to be used by | ||||
| // reflect.Value.FieldByString(). Multiple parts are required for slices of | ||||
| // structs. | ||||
| func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { | ||||
| 	var struc *structInfo | ||||
| 	var field *fieldInfo | ||||
| 	var index64 int64 | ||||
| 	var err error | ||||
| 	parts := make([]pathPart, 0) | ||||
| 	path := make([]string, 0) | ||||
| 	keys := strings.Split(p, ".") | ||||
| 	for i := 0; i < len(keys); i++ { | ||||
| 		if t.Kind() != reflect.Struct { | ||||
| 			return nil, invalidPath | ||||
| 		} | ||||
| 		if struc = c.get(t); struc == nil { | ||||
| 			return nil, invalidPath | ||||
| 		} | ||||
| 		if field = struc.get(keys[i]); field == nil { | ||||
| 			return nil, invalidPath | ||||
| 		} | ||||
| 		// Valid field. Append index. | ||||
| 		path = append(path, field.name) | ||||
| 		if field.ss { | ||||
| 			// Parse a special case: slices of structs. | ||||
| 			// i+1 must be the slice index. | ||||
| 			// | ||||
| 			// Now that struct can implements TextUnmarshaler interface, | ||||
| 			// we don't need to force the struct's fields to appear in the path. | ||||
| 			// So checking i+2 is not necessary anymore. | ||||
| 			i++ | ||||
| 			if i+1 > len(keys) { | ||||
| 				return nil, invalidPath | ||||
| 			} | ||||
| 			if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil { | ||||
| 				return nil, invalidPath | ||||
| 			} | ||||
| 			parts = append(parts, pathPart{ | ||||
| 				path:  path, | ||||
| 				field: field, | ||||
| 				index: int(index64), | ||||
| 			}) | ||||
| 			path = make([]string, 0) | ||||
|  | ||||
| 			// Get the next struct type, dropping ptrs. | ||||
| 			if field.typ.Kind() == reflect.Ptr { | ||||
| 				t = field.typ.Elem() | ||||
| 			} else { | ||||
| 				t = field.typ | ||||
| 			} | ||||
| 			if t.Kind() == reflect.Slice { | ||||
| 				t = t.Elem() | ||||
| 				if t.Kind() == reflect.Ptr { | ||||
| 					t = t.Elem() | ||||
| 				} | ||||
| 			} | ||||
| 		} else if field.typ.Kind() == reflect.Ptr { | ||||
| 			t = field.typ.Elem() | ||||
| 		} else { | ||||
| 			t = field.typ | ||||
| 		} | ||||
| 	} | ||||
| 	// Add the remaining. | ||||
| 	parts = append(parts, pathPart{ | ||||
| 		path:  path, | ||||
| 		field: field, | ||||
| 		index: -1, | ||||
| 	}) | ||||
| 	return parts, nil | ||||
| } | ||||
|  | ||||
| // get returns a cached structInfo, creating it if necessary. | ||||
| func (c *cache) get(t reflect.Type) *structInfo { | ||||
| 	c.l.RLock() | ||||
| 	info := c.m[t] | ||||
| 	c.l.RUnlock() | ||||
| 	if info == nil { | ||||
| 		info = c.create(t, nil) | ||||
| 		c.l.Lock() | ||||
| 		c.m[t] = info | ||||
| 		c.l.Unlock() | ||||
| 	} | ||||
| 	return info | ||||
| } | ||||
|  | ||||
| // create creates a structInfo with meta-data about a struct. | ||||
| func (c *cache) create(t reflect.Type, info *structInfo) *structInfo { | ||||
| 	if info == nil { | ||||
| 		info = &structInfo{fields: []*fieldInfo{}} | ||||
| 	} | ||||
| 	for i := 0; i < t.NumField(); i++ { | ||||
| 		field := t.Field(i) | ||||
| 		if field.Anonymous { | ||||
| 			ft := field.Type | ||||
| 			if ft.Kind() == reflect.Ptr { | ||||
| 				ft = ft.Elem() | ||||
| 			} | ||||
| 			if ft.Kind() == reflect.Struct { | ||||
| 				c.create(ft, info) | ||||
| 			} | ||||
| 		} | ||||
| 		c.createField(field, info) | ||||
| 	} | ||||
| 	return info | ||||
| } | ||||
|  | ||||
| // createField creates a fieldInfo for the given field. | ||||
| func (c *cache) createField(field reflect.StructField, info *structInfo) { | ||||
| 	alias := fieldAlias(field, c.tag) | ||||
| 	if alias == "-" { | ||||
| 		// Ignore this field. | ||||
| 		return | ||||
| 	} | ||||
| 	// Check if the type is supported and don't cache it if not. | ||||
| 	// First let's get the basic type. | ||||
| 	isSlice, isStruct := false, false | ||||
| 	ft := field.Type | ||||
| 	if ft.Kind() == reflect.Ptr { | ||||
| 		ft = ft.Elem() | ||||
| 	} | ||||
| 	if isSlice = ft.Kind() == reflect.Slice; isSlice { | ||||
| 		ft = ft.Elem() | ||||
| 		if ft.Kind() == reflect.Ptr { | ||||
| 			ft = ft.Elem() | ||||
| 		} | ||||
| 	} | ||||
| 	if ft.Kind() == reflect.Array { | ||||
| 		ft = ft.Elem() | ||||
| 		if ft.Kind() == reflect.Ptr { | ||||
| 			ft = ft.Elem() | ||||
| 		} | ||||
| 	} | ||||
| 	if isStruct = ft.Kind() == reflect.Struct; !isStruct { | ||||
| 		if conv := c.conv[ft.Kind()]; conv == nil { | ||||
| 			// Type is not supported. | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	info.fields = append(info.fields, &fieldInfo{ | ||||
| 		typ:   field.Type, | ||||
| 		name:  field.Name, | ||||
| 		ss:    isSlice && isStruct, | ||||
| 		alias: alias, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // converter returns the converter for a type. | ||||
| func (c *cache) converter(t reflect.Type) Converter { | ||||
| 	conv := c.regconv[t] | ||||
| 	if conv == nil { | ||||
| 		conv = c.conv[t.Kind()] | ||||
| 	} | ||||
| 	return conv | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| type structInfo struct { | ||||
| 	fields []*fieldInfo | ||||
| } | ||||
|  | ||||
| func (i *structInfo) get(alias string) *fieldInfo { | ||||
| 	for _, field := range i.fields { | ||||
| 		if strings.EqualFold(field.alias, alias) { | ||||
| 			return field | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type fieldInfo struct { | ||||
| 	typ   reflect.Type | ||||
| 	name  string // field name in the struct. | ||||
| 	ss    bool   // true if this is a slice of structs. | ||||
| 	alias string | ||||
| } | ||||
|  | ||||
| type pathPart struct { | ||||
| 	field *fieldInfo | ||||
| 	path  []string // path to the field: walks structs using field names. | ||||
| 	index int      // struct index in slices of structs. | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // fieldAlias parses a field tag to get a field alias. | ||||
| func fieldAlias(field reflect.StructField, tagName string) string { | ||||
| 	var alias string | ||||
| 	if tag := field.Tag.Get(tagName); tag != "" { | ||||
| 		// For now tags only support the name but let's follow the | ||||
| 		// comma convention from encoding/json and others. | ||||
| 		if idx := strings.Index(tag, ","); idx == -1 { | ||||
| 			alias = tag | ||||
| 		} else { | ||||
| 			alias = tag[:idx] | ||||
| 		} | ||||
| 	} | ||||
| 	if alias == "" { | ||||
| 		alias = field.Name | ||||
| 	} | ||||
| 	return alias | ||||
| } | ||||
							
								
								
									
										145
									
								
								vendor/github.com/gorilla/schema/converter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								vendor/github.com/gorilla/schema/converter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| // Copyright 2012 The Gorilla 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 schema | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| type Converter func(string) reflect.Value | ||||
|  | ||||
| var ( | ||||
| 	invalidValue = reflect.Value{} | ||||
| 	boolType     = reflect.Bool | ||||
| 	float32Type  = reflect.Float32 | ||||
| 	float64Type  = reflect.Float64 | ||||
| 	intType      = reflect.Int | ||||
| 	int8Type     = reflect.Int8 | ||||
| 	int16Type    = reflect.Int16 | ||||
| 	int32Type    = reflect.Int32 | ||||
| 	int64Type    = reflect.Int64 | ||||
| 	stringType   = reflect.String | ||||
| 	uintType     = reflect.Uint | ||||
| 	uint8Type    = reflect.Uint8 | ||||
| 	uint16Type   = reflect.Uint16 | ||||
| 	uint32Type   = reflect.Uint32 | ||||
| 	uint64Type   = reflect.Uint64 | ||||
| ) | ||||
|  | ||||
| // Default converters for basic types. | ||||
| var converters = map[reflect.Kind]Converter{ | ||||
| 	boolType:    convertBool, | ||||
| 	float32Type: convertFloat32, | ||||
| 	float64Type: convertFloat64, | ||||
| 	intType:     convertInt, | ||||
| 	int8Type:    convertInt8, | ||||
| 	int16Type:   convertInt16, | ||||
| 	int32Type:   convertInt32, | ||||
| 	int64Type:   convertInt64, | ||||
| 	stringType:  convertString, | ||||
| 	uintType:    convertUint, | ||||
| 	uint8Type:   convertUint8, | ||||
| 	uint16Type:  convertUint16, | ||||
| 	uint32Type:  convertUint32, | ||||
| 	uint64Type:  convertUint64, | ||||
| } | ||||
|  | ||||
| func convertBool(value string) reflect.Value { | ||||
| 	if value == "on" { | ||||
| 		return reflect.ValueOf(true) | ||||
| 	} else if v, err := strconv.ParseBool(value); err == nil { | ||||
| 		return reflect.ValueOf(v) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertFloat32(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseFloat(value, 32); err == nil { | ||||
| 		return reflect.ValueOf(float32(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertFloat64(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseFloat(value, 64); err == nil { | ||||
| 		return reflect.ValueOf(v) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertInt(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseInt(value, 10, 0); err == nil { | ||||
| 		return reflect.ValueOf(int(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertInt8(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseInt(value, 10, 8); err == nil { | ||||
| 		return reflect.ValueOf(int8(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertInt16(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseInt(value, 10, 16); err == nil { | ||||
| 		return reflect.ValueOf(int16(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertInt32(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseInt(value, 10, 32); err == nil { | ||||
| 		return reflect.ValueOf(int32(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertInt64(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseInt(value, 10, 64); err == nil { | ||||
| 		return reflect.ValueOf(v) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertString(value string) reflect.Value { | ||||
| 	return reflect.ValueOf(value) | ||||
| } | ||||
|  | ||||
| func convertUint(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseUint(value, 10, 0); err == nil { | ||||
| 		return reflect.ValueOf(uint(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertUint8(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseUint(value, 10, 8); err == nil { | ||||
| 		return reflect.ValueOf(uint8(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertUint16(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseUint(value, 10, 16); err == nil { | ||||
| 		return reflect.ValueOf(uint16(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertUint32(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseUint(value, 10, 32); err == nil { | ||||
| 		return reflect.ValueOf(uint32(v)) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
|  | ||||
| func convertUint64(value string) reflect.Value { | ||||
| 	if v, err := strconv.ParseUint(value, 10, 64); err == nil { | ||||
| 		return reflect.ValueOf(v) | ||||
| 	} | ||||
| 	return invalidValue | ||||
| } | ||||
							
								
								
									
										299
									
								
								vendor/github.com/gorilla/schema/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								vendor/github.com/gorilla/schema/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| // Copyright 2012 The Gorilla 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 schema | ||||
|  | ||||
| import ( | ||||
| 	"encoding" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // NewDecoder returns a new Decoder. | ||||
| func NewDecoder() *Decoder { | ||||
| 	return &Decoder{cache: newCache()} | ||||
| } | ||||
|  | ||||
| // Decoder decodes values from a map[string][]string to a struct. | ||||
| type Decoder struct { | ||||
| 	cache             *cache | ||||
| 	zeroEmpty         bool | ||||
| 	ignoreUnknownKeys bool | ||||
| } | ||||
|  | ||||
| // SetAliasTag changes the tag used to locate custom field aliases. | ||||
| // The default tag is "schema". | ||||
| func (d *Decoder) SetAliasTag(tag string) { | ||||
| 	d.cache.tag = tag | ||||
| } | ||||
|  | ||||
| // ZeroEmpty controls the behaviour when the decoder encounters empty values | ||||
| // in a map. | ||||
| // If z is true and a key in the map has the empty string as a value | ||||
| // then the corresponding struct field is set to the zero value. | ||||
| // If z is false then empty strings are ignored. | ||||
| // | ||||
| // The default value is false, that is empty values do not change | ||||
| // the value of the struct field. | ||||
| func (d *Decoder) ZeroEmpty(z bool) { | ||||
| 	d.zeroEmpty = z | ||||
| } | ||||
|  | ||||
| // IgnoreUnknownKeys controls the behaviour when the decoder encounters unknown | ||||
| // keys in the map. | ||||
| // If i is true and an unknown field is encountered, it is ignored. This is | ||||
| // similar to how unknown keys are handled by encoding/json. | ||||
| // If i is false then Decode will return an error. Note that any valid keys | ||||
| // will still be decoded in to the target struct. | ||||
| // | ||||
| // To preserve backwards compatibility, the default value is false. | ||||
| func (d *Decoder) IgnoreUnknownKeys(i bool) { | ||||
| 	d.ignoreUnknownKeys = i | ||||
| } | ||||
|  | ||||
| // RegisterConverter registers a converter function for a custom type. | ||||
| func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) { | ||||
| 	d.cache.regconv[reflect.TypeOf(value)] = converterFunc | ||||
| } | ||||
|  | ||||
| // Decode decodes a map[string][]string to a struct. | ||||
| // | ||||
| // The first parameter must be a pointer to a struct. | ||||
| // | ||||
| // The second parameter is a map, typically url.Values from an HTTP request. | ||||
| // Keys are "paths" in dotted notation to the struct fields and nested structs. | ||||
| // | ||||
| // See the package documentation for a full explanation of the mechanics. | ||||
| func (d *Decoder) Decode(dst interface{}, src map[string][]string) error { | ||||
| 	v := reflect.ValueOf(dst) | ||||
| 	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { | ||||
| 		return errors.New("schema: interface must be a pointer to struct") | ||||
| 	} | ||||
| 	v = v.Elem() | ||||
| 	t := v.Type() | ||||
| 	errors := MultiError{} | ||||
| 	for path, values := range src { | ||||
| 		if parts, err := d.cache.parsePath(path, t); err == nil { | ||||
| 			if err = d.decode(v, path, parts, values); err != nil { | ||||
| 				errors[path] = err | ||||
| 			} | ||||
| 		} else if !d.ignoreUnknownKeys { | ||||
| 			errors[path] = fmt.Errorf("schema: invalid path %q", path) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(errors) > 0 { | ||||
| 		return errors | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // decode fills a struct field using a parsed path. | ||||
| func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error { | ||||
| 	// Get the field walking the struct fields by index. | ||||
| 	for _, name := range parts[0].path { | ||||
| 		if v.Type().Kind() == reflect.Ptr { | ||||
| 			if v.IsNil() { | ||||
| 				v.Set(reflect.New(v.Type().Elem())) | ||||
| 			} | ||||
| 			v = v.Elem() | ||||
| 		} | ||||
| 		v = v.FieldByName(name) | ||||
| 	} | ||||
|  | ||||
| 	// Don't even bother for unexported fields. | ||||
| 	if !v.CanSet() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Dereference if needed. | ||||
| 	t := v.Type() | ||||
| 	if t.Kind() == reflect.Ptr { | ||||
| 		t = t.Elem() | ||||
| 		if v.IsNil() { | ||||
| 			v.Set(reflect.New(t)) | ||||
| 		} | ||||
| 		v = v.Elem() | ||||
| 	} | ||||
|  | ||||
| 	// Slice of structs. Let's go recursive. | ||||
| 	if len(parts) > 1 { | ||||
| 		idx := parts[0].index | ||||
| 		if v.IsNil() || v.Len() < idx+1 { | ||||
| 			value := reflect.MakeSlice(t, idx+1, idx+1) | ||||
| 			if v.Len() < idx+1 { | ||||
| 				// Resize it. | ||||
| 				reflect.Copy(value, v) | ||||
| 			} | ||||
| 			v.Set(value) | ||||
| 		} | ||||
| 		return d.decode(v.Index(idx), path, parts[1:], values) | ||||
| 	} | ||||
|  | ||||
| 	// Get the converter early in case there is one for a slice type. | ||||
| 	conv := d.cache.converter(t) | ||||
| 	if conv == nil && t.Kind() == reflect.Slice { | ||||
| 		var items []reflect.Value | ||||
| 		elemT := t.Elem() | ||||
| 		isPtrElem := elemT.Kind() == reflect.Ptr | ||||
| 		if isPtrElem { | ||||
| 			elemT = elemT.Elem() | ||||
| 		} | ||||
|  | ||||
| 		// Try to get a converter for the element type. | ||||
| 		conv := d.cache.converter(elemT) | ||||
| 		if conv == nil { | ||||
| 			// As we are not dealing with slice of structs here, we don't need to check if the type | ||||
| 			// implements TextUnmarshaler interface | ||||
| 			return fmt.Errorf("schema: converter not found for %v", elemT) | ||||
| 		} | ||||
|  | ||||
| 		for key, value := range values { | ||||
| 			if value == "" { | ||||
| 				if d.zeroEmpty { | ||||
| 					items = append(items, reflect.Zero(elemT)) | ||||
| 				} | ||||
| 			} else if item := conv(value); item.IsValid() { | ||||
| 				if isPtrElem { | ||||
| 					ptr := reflect.New(elemT) | ||||
| 					ptr.Elem().Set(item) | ||||
| 					item = ptr | ||||
| 				} | ||||
| 				if item.Type() != elemT && !isPtrElem { | ||||
| 					item = item.Convert(elemT) | ||||
| 				} | ||||
| 				items = append(items, item) | ||||
| 			} else { | ||||
| 				if strings.Contains(value, ",") { | ||||
| 					values := strings.Split(value, ",") | ||||
| 					for _, value := range values { | ||||
| 						if value == "" { | ||||
| 							if d.zeroEmpty { | ||||
| 								items = append(items, reflect.Zero(elemT)) | ||||
| 							} | ||||
| 						} else if item := conv(value); item.IsValid() { | ||||
| 							if isPtrElem { | ||||
| 								ptr := reflect.New(elemT) | ||||
| 								ptr.Elem().Set(item) | ||||
| 								item = ptr | ||||
| 							} | ||||
| 							if item.Type() != elemT && !isPtrElem { | ||||
| 								item = item.Convert(elemT) | ||||
| 							} | ||||
| 							items = append(items, item) | ||||
| 						} else { | ||||
| 							return ConversionError{ | ||||
| 								Key:   path, | ||||
| 								Type:  elemT, | ||||
| 								Index: key, | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					return ConversionError{ | ||||
| 						Key:   path, | ||||
| 						Type:  elemT, | ||||
| 						Index: key, | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...) | ||||
| 		v.Set(value) | ||||
| 	} else { | ||||
| 		val := "" | ||||
| 		// Use the last value provided if any values were provided | ||||
| 		if len(values) > 0 { | ||||
| 			val = values[len(values)-1] | ||||
| 		} | ||||
|  | ||||
| 		if val == "" { | ||||
| 			if d.zeroEmpty { | ||||
| 				v.Set(reflect.Zero(t)) | ||||
| 			} | ||||
| 		} else if conv != nil { | ||||
| 			if value := conv(val); value.IsValid() { | ||||
| 				v.Set(value.Convert(t)) | ||||
| 			} else { | ||||
| 				return ConversionError{ | ||||
| 					Key:   path, | ||||
| 					Type:  t, | ||||
| 					Index: -1, | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			// When there's no registered conversion for the custom type, we will check if the type | ||||
| 			// implements the TextUnmarshaler interface. As the UnmarshalText function should be applied | ||||
| 			// to the pointer of the type, we convert the value to pointer. | ||||
| 			if v.CanAddr() { | ||||
| 				v = v.Addr() | ||||
| 			} | ||||
|  | ||||
| 			if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { | ||||
| 				if err := u.UnmarshalText([]byte(val)); err != nil { | ||||
| 					return ConversionError{ | ||||
| 						Key:   path, | ||||
| 						Type:  t, | ||||
| 						Index: -1, | ||||
| 						Err:   err, | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
| 				return fmt.Errorf("schema: converter not found for %v", t) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Errors --------------------------------------------------------------------- | ||||
|  | ||||
| // ConversionError stores information about a failed conversion. | ||||
| type ConversionError struct { | ||||
| 	Key   string       // key from the source map. | ||||
| 	Type  reflect.Type // expected type of elem | ||||
| 	Index int          // index for multi-value fields; -1 for single-value fields. | ||||
| 	Err   error        // low-level error (when it exists) | ||||
| } | ||||
|  | ||||
| func (e ConversionError) Error() string { | ||||
| 	var output string | ||||
|  | ||||
| 	if e.Index < 0 { | ||||
| 		output = fmt.Sprintf("schema: error converting value for %q", e.Key) | ||||
| 	} else { | ||||
| 		output = fmt.Sprintf("schema: error converting value for index %d of %q", | ||||
| 			e.Index, e.Key) | ||||
| 	} | ||||
|  | ||||
| 	if e.Err != nil { | ||||
| 		output = fmt.Sprintf("%s. Details: %s", output, e.Err) | ||||
| 	} | ||||
|  | ||||
| 	return output | ||||
| } | ||||
|  | ||||
| // MultiError stores multiple decoding errors. | ||||
| // | ||||
| // Borrowed from the App Engine SDK. | ||||
| type MultiError map[string]error | ||||
|  | ||||
| func (e MultiError) Error() string { | ||||
| 	s := "" | ||||
| 	for _, err := range e { | ||||
| 		s = err.Error() | ||||
| 		break | ||||
| 	} | ||||
| 	switch len(e) { | ||||
| 	case 0: | ||||
| 		return "(0 errors)" | ||||
| 	case 1: | ||||
| 		return s | ||||
| 	case 2: | ||||
| 		return s + " (and 1 other error)" | ||||
| 	} | ||||
| 	return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1) | ||||
| } | ||||
							
								
								
									
										148
									
								
								vendor/github.com/gorilla/schema/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/gorilla/schema/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| // Copyright 2012 The Gorilla 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 gorilla/schema fills a struct with form values. | ||||
|  | ||||
| The basic usage is really simple. Given this struct: | ||||
|  | ||||
| 	type Person struct { | ||||
| 		Name  string | ||||
| 		Phone string | ||||
| 	} | ||||
|  | ||||
| ...we can fill it passing a map to the Load() function: | ||||
|  | ||||
| 	values := map[string][]string{ | ||||
| 		"Name":  {"John"}, | ||||
| 		"Phone": {"999-999-999"}, | ||||
| 	} | ||||
| 	person := new(Person) | ||||
| 	decoder := schema.NewDecoder() | ||||
| 	decoder.Decode(person, values) | ||||
|  | ||||
| This is just a simple example and it doesn't make a lot of sense to create | ||||
| the map manually. Typically it will come from a http.Request object and | ||||
| will be of type url.Values: http.Request.Form or http.Request.MultipartForm: | ||||
|  | ||||
| 	func MyHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 		err := r.ParseForm() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			// Handle error | ||||
| 		} | ||||
|  | ||||
| 		decoder := schema.NewDecoder() | ||||
| 		// r.PostForm is a map of our POST form values | ||||
| 		err := decoder.Decode(person, r.PostForm) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			// Handle error | ||||
| 		} | ||||
|  | ||||
| 		// Do something with person.Name or person.Phone | ||||
| 	} | ||||
|  | ||||
| Note: it is a good idea to set a Decoder instance as a package global, | ||||
| because it caches meta-data about structs, and a instance can be shared safely: | ||||
|  | ||||
| 	var decoder = schema.NewDecoder() | ||||
|  | ||||
| To define custom names for fields, use a struct tag "schema". To not populate | ||||
| certain fields, use a dash for the name and it will be ignored: | ||||
|  | ||||
| 	type Person struct { | ||||
| 		Name  string `schema:"name"`  // custom name | ||||
| 		Phone string `schema:"phone"` // custom name | ||||
| 		Admin bool   `schema:"-"`     // this field is never set | ||||
| 	} | ||||
|  | ||||
| The supported field types in the destination struct are: | ||||
|  | ||||
| 	* bool | ||||
| 	* float variants (float32, float64) | ||||
| 	* int variants (int, int8, int16, int32, int64) | ||||
| 	* string | ||||
| 	* uint variants (uint, uint8, uint16, uint32, uint64) | ||||
| 	* struct | ||||
| 	* a pointer to one of the above types | ||||
| 	* a slice or a pointer to a slice of one of the above types | ||||
|  | ||||
| Non-supported types are simply ignored, however custom types can be registered | ||||
| to be converted. | ||||
|  | ||||
| To fill nested structs, keys must use a dotted notation as the "path" for the | ||||
| field. So for example, to fill the struct Person below: | ||||
|  | ||||
| 	type Phone struct { | ||||
| 		Label  string | ||||
| 		Number string | ||||
| 	} | ||||
|  | ||||
| 	type Person struct { | ||||
| 		Name  string | ||||
| 		Phone Phone | ||||
| 	} | ||||
|  | ||||
| ...the source map must have the keys "Name", "Phone.Label" and "Phone.Number". | ||||
| This means that an HTML form to fill a Person struct must look like this: | ||||
|  | ||||
| 	<form> | ||||
| 		<input type="text" name="Name"> | ||||
| 		<input type="text" name="Phone.Label"> | ||||
| 		<input type="text" name="Phone.Number"> | ||||
| 	</form> | ||||
|  | ||||
| Single values are filled using the first value for a key from the source map. | ||||
| Slices are filled using all values for a key from the source map. So to fill | ||||
| a Person with multiple Phone values, like: | ||||
|  | ||||
| 	type Person struct { | ||||
| 		Name   string | ||||
| 		Phones []Phone | ||||
| 	} | ||||
|  | ||||
| ...an HTML form that accepts three Phone values would look like this: | ||||
|  | ||||
| 	<form> | ||||
| 		<input type="text" name="Name"> | ||||
| 		<input type="text" name="Phones.0.Label"> | ||||
| 		<input type="text" name="Phones.0.Number"> | ||||
| 		<input type="text" name="Phones.1.Label"> | ||||
| 		<input type="text" name="Phones.1.Number"> | ||||
| 		<input type="text" name="Phones.2.Label"> | ||||
| 		<input type="text" name="Phones.2.Number"> | ||||
| 	</form> | ||||
|  | ||||
| Notice that only for slices of structs the slice index is required. | ||||
| This is needed for disambiguation: if the nested struct also had a slice | ||||
| field, we could not translate multiple values to it if we did not use an | ||||
| index for the parent struct. | ||||
|  | ||||
| There's also the possibility to create a custom type that implements the | ||||
| TextUnmarshaler interface, and in this case there's no need to registry | ||||
| a converter, like: | ||||
|  | ||||
| 	type Person struct { | ||||
| 	  Emails []Email | ||||
| 	} | ||||
|  | ||||
| 	type Email struct { | ||||
| 	  *mail.Address | ||||
| 	} | ||||
|  | ||||
| 	func (e *Email) UnmarshalText(text []byte) (err error) { | ||||
| 		e.Address, err = mail.ParseAddress(string(text)) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| ...an HTML form that accepts three Email values would look like this: | ||||
|  | ||||
| 	<form> | ||||
| 		<input type="email" name="Emails.0"> | ||||
| 		<input type="email" name="Emails.1"> | ||||
| 		<input type="email" name="Emails.2"> | ||||
| 	</form> | ||||
| */ | ||||
| package schema | ||||
							
								
								
									
										22
									
								
								vendor/github.com/gorilla/websocket/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/gorilla/websocket/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|  | ||||
|   Redistributions of source code must retain the above copyright notice, this | ||||
|   list of conditions and the following disclaimer. | ||||
|  | ||||
|   Redistributions in binary form must reproduce the above copyright notice, | ||||
|   this list of conditions and the following disclaimer in the documentation | ||||
|   and/or other materials provided with the distribution. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										350
									
								
								vendor/github.com/gorilla/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								vendor/github.com/gorilla/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,350 @@ | ||||
| // 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 websocket | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // ErrBadHandshake is returned when the server response to opening handshake is | ||||
| // invalid. | ||||
| var ErrBadHandshake = errors.New("websocket: bad handshake") | ||||
|  | ||||
| // 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 origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies | ||||
| // (Cookie). Use the response.Header to get the selected subprotocol | ||||
| // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). | ||||
| // | ||||
| // If the WebSocket handshake fails, ErrBadHandshake is returned along with a | ||||
| // non-nil *http.Response so that callers can handle redirects, authentication, | ||||
| // etc. | ||||
| // | ||||
| // Deprecated: Use Dialer instead. | ||||
| func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { | ||||
| 	d := Dialer{ | ||||
| 		ReadBufferSize:  readBufSize, | ||||
| 		WriteBufferSize: writeBufSize, | ||||
| 		NetDial: func(net, addr string) (net.Conn, error) { | ||||
| 			return netConn, nil | ||||
| 		}, | ||||
| 	} | ||||
| 	return d.Dial(u.String(), requestHeader) | ||||
| } | ||||
|  | ||||
| // A Dialer contains options for connecting to WebSocket server. | ||||
| type Dialer struct { | ||||
| 	// NetDial specifies the dial function for creating TCP connections. If | ||||
| 	// NetDial is nil, net.Dial is used. | ||||
| 	NetDial func(network, addr string) (net.Conn, error) | ||||
|  | ||||
| 	// Proxy specifies a function to return a proxy for a given | ||||
| 	// Request. If the function returns a non-nil error, the | ||||
| 	// request is aborted with the provided error. | ||||
| 	// If Proxy is nil or returns a nil *URL, no proxy is used. | ||||
| 	Proxy func(*http.Request) (*url.URL, error) | ||||
|  | ||||
| 	// TLSClientConfig specifies the TLS configuration to use with tls.Client. | ||||
| 	// If nil, the default configuration is used. | ||||
| 	TLSClientConfig *tls.Config | ||||
|  | ||||
| 	// HandshakeTimeout specifies the duration for the handshake to complete. | ||||
| 	HandshakeTimeout time.Duration | ||||
|  | ||||
| 	// Input and output buffer sizes. If the buffer size is zero, then a | ||||
| 	// default value of 4096 is used. | ||||
| 	ReadBufferSize, WriteBufferSize int | ||||
|  | ||||
| 	// Subprotocols specifies the client's requested subprotocols. | ||||
| 	Subprotocols []string | ||||
| } | ||||
|  | ||||
| var errMalformedURL = errors.New("malformed ws or wss URL") | ||||
|  | ||||
| // parseURL parses the URL. | ||||
| // | ||||
| // This function is a replacement for the standard library url.Parse function. | ||||
| // In Go 1.4 and earlier, url.Parse loses information from the path. | ||||
| func parseURL(s string) (*url.URL, error) { | ||||
| 	// From the RFC: | ||||
| 	// | ||||
| 	// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] | ||||
| 	// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] | ||||
|  | ||||
| 	var u url.URL | ||||
| 	switch { | ||||
| 	case strings.HasPrefix(s, "ws://"): | ||||
| 		u.Scheme = "ws" | ||||
| 		s = s[len("ws://"):] | ||||
| 	case strings.HasPrefix(s, "wss://"): | ||||
| 		u.Scheme = "wss" | ||||
| 		s = s[len("wss://"):] | ||||
| 	default: | ||||
| 		return nil, errMalformedURL | ||||
| 	} | ||||
|  | ||||
| 	if i := strings.Index(s, "?"); i >= 0 { | ||||
| 		u.RawQuery = s[i+1:] | ||||
| 		s = s[:i] | ||||
| 	} | ||||
|  | ||||
| 	if i := strings.Index(s, "/"); i >= 0 { | ||||
| 		u.Opaque = s[i:] | ||||
| 		s = s[:i] | ||||
| 	} else { | ||||
| 		u.Opaque = "/" | ||||
| 	} | ||||
|  | ||||
| 	u.Host = s | ||||
|  | ||||
| 	if strings.Contains(u.Host, "@") { | ||||
| 		// Don't bother parsing user information because user information is | ||||
| 		// not allowed in websocket URIs. | ||||
| 		return nil, errMalformedURL | ||||
| 	} | ||||
|  | ||||
| 	return &u, nil | ||||
| } | ||||
|  | ||||
| func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { | ||||
| 	hostPort = u.Host | ||||
| 	hostNoPort = u.Host | ||||
| 	if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { | ||||
| 		hostNoPort = hostNoPort[:i] | ||||
| 	} else { | ||||
| 		switch u.Scheme { | ||||
| 		case "wss": | ||||
| 			hostPort += ":443" | ||||
| 		case "https": | ||||
| 			hostPort += ":443" | ||||
| 		default: | ||||
| 			hostPort += ":80" | ||||
| 		} | ||||
| 	} | ||||
| 	return hostPort, hostNoPort | ||||
| } | ||||
|  | ||||
| // DefaultDialer is a dialer with all fields set to the default zero values. | ||||
| var DefaultDialer = &Dialer{ | ||||
| 	Proxy: http.ProxyFromEnvironment, | ||||
| } | ||||
|  | ||||
| // Dial creates a new client connection. Use requestHeader to specify the | ||||
| // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). | ||||
| // Use the response.Header to get the selected subprotocol | ||||
| // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). | ||||
| // | ||||
| // If the WebSocket handshake fails, ErrBadHandshake is returned along with a | ||||
| // non-nil *http.Response so that callers can handle redirects, authentication, | ||||
| // etcetera. The response body may not contain the entire response and does not | ||||
| // need to be closed by the application. | ||||
| func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { | ||||
|  | ||||
| 	if d == nil { | ||||
| 		d = &Dialer{ | ||||
| 			Proxy: http.ProxyFromEnvironment, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	challengeKey, err := generateChallengeKey() | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	u, err := parseURL(urlStr) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch u.Scheme { | ||||
| 	case "ws": | ||||
| 		u.Scheme = "http" | ||||
| 	case "wss": | ||||
| 		u.Scheme = "https" | ||||
| 	default: | ||||
| 		return nil, nil, errMalformedURL | ||||
| 	} | ||||
|  | ||||
| 	if u.User != nil { | ||||
| 		// User name and password are not allowed in websocket URIs. | ||||
| 		return nil, nil, errMalformedURL | ||||
| 	} | ||||
|  | ||||
| 	req := &http.Request{ | ||||
| 		Method:     "GET", | ||||
| 		URL:        u, | ||||
| 		Proto:      "HTTP/1.1", | ||||
| 		ProtoMajor: 1, | ||||
| 		ProtoMinor: 1, | ||||
| 		Header:     make(http.Header), | ||||
| 		Host:       u.Host, | ||||
| 	} | ||||
|  | ||||
| 	// Set the request headers using the capitalization for names and values in | ||||
| 	// RFC examples. Although the capitalization shouldn't matter, there are | ||||
| 	// servers that depend on it. The Header.Set method is not used because the | ||||
| 	// method canonicalizes the header names. | ||||
| 	req.Header["Upgrade"] = []string{"websocket"} | ||||
| 	req.Header["Connection"] = []string{"Upgrade"} | ||||
| 	req.Header["Sec-WebSocket-Key"] = []string{challengeKey} | ||||
| 	req.Header["Sec-WebSocket-Version"] = []string{"13"} | ||||
| 	if len(d.Subprotocols) > 0 { | ||||
| 		req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} | ||||
| 	} | ||||
| 	for k, vs := range requestHeader { | ||||
| 		switch { | ||||
| 		case k == "Host": | ||||
| 			if len(vs) > 0 { | ||||
| 				req.Host = vs[0] | ||||
| 			} | ||||
| 		case k == "Upgrade" || | ||||
| 			k == "Connection" || | ||||
| 			k == "Sec-Websocket-Key" || | ||||
| 			k == "Sec-Websocket-Version" || | ||||
| 			(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): | ||||
| 			return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) | ||||
| 		default: | ||||
| 			req.Header[k] = vs | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	hostPort, hostNoPort := hostPortNoPort(u) | ||||
|  | ||||
| 	var proxyURL *url.URL | ||||
| 	// Check wether the proxy method has been configured | ||||
| 	if d.Proxy != nil { | ||||
| 		proxyURL, err = d.Proxy(req) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	var targetHostPort string | ||||
| 	if proxyURL != nil { | ||||
| 		targetHostPort, _ = hostPortNoPort(proxyURL) | ||||
| 	} else { | ||||
| 		targetHostPort = hostPort | ||||
| 	} | ||||
|  | ||||
| 	var deadline time.Time | ||||
| 	if d.HandshakeTimeout != 0 { | ||||
| 		deadline = time.Now().Add(d.HandshakeTimeout) | ||||
| 	} | ||||
|  | ||||
| 	netDial := d.NetDial | ||||
| 	if netDial == nil { | ||||
| 		netDialer := &net.Dialer{Deadline: deadline} | ||||
| 		netDial = netDialer.Dial | ||||
| 	} | ||||
|  | ||||
| 	netConn, err := netDial("tcp", targetHostPort) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		if netConn != nil { | ||||
| 			netConn.Close() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	if err := netConn.SetDeadline(deadline); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	if proxyURL != nil { | ||||
| 		connectHeader := make(http.Header) | ||||
| 		if user := proxyURL.User; user != nil { | ||||
| 			proxyUser := user.Username() | ||||
| 			if proxyPassword, passwordSet := user.Password(); passwordSet { | ||||
| 				credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) | ||||
| 				connectHeader.Set("Proxy-Authorization", "Basic "+credential) | ||||
| 			} | ||||
| 		} | ||||
| 		connectReq := &http.Request{ | ||||
| 			Method: "CONNECT", | ||||
| 			URL:    &url.URL{Opaque: hostPort}, | ||||
| 			Host:   hostPort, | ||||
| 			Header: connectHeader, | ||||
| 		} | ||||
|  | ||||
| 		connectReq.Write(netConn) | ||||
|  | ||||
| 		// Read response. | ||||
| 		// Okay to use and discard buffered reader here, because | ||||
| 		// TLS server will not speak until spoken to. | ||||
| 		br := bufio.NewReader(netConn) | ||||
| 		resp, err := http.ReadResponse(br, connectReq) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		if resp.StatusCode != 200 { | ||||
| 			f := strings.SplitN(resp.Status, " ", 2) | ||||
| 			return nil, nil, errors.New(f[1]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if u.Scheme == "https" { | ||||
| 		cfg := d.TLSClientConfig | ||||
| 		if cfg == nil { | ||||
| 			cfg = &tls.Config{ServerName: hostNoPort} | ||||
| 		} else if cfg.ServerName == "" { | ||||
| 			shallowCopy := *cfg | ||||
| 			cfg = &shallowCopy | ||||
| 			cfg.ServerName = hostNoPort | ||||
| 		} | ||||
| 		tlsConn := tls.Client(netConn, cfg) | ||||
| 		netConn = tlsConn | ||||
| 		if err := tlsConn.Handshake(); err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		if !cfg.InsecureSkipVerify { | ||||
| 			if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { | ||||
| 				return nil, nil, err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) | ||||
|  | ||||
| 	if err := req.Write(netConn); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	resp, err := http.ReadResponse(conn.br, req) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode != 101 || | ||||
| 		!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || | ||||
| 		!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || | ||||
| 		resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { | ||||
| 		// Before closing the network connection on return from this | ||||
| 		// function, slurp up some of the response to aid application | ||||
| 		// debugging. | ||||
| 		buf := make([]byte, 1024) | ||||
| 		n, _ := io.ReadFull(resp.Body, buf) | ||||
| 		resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) | ||||
| 		return nil, resp, ErrBadHandshake | ||||
| 	} | ||||
|  | ||||
| 	resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) | ||||
| 	conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") | ||||
|  | ||||
| 	netConn.SetDeadline(time.Time{}) | ||||
| 	netConn = nil // to avoid close in defer. | ||||
| 	return conn, resp, nil | ||||
| } | ||||
							
								
								
									
										915
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										915
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,915 @@ | ||||
| // 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 websocket | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	maxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask | ||||
| 	maxControlFramePayloadSize = 125 | ||||
| 	finalBit                   = 1 << 7 | ||||
| 	maskBit                    = 1 << 7 | ||||
| 	writeWait                  = time.Second | ||||
|  | ||||
| 	defaultReadBufferSize  = 4096 | ||||
| 	defaultWriteBufferSize = 4096 | ||||
|  | ||||
| 	continuationFrame = 0 | ||||
| 	noFrame           = -1 | ||||
| ) | ||||
|  | ||||
| // Close codes defined in RFC 6455, section 11.7. | ||||
| const ( | ||||
| 	CloseNormalClosure           = 1000 | ||||
| 	CloseGoingAway               = 1001 | ||||
| 	CloseProtocolError           = 1002 | ||||
| 	CloseUnsupportedData         = 1003 | ||||
| 	CloseNoStatusReceived        = 1005 | ||||
| 	CloseAbnormalClosure         = 1006 | ||||
| 	CloseInvalidFramePayloadData = 1007 | ||||
| 	ClosePolicyViolation         = 1008 | ||||
| 	CloseMessageTooBig           = 1009 | ||||
| 	CloseMandatoryExtension      = 1010 | ||||
| 	CloseInternalServerErr       = 1011 | ||||
| 	CloseTLSHandshake            = 1015 | ||||
| ) | ||||
|  | ||||
| // The message types are defined in RFC 6455, section 11.8. | ||||
| const ( | ||||
| 	// TextMessage denotes a text data message. The text message payload is | ||||
| 	// interpreted as UTF-8 encoded text data. | ||||
| 	TextMessage = 1 | ||||
|  | ||||
| 	// BinaryMessage denotes a binary data message. | ||||
| 	BinaryMessage = 2 | ||||
|  | ||||
| 	// CloseMessage denotes a close control message. The optional message | ||||
| 	// payload contains a numeric code and text. Use the FormatCloseMessage | ||||
| 	// function to format a close message payload. | ||||
| 	CloseMessage = 8 | ||||
|  | ||||
| 	// PingMessage denotes a ping control message. The optional message payload | ||||
| 	// is UTF-8 encoded text. | ||||
| 	PingMessage = 9 | ||||
|  | ||||
| 	// PongMessage denotes a ping control message. The optional message payload | ||||
| 	// is UTF-8 encoded text. | ||||
| 	PongMessage = 10 | ||||
| ) | ||||
|  | ||||
| // ErrCloseSent is returned when the application writes a message to the | ||||
| // connection after sending a close message. | ||||
| var ErrCloseSent = errors.New("websocket: close sent") | ||||
|  | ||||
| // ErrReadLimit is returned when reading a message that is larger than the | ||||
| // read limit set for the connection. | ||||
| var ErrReadLimit = errors.New("websocket: read limit exceeded") | ||||
|  | ||||
| // netError satisfies the net Error interface. | ||||
| type netError struct { | ||||
| 	msg       string | ||||
| 	temporary bool | ||||
| 	timeout   bool | ||||
| } | ||||
|  | ||||
| func (e *netError) Error() string   { return e.msg } | ||||
| func (e *netError) Temporary() bool { return e.temporary } | ||||
| func (e *netError) Timeout() bool   { return e.timeout } | ||||
|  | ||||
| // CloseError represents close frame. | ||||
| type CloseError struct { | ||||
|  | ||||
| 	// Code is defined in RFC 6455, section 11.7. | ||||
| 	Code int | ||||
|  | ||||
| 	// Text is the optional text payload. | ||||
| 	Text string | ||||
| } | ||||
|  | ||||
| func (e *CloseError) Error() string { | ||||
| 	s := []byte("websocket: close ") | ||||
| 	s = strconv.AppendInt(s, int64(e.Code), 10) | ||||
| 	switch e.Code { | ||||
| 	case CloseNormalClosure: | ||||
| 		s = append(s, " (normal)"...) | ||||
| 	case CloseGoingAway: | ||||
| 		s = append(s, " (going away)"...) | ||||
| 	case CloseProtocolError: | ||||
| 		s = append(s, " (protocol error)"...) | ||||
| 	case CloseUnsupportedData: | ||||
| 		s = append(s, " (unsupported data)"...) | ||||
| 	case CloseNoStatusReceived: | ||||
| 		s = append(s, " (no status)"...) | ||||
| 	case CloseAbnormalClosure: | ||||
| 		s = append(s, " (abnormal closure)"...) | ||||
| 	case CloseInvalidFramePayloadData: | ||||
| 		s = append(s, " (invalid payload data)"...) | ||||
| 	case ClosePolicyViolation: | ||||
| 		s = append(s, " (policy violation)"...) | ||||
| 	case CloseMessageTooBig: | ||||
| 		s = append(s, " (message too big)"...) | ||||
| 	case CloseMandatoryExtension: | ||||
| 		s = append(s, " (mandatory extension missing)"...) | ||||
| 	case CloseInternalServerErr: | ||||
| 		s = append(s, " (internal server error)"...) | ||||
| 	case CloseTLSHandshake: | ||||
| 		s = append(s, " (TLS handshake error)"...) | ||||
| 	} | ||||
| 	if e.Text != "" { | ||||
| 		s = append(s, ": "...) | ||||
| 		s = append(s, e.Text...) | ||||
| 	} | ||||
| 	return string(s) | ||||
| } | ||||
|  | ||||
| // IsCloseError returns boolean indicating whether the error is a *CloseError | ||||
| // with one of the specified codes. | ||||
| func IsCloseError(err error, codes ...int) bool { | ||||
| 	if e, ok := err.(*CloseError); ok { | ||||
| 		for _, code := range codes { | ||||
| 			if e.Code == code { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // IsUnexpectedCloseError returns boolean indicating whether the error is a | ||||
| // *CloseError with a code not in the list of expected codes. | ||||
| func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { | ||||
| 	if e, ok := err.(*CloseError); ok { | ||||
| 		for _, code := range expectedCodes { | ||||
| 			if e.Code == code { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	errWriteTimeout        = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} | ||||
| 	errUnexpectedEOF       = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} | ||||
| 	errBadWriteOpCode      = errors.New("websocket: bad write message type") | ||||
| 	errWriteClosed         = errors.New("websocket: write closed") | ||||
| 	errInvalidControlFrame = errors.New("websocket: invalid control frame") | ||||
| ) | ||||
|  | ||||
| func hideTempErr(err error) error { | ||||
| 	if e, ok := err.(net.Error); ok && e.Temporary() { | ||||
| 		err = &netError{msg: e.Error(), timeout: e.Timeout()} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func isControl(frameType int) bool { | ||||
| 	return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage | ||||
| } | ||||
|  | ||||
| func isData(frameType int) bool { | ||||
| 	return frameType == TextMessage || frameType == BinaryMessage | ||||
| } | ||||
|  | ||||
| func maskBytes(key [4]byte, pos int, b []byte) int { | ||||
| 	for i := range b { | ||||
| 		b[i] ^= key[pos&3] | ||||
| 		pos++ | ||||
| 	} | ||||
| 	return pos & 3 | ||||
| } | ||||
|  | ||||
| func newMaskKey() [4]byte { | ||||
| 	n := rand.Uint32() | ||||
| 	return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} | ||||
| } | ||||
|  | ||||
| // Conn represents a WebSocket connection. | ||||
| type Conn struct { | ||||
| 	conn        net.Conn | ||||
| 	isServer    bool | ||||
| 	subprotocol string | ||||
|  | ||||
| 	// Write fields | ||||
| 	mu        chan bool // used as mutex to protect write to conn and closeSent | ||||
| 	closeSent bool      // true if close message was sent | ||||
|  | ||||
| 	// Message writer fields. | ||||
| 	writeErr       error | ||||
| 	writeBuf       []byte // frame is constructed in this buffer. | ||||
| 	writePos       int    // end of data in writeBuf. | ||||
| 	writeFrameType int    // type of the current frame. | ||||
| 	writeSeq       int    // incremented to invalidate message writers. | ||||
| 	writeDeadline  time.Time | ||||
| 	isWriting      bool // for best-effort concurrent write detection | ||||
|  | ||||
| 	// Read fields | ||||
| 	readErr       error | ||||
| 	br            *bufio.Reader | ||||
| 	readRemaining int64 // bytes remaining in current frame. | ||||
| 	readFinal     bool  // true the current message has more frames. | ||||
| 	readSeq       int   // incremented to invalidate message readers. | ||||
| 	readLength    int64 // Message size. | ||||
| 	readLimit     int64 // Maximum message size. | ||||
| 	readMaskPos   int | ||||
| 	readMaskKey   [4]byte | ||||
| 	handlePong    func(string) error | ||||
| 	handlePing    func(string) error | ||||
| 	readErrCount  int | ||||
| } | ||||
|  | ||||
| func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { | ||||
| 	mu := make(chan bool, 1) | ||||
| 	mu <- true | ||||
|  | ||||
| 	if readBufferSize == 0 { | ||||
| 		readBufferSize = defaultReadBufferSize | ||||
| 	} | ||||
| 	if writeBufferSize == 0 { | ||||
| 		writeBufferSize = defaultWriteBufferSize | ||||
| 	} | ||||
|  | ||||
| 	c := &Conn{ | ||||
| 		isServer:       isServer, | ||||
| 		br:             bufio.NewReaderSize(conn, readBufferSize), | ||||
| 		conn:           conn, | ||||
| 		mu:             mu, | ||||
| 		readFinal:      true, | ||||
| 		writeBuf:       make([]byte, writeBufferSize+maxFrameHeaderSize), | ||||
| 		writeFrameType: noFrame, | ||||
| 		writePos:       maxFrameHeaderSize, | ||||
| 	} | ||||
| 	c.SetPingHandler(nil) | ||||
| 	c.SetPongHandler(nil) | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| // Subprotocol returns the negotiated protocol for the connection. | ||||
| func (c *Conn) Subprotocol() string { | ||||
| 	return c.subprotocol | ||||
| } | ||||
|  | ||||
| // Close closes the underlying network connection without sending or waiting for a close frame. | ||||
| func (c *Conn) Close() error { | ||||
| 	return c.conn.Close() | ||||
| } | ||||
|  | ||||
| // LocalAddr returns the local network address. | ||||
| func (c *Conn) LocalAddr() net.Addr { | ||||
| 	return c.conn.LocalAddr() | ||||
| } | ||||
|  | ||||
| // RemoteAddr returns the remote network address. | ||||
| func (c *Conn) RemoteAddr() net.Addr { | ||||
| 	return c.conn.RemoteAddr() | ||||
| } | ||||
|  | ||||
| // Write methods | ||||
|  | ||||
| func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { | ||||
| 	<-c.mu | ||||
| 	defer func() { c.mu <- true }() | ||||
|  | ||||
| 	if c.closeSent { | ||||
| 		return ErrCloseSent | ||||
| 	} else if frameType == CloseMessage { | ||||
| 		c.closeSent = true | ||||
| 	} | ||||
|  | ||||
| 	c.conn.SetWriteDeadline(deadline) | ||||
| 	for _, buf := range bufs { | ||||
| 		if len(buf) > 0 { | ||||
| 			n, err := c.conn.Write(buf) | ||||
| 			if n != len(buf) { | ||||
| 				// Close on partial write. | ||||
| 				c.conn.Close() | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // WriteControl writes a control message with the given deadline. The allowed | ||||
| // message types are CloseMessage, PingMessage and PongMessage. | ||||
| func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { | ||||
| 	if !isControl(messageType) { | ||||
| 		return errBadWriteOpCode | ||||
| 	} | ||||
| 	if len(data) > maxControlFramePayloadSize { | ||||
| 		return errInvalidControlFrame | ||||
| 	} | ||||
|  | ||||
| 	b0 := byte(messageType) | finalBit | ||||
| 	b1 := byte(len(data)) | ||||
| 	if !c.isServer { | ||||
| 		b1 |= maskBit | ||||
| 	} | ||||
|  | ||||
| 	buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) | ||||
| 	buf = append(buf, b0, b1) | ||||
|  | ||||
| 	if c.isServer { | ||||
| 		buf = append(buf, data...) | ||||
| 	} else { | ||||
| 		key := newMaskKey() | ||||
| 		buf = append(buf, key[:]...) | ||||
| 		buf = append(buf, data...) | ||||
| 		maskBytes(key, 0, buf[6:]) | ||||
| 	} | ||||
|  | ||||
| 	d := time.Hour * 1000 | ||||
| 	if !deadline.IsZero() { | ||||
| 		d = deadline.Sub(time.Now()) | ||||
| 		if d < 0 { | ||||
| 			return errWriteTimeout | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	timer := time.NewTimer(d) | ||||
| 	select { | ||||
| 	case <-c.mu: | ||||
| 		timer.Stop() | ||||
| 	case <-timer.C: | ||||
| 		return errWriteTimeout | ||||
| 	} | ||||
| 	defer func() { c.mu <- true }() | ||||
|  | ||||
| 	if c.closeSent { | ||||
| 		return ErrCloseSent | ||||
| 	} else if messageType == CloseMessage { | ||||
| 		c.closeSent = true | ||||
| 	} | ||||
|  | ||||
| 	c.conn.SetWriteDeadline(deadline) | ||||
| 	n, err := c.conn.Write(buf) | ||||
| 	if n != 0 && n != len(buf) { | ||||
| 		c.conn.Close() | ||||
| 	} | ||||
| 	return hideTempErr(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 | ||||
| // previous writer if the application has not already done so. | ||||
| func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { | ||||
| 	if c.writeErr != nil { | ||||
| 		return nil, c.writeErr | ||||
| 	} | ||||
|  | ||||
| 	if c.writeFrameType != noFrame { | ||||
| 		if err := c.flushFrame(true, nil); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !isControl(messageType) && !isData(messageType) { | ||||
| 		return nil, errBadWriteOpCode | ||||
| 	} | ||||
|  | ||||
| 	c.writeFrameType = messageType | ||||
| 	return messageWriter{c, c.writeSeq}, nil | ||||
| } | ||||
|  | ||||
| func (c *Conn) flushFrame(final bool, extra []byte) error { | ||||
| 	length := c.writePos - maxFrameHeaderSize + len(extra) | ||||
|  | ||||
| 	// Check for invalid control frames. | ||||
| 	if isControl(c.writeFrameType) && | ||||
| 		(!final || length > maxControlFramePayloadSize) { | ||||
| 		c.writeSeq++ | ||||
| 		c.writeFrameType = noFrame | ||||
| 		c.writePos = maxFrameHeaderSize | ||||
| 		return errInvalidControlFrame | ||||
| 	} | ||||
|  | ||||
| 	b0 := byte(c.writeFrameType) | ||||
| 	if final { | ||||
| 		b0 |= finalBit | ||||
| 	} | ||||
| 	b1 := byte(0) | ||||
| 	if !c.isServer { | ||||
| 		b1 |= maskBit | ||||
| 	} | ||||
|  | ||||
| 	// Assume that the frame starts at beginning of c.writeBuf. | ||||
| 	framePos := 0 | ||||
| 	if c.isServer { | ||||
| 		// Adjust up if mask not included in the header. | ||||
| 		framePos = 4 | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case length >= 65536: | ||||
| 		c.writeBuf[framePos] = b0 | ||||
| 		c.writeBuf[framePos+1] = b1 | 127 | ||||
| 		binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) | ||||
| 	case length > 125: | ||||
| 		framePos += 6 | ||||
| 		c.writeBuf[framePos] = b0 | ||||
| 		c.writeBuf[framePos+1] = b1 | 126 | ||||
| 		binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) | ||||
| 	default: | ||||
| 		framePos += 8 | ||||
| 		c.writeBuf[framePos] = b0 | ||||
| 		c.writeBuf[framePos+1] = b1 | byte(length) | ||||
| 	} | ||||
|  | ||||
| 	if !c.isServer { | ||||
| 		key := newMaskKey() | ||||
| 		copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | ||||
| 		maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos]) | ||||
| 		if len(extra) > 0 { | ||||
| 			c.writeErr = errors.New("websocket: internal error, extra used in client mode") | ||||
| 			return c.writeErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Write the buffers to the connection with best-effort detection of | ||||
| 	// concurrent writes. See the concurrency section in the package | ||||
| 	// documentation for more info. | ||||
|  | ||||
| 	if c.isWriting { | ||||
| 		panic("concurrent write to websocket connection") | ||||
| 	} | ||||
| 	c.isWriting = true | ||||
|  | ||||
| 	c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra) | ||||
|  | ||||
| 	if !c.isWriting { | ||||
| 		panic("concurrent write to websocket connection") | ||||
| 	} | ||||
| 	c.isWriting = false | ||||
|  | ||||
| 	// Setup for next frame. | ||||
| 	c.writePos = maxFrameHeaderSize | ||||
| 	c.writeFrameType = continuationFrame | ||||
| 	if final { | ||||
| 		c.writeSeq++ | ||||
| 		c.writeFrameType = noFrame | ||||
| 	} | ||||
| 	return c.writeErr | ||||
| } | ||||
|  | ||||
| type messageWriter struct { | ||||
| 	c   *Conn | ||||
| 	seq int | ||||
| } | ||||
|  | ||||
| func (w messageWriter) err() error { | ||||
| 	c := w.c | ||||
| 	if c.writeSeq != w.seq { | ||||
| 		return errWriteClosed | ||||
| 	} | ||||
| 	if c.writeErr != nil { | ||||
| 		return c.writeErr | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (w messageWriter) ncopy(max int) (int, error) { | ||||
| 	n := len(w.c.writeBuf) - w.c.writePos | ||||
| 	if n <= 0 { | ||||
| 		if err := w.c.flushFrame(false, nil); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		n = len(w.c.writeBuf) - w.c.writePos | ||||
| 	} | ||||
| 	if n > max { | ||||
| 		n = max | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| func (w messageWriter) write(final bool, p []byte) (int, error) { | ||||
| 	if err := w.err(); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { | ||||
| 		// Don't buffer large messages. | ||||
| 		err := w.c.flushFrame(final, p) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		return len(p), nil | ||||
| 	} | ||||
|  | ||||
| 	nn := len(p) | ||||
| 	for len(p) > 0 { | ||||
| 		n, err := w.ncopy(len(p)) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		copy(w.c.writeBuf[w.c.writePos:], p[:n]) | ||||
| 		w.c.writePos += n | ||||
| 		p = p[n:] | ||||
| 	} | ||||
| 	return nn, nil | ||||
| } | ||||
|  | ||||
| func (w messageWriter) Write(p []byte) (int, error) { | ||||
| 	return w.write(false, p) | ||||
| } | ||||
|  | ||||
| func (w messageWriter) WriteString(p string) (int, error) { | ||||
| 	if err := w.err(); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	nn := len(p) | ||||
| 	for len(p) > 0 { | ||||
| 		n, err := w.ncopy(len(p)) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		copy(w.c.writeBuf[w.c.writePos:], p[:n]) | ||||
| 		w.c.writePos += n | ||||
| 		p = p[n:] | ||||
| 	} | ||||
| 	return nn, nil | ||||
| } | ||||
|  | ||||
| func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { | ||||
| 	if err := w.err(); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	for { | ||||
| 		if w.c.writePos == len(w.c.writeBuf) { | ||||
| 			err = w.c.flushFrame(false, nil) | ||||
| 			if err != nil { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		var n int | ||||
| 		n, err = r.Read(w.c.writeBuf[w.c.writePos:]) | ||||
| 		w.c.writePos += n | ||||
| 		nn += int64(n) | ||||
| 		if err != nil { | ||||
| 			if err == io.EOF { | ||||
| 				err = nil | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return nn, err | ||||
| } | ||||
|  | ||||
| func (w messageWriter) Close() error { | ||||
| 	if err := w.err(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return w.c.flushFrame(true, nil) | ||||
| } | ||||
|  | ||||
| // WriteMessage is a helper method for getting a writer using NextWriter, | ||||
| // writing the message and closing the writer. | ||||
| func (c *Conn) WriteMessage(messageType int, data []byte) error { | ||||
| 	wr, err := c.NextWriter(messageType) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w := wr.(messageWriter) | ||||
| 	if _, err := w.write(true, data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if c.writeSeq == w.seq { | ||||
| 		if err := c.flushFrame(true, nil); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SetWriteDeadline sets the write deadline on the underlying network | ||||
| // connection. After a write has timed out, the websocket state is corrupt and | ||||
| // all future writes will return an error. A zero value for t means writes will | ||||
| // not time out. | ||||
| func (c *Conn) SetWriteDeadline(t time.Time) error { | ||||
| 	c.writeDeadline = t | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 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) { | ||||
|  | ||||
| 	// 1. Skip remainder of previous frame. | ||||
|  | ||||
| 	if c.readRemaining > 0 { | ||||
| 		if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 2. Read and parse first two bytes of frame header. | ||||
|  | ||||
| 	var b [8]byte | ||||
| 	if err := c.readFull(b[:2]); err != nil { | ||||
| 		return noFrame, err | ||||
| 	} | ||||
|  | ||||
| 	final := b[0]&finalBit != 0 | ||||
| 	frameType := int(b[0] & 0xf) | ||||
| 	reserved := int((b[0] >> 4) & 0x7) | ||||
| 	mask := b[1]&maskBit != 0 | ||||
| 	c.readRemaining = int64(b[1] & 0x7f) | ||||
|  | ||||
| 	if reserved != 0 { | ||||
| 		return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved)) | ||||
| 	} | ||||
|  | ||||
| 	switch frameType { | ||||
| 	case CloseMessage, PingMessage, PongMessage: | ||||
| 		if c.readRemaining > maxControlFramePayloadSize { | ||||
| 			return noFrame, c.handleProtocolError("control frame length > 125") | ||||
| 		} | ||||
| 		if !final { | ||||
| 			return noFrame, c.handleProtocolError("control frame not final") | ||||
| 		} | ||||
| 	case TextMessage, BinaryMessage: | ||||
| 		if !c.readFinal { | ||||
| 			return noFrame, c.handleProtocolError("message start before final message frame") | ||||
| 		} | ||||
| 		c.readFinal = final | ||||
| 	case continuationFrame: | ||||
| 		if c.readFinal { | ||||
| 			return noFrame, c.handleProtocolError("continuation after final message frame") | ||||
| 		} | ||||
| 		c.readFinal = final | ||||
| 	default: | ||||
| 		return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) | ||||
| 	} | ||||
|  | ||||
| 	// 3. Read and parse frame length. | ||||
|  | ||||
| 	switch c.readRemaining { | ||||
| 	case 126: | ||||
| 		if err := c.readFull(b[:2]); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 		c.readRemaining = int64(binary.BigEndian.Uint16(b[:2])) | ||||
| 	case 127: | ||||
| 		if err := c.readFull(b[:8]); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 		c.readRemaining = int64(binary.BigEndian.Uint64(b[:8])) | ||||
| 	} | ||||
|  | ||||
| 	// 4. Handle frame masking. | ||||
|  | ||||
| 	if mask != c.isServer { | ||||
| 		return noFrame, c.handleProtocolError("incorrect mask flag") | ||||
| 	} | ||||
|  | ||||
| 	if mask { | ||||
| 		c.readMaskPos = 0 | ||||
| 		if err := c.readFull(c.readMaskKey[:]); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 5. For text and binary messages, enforce read limit and return. | ||||
|  | ||||
| 	if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { | ||||
|  | ||||
| 		c.readLength += c.readRemaining | ||||
| 		if c.readLimit > 0 && c.readLength > c.readLimit { | ||||
| 			c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) | ||||
| 			return noFrame, ErrReadLimit | ||||
| 		} | ||||
|  | ||||
| 		return frameType, nil | ||||
| 	} | ||||
|  | ||||
| 	// 6. Read control frame payload. | ||||
|  | ||||
| 	var payload []byte | ||||
| 	if c.readRemaining > 0 { | ||||
| 		payload = make([]byte, c.readRemaining) | ||||
| 		c.readRemaining = 0 | ||||
| 		if err := c.readFull(payload); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 		if c.isServer { | ||||
| 			maskBytes(c.readMaskKey, 0, payload) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 7. Process control frame payload. | ||||
|  | ||||
| 	switch frameType { | ||||
| 	case PongMessage: | ||||
| 		if err := c.handlePong(string(payload)); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 	case PingMessage: | ||||
| 		if err := c.handlePing(string(payload)); err != nil { | ||||
| 			return noFrame, err | ||||
| 		} | ||||
| 	case CloseMessage: | ||||
| 		echoMessage := []byte{} | ||||
| 		closeCode := CloseNoStatusReceived | ||||
| 		closeText := "" | ||||
| 		if len(payload) >= 2 { | ||||
| 			echoMessage = payload[:2] | ||||
| 			closeCode = int(binary.BigEndian.Uint16(payload)) | ||||
| 			closeText = string(payload[2:]) | ||||
| 		} | ||||
| 		c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait)) | ||||
| 		return noFrame, &CloseError{Code: closeCode, Text: closeText} | ||||
| 	} | ||||
|  | ||||
| 	return frameType, nil | ||||
| } | ||||
|  | ||||
| func (c *Conn) handleProtocolError(message string) error { | ||||
| 	c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) | ||||
| 	return errors.New("websocket: " + message) | ||||
| } | ||||
|  | ||||
| // NextReader returns the next data message received from the peer. The | ||||
| // returned messageType is either TextMessage or BinaryMessage. | ||||
| // | ||||
| // There can be at most one open reader on a connection. NextReader discards | ||||
| // the previous message if the application has not already consumed it. | ||||
| // | ||||
| // Applications must break out of the application's read loop when this method | ||||
| // returns a non-nil error value. Errors returned from this method are | ||||
| // permanent. Once this method returns a non-nil error, all subsequent calls to | ||||
| // this method return the same error. | ||||
| func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | ||||
|  | ||||
| 	c.readSeq++ | ||||
| 	c.readLength = 0 | ||||
|  | ||||
| 	for c.readErr == nil { | ||||
| 		frameType, err := c.advanceFrame() | ||||
| 		if err != nil { | ||||
| 			c.readErr = hideTempErr(err) | ||||
| 			break | ||||
| 		} | ||||
| 		if frameType == TextMessage || frameType == BinaryMessage { | ||||
| 			return frameType, messageReader{c, c.readSeq}, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Applications that do handle the error returned from this method spin in | ||||
| 	// tight loop on connection failure. To help application developers detect | ||||
| 	// this error, panic on repeated reads to the failed connection. | ||||
| 	c.readErrCount++ | ||||
| 	if c.readErrCount >= 1000 { | ||||
| 		panic("repeated read on failed websocket connection") | ||||
| 	} | ||||
|  | ||||
| 	return noFrame, nil, c.readErr | ||||
| } | ||||
|  | ||||
| type messageReader struct { | ||||
| 	c   *Conn | ||||
| 	seq int | ||||
| } | ||||
|  | ||||
| func (r messageReader) Read(b []byte) (int, error) { | ||||
|  | ||||
| 	if r.seq != r.c.readSeq { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
|  | ||||
| 	for r.c.readErr == nil { | ||||
|  | ||||
| 		if r.c.readRemaining > 0 { | ||||
| 			if int64(len(b)) > r.c.readRemaining { | ||||
| 				b = b[:r.c.readRemaining] | ||||
| 			} | ||||
| 			n, err := r.c.br.Read(b) | ||||
| 			r.c.readErr = hideTempErr(err) | ||||
| 			if r.c.isServer { | ||||
| 				r.c.readMaskPos = maskBytes(r.c.readMaskKey, r.c.readMaskPos, b[:n]) | ||||
| 			} | ||||
| 			r.c.readRemaining -= int64(n) | ||||
| 			return n, r.c.readErr | ||||
| 		} | ||||
|  | ||||
| 		if r.c.readFinal { | ||||
| 			r.c.readSeq++ | ||||
| 			return 0, io.EOF | ||||
| 		} | ||||
|  | ||||
| 		frameType, err := r.c.advanceFrame() | ||||
| 		switch { | ||||
| 		case err != nil: | ||||
| 			r.c.readErr = hideTempErr(err) | ||||
| 		case frameType == TextMessage || frameType == BinaryMessage: | ||||
| 			r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err := r.c.readErr | ||||
| 	if err == io.EOF && r.seq == r.c.readSeq { | ||||
| 		err = errUnexpectedEOF | ||||
| 	} | ||||
| 	return 0, err | ||||
| } | ||||
|  | ||||
| // ReadMessage is a helper method for getting a reader using NextReader and | ||||
| // reading from that reader to a buffer. | ||||
| func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { | ||||
| 	var r io.Reader | ||||
| 	messageType, r, err = c.NextReader() | ||||
| 	if err != nil { | ||||
| 		return messageType, nil, err | ||||
| 	} | ||||
| 	p, err = ioutil.ReadAll(r) | ||||
| 	return messageType, p, err | ||||
| } | ||||
|  | ||||
| // SetReadDeadline sets the read deadline on the underlying network connection. | ||||
| // After a read has timed out, the websocket connection state is corrupt and | ||||
| // all future reads will return an error. A zero value for t means reads will | ||||
| // not time out. | ||||
| func (c *Conn) SetReadDeadline(t time.Time) error { | ||||
| 	return c.conn.SetReadDeadline(t) | ||||
| } | ||||
|  | ||||
| // SetReadLimit sets the maximum size for a message read from the peer. If a | ||||
| // message exceeds the limit, the connection sends a close frame to the peer | ||||
| // and returns ErrReadLimit to the application. | ||||
| func (c *Conn) SetReadLimit(limit int64) { | ||||
| 	c.readLimit = limit | ||||
| } | ||||
|  | ||||
| // SetPingHandler sets the handler for ping messages received from the peer. | ||||
| // The appData argument to h is the PING frame application data. The default | ||||
| // ping handler sends a pong to the peer. | ||||
| func (c *Conn) SetPingHandler(h func(appData string) error) { | ||||
| 	if h == nil { | ||||
| 		h = func(message string) error { | ||||
| 			err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) | ||||
| 			if err == ErrCloseSent { | ||||
| 				return nil | ||||
| 			} else if e, ok := err.(net.Error); ok && e.Temporary() { | ||||
| 				return nil | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	c.handlePing = h | ||||
| } | ||||
|  | ||||
| // SetPongHandler sets the handler for pong messages received from the peer. | ||||
| // The appData argument to h is the PONG frame application data. The default | ||||
| // pong handler does nothing. | ||||
| func (c *Conn) SetPongHandler(h func(appData string) error) { | ||||
| 	if h == nil { | ||||
| 		h = func(string) error { return nil } | ||||
| 	} | ||||
| 	c.handlePong = h | ||||
| } | ||||
|  | ||||
| // UnderlyingConn returns the internal net.Conn. This can be used to further | ||||
| // modifications to connection specific flags. | ||||
| func (c *Conn) UnderlyingConn() net.Conn { | ||||
| 	return c.conn | ||||
| } | ||||
|  | ||||
| // FormatCloseMessage formats closeCode and text as a WebSocket close message. | ||||
| func FormatCloseMessage(closeCode int, text string) []byte { | ||||
| 	buf := make([]byte, 2+len(text)) | ||||
| 	binary.BigEndian.PutUint16(buf, uint16(closeCode)) | ||||
| 	copy(buf[2:], text) | ||||
| 	return buf | ||||
| } | ||||
							
								
								
									
										152
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // 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 websocket implements the WebSocket protocol defined in RFC 6455. | ||||
| // | ||||
| // Overview | ||||
| // | ||||
| // The Conn type represents a WebSocket connection. A server application uses | ||||
| // the Upgrade function from an Upgrader object with a HTTP request handler | ||||
| // to get a pointer to a Conn: | ||||
| // | ||||
| //  var upgrader = websocket.Upgrader{ | ||||
| //      ReadBufferSize:  1024, | ||||
| //      WriteBufferSize: 1024, | ||||
| //  } | ||||
| // | ||||
| //  func handler(w http.ResponseWriter, r *http.Request) { | ||||
| //      conn, err := upgrader.Upgrade(w, r, nil) | ||||
| //      if err != nil { | ||||
| //          log.Println(err) | ||||
| //          return | ||||
| //      } | ||||
| //      ... Use conn to send and receive messages. | ||||
| //  } | ||||
| // | ||||
| // Call the connection's WriteMessage and ReadMessage methods to send and | ||||
| // receive messages as a slice of bytes. This snippet of code shows how to echo | ||||
| // messages using these methods: | ||||
| // | ||||
| //  for { | ||||
| //      messageType, p, err := conn.ReadMessage() | ||||
| //      if err != nil { | ||||
| //          return | ||||
| //      } | ||||
| //      if err = conn.WriteMessage(messageType, p); err != nil { | ||||
| //          return err | ||||
| //      } | ||||
| //  } | ||||
| // | ||||
| // In above snippet of code, p is a []byte and messageType is an int with value | ||||
| // websocket.BinaryMessage or websocket.TextMessage. | ||||
| // | ||||
| // An application can also send and receive messages using the io.WriteCloser | ||||
| // and io.Reader interfaces. To send a message, call the connection NextWriter | ||||
| // method to get an io.WriteCloser, write the message to the writer and close | ||||
| // the writer when done. To receive a message, call the connection NextReader | ||||
| // method to get an io.Reader and read until io.EOF is returned. This snippet | ||||
| // shows how to echo messages using the NextWriter and NextReader methods: | ||||
| // | ||||
| //  for { | ||||
| //      messageType, r, err := conn.NextReader() | ||||
| //      if err != nil { | ||||
| //          return | ||||
| //      } | ||||
| //      w, err := conn.NextWriter(messageType) | ||||
| //      if err != nil { | ||||
| //          return err | ||||
| //      } | ||||
| //      if _, err := io.Copy(w, r); err != nil { | ||||
| //          return err | ||||
| //      } | ||||
| //      if err := w.Close(); err != nil { | ||||
| //          return err | ||||
| //      } | ||||
| //  } | ||||
| // | ||||
| // Data Messages | ||||
| // | ||||
| // The WebSocket protocol distinguishes between text and binary data messages. | ||||
| // Text messages are interpreted as UTF-8 encoded text. The interpretation of | ||||
| // binary messages is left to the application. | ||||
| // | ||||
| // This package uses the TextMessage and BinaryMessage integer constants to | ||||
| // identify the two data message types. The ReadMessage and NextReader methods | ||||
| // return the type of the received message. The messageType argument to the | ||||
| // WriteMessage and NextWriter methods specifies the type of a sent message. | ||||
| // | ||||
| // It is the application's responsibility to ensure that text messages are | ||||
| // valid UTF-8 encoded text. | ||||
| // | ||||
| // Control Messages | ||||
| // | ||||
| // The WebSocket protocol defines three types of control messages: close, ping | ||||
| // and pong. Call the connection WriteControl, WriteMessage or NextWriter | ||||
| // methods to send a control message to the peer. | ||||
| // | ||||
| // Connections handle received close messages by sending a close message to the | ||||
| // peer and returning a *CloseError from the the NextReader, ReadMessage or the | ||||
| // message Read method. | ||||
| // | ||||
| // Connections handle received ping and pong messages by invoking callback | ||||
| // functions set with SetPingHandler and SetPongHandler methods. The callback | ||||
| // functions are called from the NextReader, ReadMessage and the message Read | ||||
| // methods. | ||||
| // | ||||
| // The default ping handler sends a pong to the peer. The application's reading | ||||
| // goroutine can block for a short time while the handler writes the pong data | ||||
| // to the connection. | ||||
| // | ||||
| // The application must read the connection to process ping, pong and close | ||||
| // messages sent from the peer. If the application is not otherwise interested | ||||
| // in messages from the peer, then the application should start a goroutine to | ||||
| // read and discard messages from the peer. A simple example is: | ||||
| // | ||||
| //  func readLoop(c *websocket.Conn) { | ||||
| //      for { | ||||
| //          if _, _, err := c.NextReader(); err != nil { | ||||
| //              c.Close() | ||||
| //              break | ||||
| //          } | ||||
| //      } | ||||
| //  } | ||||
| // | ||||
| // Concurrency | ||||
| // | ||||
| // Connections support one concurrent reader and one concurrent writer. | ||||
| // | ||||
| // Applications are responsible for ensuring that no more than one goroutine | ||||
| // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, | ||||
| // WriteJSON) concurrently and that no more than one goroutine calls the read | ||||
| // methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, | ||||
| // SetPingHandler) concurrently. | ||||
| // | ||||
| // The Close and WriteControl methods can be called concurrently with all other | ||||
| // methods. | ||||
| // | ||||
| // Origin Considerations | ||||
| // | ||||
| // Web browsers allow Javascript applications to open a WebSocket connection to | ||||
| // any host. It's up to the server to enforce an origin policy using the Origin | ||||
| // request header sent by the browser. | ||||
| // | ||||
| // The Upgrader calls the function specified in the CheckOrigin field to check | ||||
| // the origin. If the CheckOrigin function returns false, then the Upgrade | ||||
| // method fails the WebSocket handshake with HTTP status 403. | ||||
| // | ||||
| // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail | ||||
| // the handshake if the Origin request header is present and not equal to the | ||||
| // Host request header. | ||||
| // | ||||
| // An application can allow connections from any origin by specifying a | ||||
| // function that always returns true: | ||||
| // | ||||
| //  var upgrader = websocket.Upgrader{ | ||||
| //      CheckOrigin: func(r *http.Request) bool { return true }, | ||||
| //  } | ||||
| // | ||||
| // The deprecated Upgrade function does not enforce an origin policy. It's the | ||||
| // application's responsibility to check the Origin header before calling | ||||
| // Upgrade. | ||||
| package websocket | ||||
							
								
								
									
										246
									
								
								vendor/github.com/gorilla/websocket/examples/autobahn/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								vendor/github.com/gorilla/websocket/examples/autobahn/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| // 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. | ||||
|  | ||||
| // Command server is a test server for the Autobahn WebSockets Test Suite. | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"flag" | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| var upgrader = websocket.Upgrader{ | ||||
| 	ReadBufferSize:  4096, | ||||
| 	WriteBufferSize: 4096, | ||||
| 	CheckOrigin: func(r *http.Request) bool { | ||||
| 		return true | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| // echoCopy echoes messages from the client using io.Copy. | ||||
| func echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) { | ||||
| 	conn, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		log.Println("Upgrade:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
| 	for { | ||||
| 		mt, r, err := conn.NextReader() | ||||
| 		if err != nil { | ||||
| 			if err != io.EOF { | ||||
| 				log.Println("NextReader:", err) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		if mt == websocket.TextMessage { | ||||
| 			r = &validator{r: r} | ||||
| 		} | ||||
| 		w, err := conn.NextWriter(mt) | ||||
| 		if err != nil { | ||||
| 			log.Println("NextWriter:", err) | ||||
| 			return | ||||
| 		} | ||||
| 		if mt == websocket.TextMessage { | ||||
| 			r = &validator{r: r} | ||||
| 		} | ||||
| 		if writerOnly { | ||||
| 			_, err = io.Copy(struct{ io.Writer }{w}, r) | ||||
| 		} else { | ||||
| 			_, err = io.Copy(w, r) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			if err == errInvalidUTF8 { | ||||
| 				conn.WriteControl(websocket.CloseMessage, | ||||
| 					websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), | ||||
| 					time.Time{}) | ||||
| 			} | ||||
| 			log.Println("Copy:", err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = w.Close() | ||||
| 		if err != nil { | ||||
| 			log.Println("Close:", err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) { | ||||
| 	echoCopy(w, r, true) | ||||
| } | ||||
|  | ||||
| func echoCopyFull(w http.ResponseWriter, r *http.Request) { | ||||
| 	echoCopy(w, r, false) | ||||
| } | ||||
|  | ||||
| // echoReadAll echoes messages from the client by reading the entire message | ||||
| // with ioutil.ReadAll. | ||||
| func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { | ||||
| 	conn, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		log.Println("Upgrade:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
| 	for { | ||||
| 		mt, b, err := conn.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			if err != io.EOF { | ||||
| 				log.Println("NextReader:", err) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		if mt == websocket.TextMessage { | ||||
| 			if !utf8.Valid(b) { | ||||
| 				conn.WriteControl(websocket.CloseMessage, | ||||
| 					websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), | ||||
| 					time.Time{}) | ||||
| 				log.Println("ReadAll: invalid utf8") | ||||
| 			} | ||||
| 		} | ||||
| 		if writeMessage { | ||||
| 			err = conn.WriteMessage(mt, b) | ||||
| 			if err != nil { | ||||
| 				log.Println("WriteMessage:", err) | ||||
| 			} | ||||
| 		} else { | ||||
| 			w, err := conn.NextWriter(mt) | ||||
| 			if err != nil { | ||||
| 				log.Println("NextWriter:", err) | ||||
| 				return | ||||
| 			} | ||||
| 			if _, err := w.Write(b); err != nil { | ||||
| 				log.Println("Writer:", err) | ||||
| 				return | ||||
| 			} | ||||
| 			if err := w.Close(); err != nil { | ||||
| 				log.Println("Close:", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { | ||||
| 	echoReadAll(w, r, false) | ||||
| } | ||||
|  | ||||
| func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { | ||||
| 	echoReadAll(w, r, true) | ||||
| } | ||||
|  | ||||
| func serveHome(w http.ResponseWriter, r *http.Request) { | ||||
| 	if r.URL.Path != "/" { | ||||
| 		http.Error(w, "Not found.", 404) | ||||
| 		return | ||||
| 	} | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method not allowed", 405) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	io.WriteString(w, "<html><body>Echo Server</body></html>") | ||||
| } | ||||
|  | ||||
| var addr = flag.String("addr", ":9000", "http service address") | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	http.HandleFunc("/", serveHome) | ||||
| 	http.HandleFunc("/c", echoCopyWriterOnly) | ||||
| 	http.HandleFunc("/f", echoCopyFull) | ||||
| 	http.HandleFunc("/r", echoReadAllWriter) | ||||
| 	http.HandleFunc("/m", echoReadAllWriteMessage) | ||||
| 	err := http.ListenAndServe(*addr, nil) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("ListenAndServe: ", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type validator struct { | ||||
| 	state int | ||||
| 	x     rune | ||||
| 	r     io.Reader | ||||
| } | ||||
|  | ||||
| var errInvalidUTF8 = errors.New("invalid utf8") | ||||
|  | ||||
| func (r *validator) Read(p []byte) (int, error) { | ||||
| 	n, err := r.r.Read(p) | ||||
| 	state := r.state | ||||
| 	x := r.x | ||||
| 	for _, b := range p[:n] { | ||||
| 		state, x = decode(state, x, b) | ||||
| 		if state == utf8Reject { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	r.state = state | ||||
| 	r.x = x | ||||
| 	if state == utf8Reject || (err == io.EOF && state != utf8Accept) { | ||||
| 		return n, errInvalidUTF8 | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| // UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ | ||||
| // | ||||
| // Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> | ||||
| // | ||||
| // 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. | ||||
| var utf8d = [...]byte{ | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f | ||||
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f | ||||
| 	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf | ||||
| 	8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df | ||||
| 	0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef | ||||
| 	0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff | ||||
| 	0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 | ||||
| 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 | ||||
| 	1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 | ||||
| 	1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 | ||||
| 	1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8 | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	utf8Accept = 0 | ||||
| 	utf8Reject = 1 | ||||
| ) | ||||
|  | ||||
| func decode(state int, x rune, b byte) (int, rune) { | ||||
| 	t := utf8d[b] | ||||
| 	if state != utf8Accept { | ||||
| 		x = rune(b&0x3f) | (x << 6) | ||||
| 	} else { | ||||
| 		x = rune((0xff >> t) & b) | ||||
| 	} | ||||
| 	state = int(utf8d[256+state*16+int(t)]) | ||||
| 	return state, x | ||||
| } | ||||
							
								
								
									
										105
									
								
								vendor/github.com/gorilla/websocket/examples/chat/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/gorilla/websocket/examples/chat/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| // 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() | ||||
| } | ||||
							
								
								
									
										51
									
								
								vendor/github.com/gorilla/websocket/examples/chat/hub.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/gorilla/websocket/examples/chat/hub.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| // 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 | ||||
|  | ||||
| // hub maintains the set of active connections and broadcasts messages to the | ||||
| // connections. | ||||
| type hub struct { | ||||
| 	// Registered connections. | ||||
| 	connections map[*connection]bool | ||||
|  | ||||
| 	// Inbound messages from the connections. | ||||
| 	broadcast chan []byte | ||||
|  | ||||
| 	// Register requests from the connections. | ||||
| 	register chan *connection | ||||
|  | ||||
| 	// Unregister requests from connections. | ||||
| 	unregister chan *connection | ||||
| } | ||||
|  | ||||
| var h = hub{ | ||||
| 	broadcast:   make(chan []byte), | ||||
| 	register:    make(chan *connection), | ||||
| 	unregister:  make(chan *connection), | ||||
| 	connections: make(map[*connection]bool), | ||||
| } | ||||
|  | ||||
| func (h *hub) run() { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case c := <-h.register: | ||||
| 			h.connections[c] = true | ||||
| 		case c := <-h.unregister: | ||||
| 			if _, ok := h.connections[c]; ok { | ||||
| 				delete(h.connections, c) | ||||
| 				close(c.send) | ||||
| 			} | ||||
| 		case m := <-h.broadcast: | ||||
| 			for c := range h.connections { | ||||
| 				select { | ||||
| 				case c.send <- m: | ||||
| 				default: | ||||
| 					close(c.send) | ||||
| 					delete(h.connections, c) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/gorilla/websocket/examples/chat/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/gorilla/websocket/examples/chat/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // 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 ( | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"text/template" | ||||
| ) | ||||
|  | ||||
| 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) { | ||||
| 	if r.URL.Path != "/" { | ||||
| 		http.Error(w, "Not found", 404) | ||||
| 		return | ||||
| 	} | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method not allowed", 405) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	homeTempl.Execute(w, r.Host) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	go h.run() | ||||
| 	http.HandleFunc("/", serveHome) | ||||
| 	http.HandleFunc("/ws", serveWs) | ||||
| 	err := http.ListenAndServe(*addr, nil) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("ListenAndServe: ", err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										188
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								vendor/github.com/gorilla/websocket/examples/command/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| // Copyright 2015 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 ( | ||||
| 	"bufio" | ||||
| 	"flag" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"text/template" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	addr      = flag.String("addr", "127.0.0.1:8080", "http service address") | ||||
| 	cmdPath   string | ||||
| 	homeTempl = template.Must(template.ParseFiles("home.html")) | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Time allowed to write a message to the peer. | ||||
| 	writeWait = 10 * time.Second | ||||
|  | ||||
| 	// Maximum message size allowed from peer. | ||||
| 	maxMessageSize = 8192 | ||||
|  | ||||
| 	// 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 | ||||
| ) | ||||
|  | ||||
| func pumpStdin(ws *websocket.Conn, w io.Writer) { | ||||
| 	defer ws.Close() | ||||
| 	ws.SetReadLimit(maxMessageSize) | ||||
| 	ws.SetReadDeadline(time.Now().Add(pongWait)) | ||||
| 	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) | ||||
| 	for { | ||||
| 		_, message, err := ws.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		message = append(message, '\n') | ||||
| 		if _, err := w.Write(message); err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { | ||||
| 	defer func() { | ||||
| 		ws.Close() | ||||
| 		close(done) | ||||
| 	}() | ||||
| 	s := bufio.NewScanner(r) | ||||
| 	for s.Scan() { | ||||
| 		ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||
| 		if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if s.Err() != nil { | ||||
| 		log.Println("scan:", s.Err()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ping(ws *websocket.Conn, done chan struct{}) { | ||||
| 	ticker := time.NewTicker(pingPeriod) | ||||
| 	defer ticker.Stop() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-ticker.C: | ||||
| 			if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { | ||||
| 				log.Println("ping:", err) | ||||
| 			} | ||||
| 		case <-done: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func internalError(ws *websocket.Conn, msg string, err error) { | ||||
| 	log.Println(msg, err) | ||||
| 	ws.WriteMessage(websocket.TextMessage, []byte("Internal server error.")) | ||||
| } | ||||
|  | ||||
| var upgrader = websocket.Upgrader{} | ||||
|  | ||||
| func serveWs(w http.ResponseWriter, r *http.Request) { | ||||
| 	ws, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		log.Println("upgrade:", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	defer ws.Close() | ||||
|  | ||||
| 	outr, outw, err := os.Pipe() | ||||
| 	if err != nil { | ||||
| 		internalError(ws, "stdout:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer outr.Close() | ||||
| 	defer outw.Close() | ||||
|  | ||||
| 	inr, inw, err := os.Pipe() | ||||
| 	if err != nil { | ||||
| 		internalError(ws, "stdin:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer inr.Close() | ||||
| 	defer inw.Close() | ||||
|  | ||||
| 	proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ | ||||
| 		Files: []*os.File{inr, outw, outw}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		internalError(ws, "start:", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	inr.Close() | ||||
| 	outw.Close() | ||||
|  | ||||
| 	stdoutDone := make(chan struct{}) | ||||
| 	go pumpStdout(ws, outr, stdoutDone) | ||||
| 	go ping(ws, stdoutDone) | ||||
|  | ||||
| 	pumpStdin(ws, inw) | ||||
|  | ||||
| 	// Some commands will exit when stdin is closed. | ||||
| 	inw.Close() | ||||
|  | ||||
| 	// Other commands need a bonk on the head. | ||||
| 	if err := proc.Signal(os.Interrupt); err != nil { | ||||
| 		log.Println("inter:", err) | ||||
| 	} | ||||
|  | ||||
| 	select { | ||||
| 	case <-stdoutDone: | ||||
| 	case <-time.After(time.Second): | ||||
| 		// A bigger bonk on the head. | ||||
| 		if err := proc.Signal(os.Kill); err != nil { | ||||
| 			log.Println("term:", err) | ||||
| 		} | ||||
| 		<-stdoutDone | ||||
| 	} | ||||
|  | ||||
| 	if _, err := proc.Wait(); err != nil { | ||||
| 		log.Println("wait:", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func serveHome(w http.ResponseWriter, r *http.Request) { | ||||
| 	if r.URL.Path != "/" { | ||||
| 		http.Error(w, "Not found", 404) | ||||
| 		return | ||||
| 	} | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method not allowed", 405) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	homeTempl.Execute(w, r.Host) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	if len(flag.Args()) < 1 { | ||||
| 		log.Fatal("must specify at least one argument") | ||||
| 	} | ||||
| 	var err error | ||||
| 	cmdPath, err = exec.LookPath(flag.Args()[0]) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	http.HandleFunc("/", serveHome) | ||||
| 	http.HandleFunc("/ws", serveWs) | ||||
| 	log.Fatal(http.ListenAndServe(*addr, nil)) | ||||
| } | ||||
							
								
								
									
										81
									
								
								vendor/github.com/gorilla/websocket/examples/echo/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/gorilla/websocket/examples/echo/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| // Copyright 2015 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 ignore | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| ) | ||||
|  | ||||
| var addr = flag.String("addr", "localhost:8080", "http service address") | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	log.SetFlags(0) | ||||
|  | ||||
| 	interrupt := make(chan os.Signal, 1) | ||||
| 	signal.Notify(interrupt, os.Interrupt) | ||||
|  | ||||
| 	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"} | ||||
| 	log.Printf("connecting to %s", u.String()) | ||||
|  | ||||
| 	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("dial:", err) | ||||
| 	} | ||||
| 	defer c.Close() | ||||
|  | ||||
| 	done := make(chan struct{}) | ||||
|  | ||||
| 	go func() { | ||||
| 		defer c.Close() | ||||
| 		defer close(done) | ||||
| 		for { | ||||
| 			_, message, err := c.ReadMessage() | ||||
| 			if err != nil { | ||||
| 				log.Println("read:", err) | ||||
| 				return | ||||
| 			} | ||||
| 			log.Printf("recv: %s", message) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	ticker := time.NewTicker(time.Second) | ||||
| 	defer ticker.Stop() | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case t := <-ticker.C: | ||||
| 			err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) | ||||
| 			if err != nil { | ||||
| 				log.Println("write:", err) | ||||
| 				return | ||||
| 			} | ||||
| 		case <-interrupt: | ||||
| 			log.Println("interrupt") | ||||
| 			// To cleanly close a connection, a client should send a close | ||||
| 			// frame and wait for the server to close the connection. | ||||
| 			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) | ||||
| 			if err != nil { | ||||
| 				log.Println("write close:", err) | ||||
| 				return | ||||
| 			} | ||||
| 			select { | ||||
| 			case <-done: | ||||
| 			case <-time.After(time.Second): | ||||
| 			} | ||||
| 			c.Close() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										132
									
								
								vendor/github.com/gorilla/websocket/examples/echo/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/gorilla/websocket/examples/echo/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| // Copyright 2015 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 ignore | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"html/template" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| ) | ||||
|  | ||||
| var addr = flag.String("addr", "localhost:8080", "http service address") | ||||
|  | ||||
| var upgrader = websocket.Upgrader{} // use default options | ||||
|  | ||||
| func echo(w http.ResponseWriter, r *http.Request) { | ||||
| 	c, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		log.Print("upgrade:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer c.Close() | ||||
| 	for { | ||||
| 		mt, message, err := c.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			log.Println("read:", err) | ||||
| 			break | ||||
| 		} | ||||
| 		log.Printf("recv: %s", message) | ||||
| 		err = c.WriteMessage(mt, message) | ||||
| 		if err != nil { | ||||
| 			log.Println("write:", err) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func home(w http.ResponseWriter, r *http.Request) { | ||||
| 	homeTemplate.Execute(w, "ws://"+r.Host+"/echo") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	log.SetFlags(0) | ||||
| 	http.HandleFunc("/echo", echo) | ||||
| 	http.HandleFunc("/", home) | ||||
| 	log.Fatal(http.ListenAndServe(*addr, nil)) | ||||
| } | ||||
|  | ||||
| var homeTemplate = template.Must(template.New("").Parse(` | ||||
| <!DOCTYPE html> | ||||
| <head> | ||||
| <meta charset="utf-8"> | ||||
| <script>   | ||||
| window.addEventListener("load", function(evt) { | ||||
|  | ||||
|     var output = document.getElementById("output"); | ||||
|     var input = document.getElementById("input"); | ||||
|     var ws; | ||||
|  | ||||
|     var print = function(message) { | ||||
|         var d = document.createElement("div"); | ||||
|         d.innerHTML = message; | ||||
|         output.appendChild(d); | ||||
|     }; | ||||
|  | ||||
|     document.getElementById("open").onclick = function(evt) { | ||||
|         if (ws) { | ||||
|             return false; | ||||
|         } | ||||
|         ws = new WebSocket("{{.}}"); | ||||
|         ws.onopen = function(evt) { | ||||
|             print("OPEN"); | ||||
|         } | ||||
|         ws.onclose = function(evt) { | ||||
|             print("CLOSE"); | ||||
|             ws = null; | ||||
|         } | ||||
|         ws.onmessage = function(evt) { | ||||
|             print("RESPONSE: " + evt.data); | ||||
|         } | ||||
|         ws.onerror = function(evt) { | ||||
|             print("ERROR: " + evt.data); | ||||
|         } | ||||
|         return false; | ||||
|     }; | ||||
|  | ||||
|     document.getElementById("send").onclick = function(evt) { | ||||
|         if (!ws) { | ||||
|             return false; | ||||
|         } | ||||
|         print("SEND: " + input.value); | ||||
|         ws.send(input.value); | ||||
|         return false; | ||||
|     }; | ||||
|  | ||||
|     document.getElementById("close").onclick = function(evt) { | ||||
|         if (!ws) { | ||||
|             return false; | ||||
|         } | ||||
|         ws.close(); | ||||
|         return false; | ||||
|     }; | ||||
|  | ||||
| }); | ||||
| </script> | ||||
| </head> | ||||
| <body> | ||||
| <table> | ||||
| <tr><td valign="top" width="50%"> | ||||
| <p>Click "Open" to create a connection to the server,  | ||||
| "Send" to send a message to the server and "Close" to close the connection.  | ||||
| You can change the message and send multiple times. | ||||
| <p> | ||||
| <form> | ||||
| <button id="open">Open</button> | ||||
| <button id="close">Close</button> | ||||
| <p><input id="input" type="text" value="Hello world!"> | ||||
| <button id="send">Send</button> | ||||
| </form> | ||||
| </td><td valign="top" width="50%"> | ||||
| <div id="output"></div> | ||||
| </td></tr></table> | ||||
| </body> | ||||
| </html> | ||||
| `)) | ||||
							
								
								
									
										193
									
								
								vendor/github.com/gorilla/websocket/examples/filewatch/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								vendor/github.com/gorilla/websocket/examples/filewatch/main.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | ||||
| // 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 ( | ||||
| 	"flag" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"text/template" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Time allowed to write the file to the client. | ||||
| 	writeWait = 10 * time.Second | ||||
|  | ||||
| 	// Time allowed to read the next pong message from the client. | ||||
| 	pongWait = 60 * time.Second | ||||
|  | ||||
| 	// Send pings to client with this period. Must be less than pongWait. | ||||
| 	pingPeriod = (pongWait * 9) / 10 | ||||
|  | ||||
| 	// Poll file for changes with this period. | ||||
| 	filePeriod = 10 * time.Second | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	addr      = flag.String("addr", ":8080", "http service address") | ||||
| 	homeTempl = template.Must(template.New("").Parse(homeHTML)) | ||||
| 	filename  string | ||||
| 	upgrader  = websocket.Upgrader{ | ||||
| 		ReadBufferSize:  1024, | ||||
| 		WriteBufferSize: 1024, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { | ||||
| 	fi, err := os.Stat(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, lastMod, err | ||||
| 	} | ||||
| 	if !fi.ModTime().After(lastMod) { | ||||
| 		return nil, lastMod, nil | ||||
| 	} | ||||
| 	p, err := ioutil.ReadFile(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, fi.ModTime(), err | ||||
| 	} | ||||
| 	return p, fi.ModTime(), nil | ||||
| } | ||||
|  | ||||
| func reader(ws *websocket.Conn) { | ||||
| 	defer ws.Close() | ||||
| 	ws.SetReadLimit(512) | ||||
| 	ws.SetReadDeadline(time.Now().Add(pongWait)) | ||||
| 	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) | ||||
| 	for { | ||||
| 		_, _, err := ws.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func writer(ws *websocket.Conn, lastMod time.Time) { | ||||
| 	lastError := "" | ||||
| 	pingTicker := time.NewTicker(pingPeriod) | ||||
| 	fileTicker := time.NewTicker(filePeriod) | ||||
| 	defer func() { | ||||
| 		pingTicker.Stop() | ||||
| 		fileTicker.Stop() | ||||
| 		ws.Close() | ||||
| 	}() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-fileTicker.C: | ||||
| 			var p []byte | ||||
| 			var err error | ||||
|  | ||||
| 			p, lastMod, err = readFileIfModified(lastMod) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				if s := err.Error(); s != lastError { | ||||
| 					lastError = s | ||||
| 					p = []byte(lastError) | ||||
| 				} | ||||
| 			} else { | ||||
| 				lastError = "" | ||||
| 			} | ||||
|  | ||||
| 			if p != nil { | ||||
| 				ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||
| 				if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		case <-pingTicker.C: | ||||
| 			ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||
| 			if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func serveWs(w http.ResponseWriter, r *http.Request) { | ||||
| 	ws, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(websocket.HandshakeError); !ok { | ||||
| 			log.Println(err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var lastMod time.Time | ||||
| 	if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil { | ||||
| 		lastMod = time.Unix(0, n) | ||||
| 	} | ||||
|  | ||||
| 	go writer(ws, lastMod) | ||||
| 	reader(ws) | ||||
| } | ||||
|  | ||||
| func serveHome(w http.ResponseWriter, r *http.Request) { | ||||
| 	if r.URL.Path != "/" { | ||||
| 		http.Error(w, "Not found", 404) | ||||
| 		return | ||||
| 	} | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method not allowed", 405) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	p, lastMod, err := readFileIfModified(time.Time{}) | ||||
| 	if err != nil { | ||||
| 		p = []byte(err.Error()) | ||||
| 		lastMod = time.Unix(0, 0) | ||||
| 	} | ||||
| 	var v = struct { | ||||
| 		Host    string | ||||
| 		Data    string | ||||
| 		LastMod string | ||||
| 	}{ | ||||
| 		r.Host, | ||||
| 		string(p), | ||||
| 		strconv.FormatInt(lastMod.UnixNano(), 16), | ||||
| 	} | ||||
| 	homeTempl.Execute(w, &v) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	if flag.NArg() != 1 { | ||||
| 		log.Fatal("filename not specified") | ||||
| 	} | ||||
| 	filename = flag.Args()[0] | ||||
| 	http.HandleFunc("/", serveHome) | ||||
| 	http.HandleFunc("/ws", serveWs) | ||||
| 	if err := http.ListenAndServe(*addr, nil); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const homeHTML = `<!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|     <head> | ||||
|         <title>WebSocket Example</title> | ||||
|     </head> | ||||
|     <body> | ||||
|         <pre id="fileData">{{.Data}}</pre> | ||||
|         <script type="text/javascript"> | ||||
|             (function() { | ||||
|                 var data = document.getElementById("fileData"); | ||||
|                 var conn = new WebSocket("ws://{{.Host}}/ws?lastMod={{.LastMod}}"); | ||||
|                 conn.onclose = function(evt) { | ||||
|                     data.textContent = 'Connection closed'; | ||||
|                 } | ||||
|                 conn.onmessage = function(evt) { | ||||
|                     console.log('file updated'); | ||||
|                     data.textContent = evt.data; | ||||
|                 } | ||||
|             })(); | ||||
|         </script> | ||||
|     </body> | ||||
| </html> | ||||
| ` | ||||
							
								
								
									
										55
									
								
								vendor/github.com/gorilla/websocket/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/gorilla/websocket/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| // 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 websocket | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // WriteJSON is deprecated, use c.WriteJSON instead. | ||||
| func WriteJSON(c *Conn, v interface{}) error { | ||||
| 	return c.WriteJSON(v) | ||||
| } | ||||
|  | ||||
| // WriteJSON writes the JSON encoding of v to the connection. | ||||
| // | ||||
| // See the documentation for encoding/json Marshal for details about the | ||||
| // conversion of Go values to JSON. | ||||
| func (c *Conn) WriteJSON(v interface{}) error { | ||||
| 	w, err := c.NextWriter(TextMessage) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err1 := json.NewEncoder(w).Encode(v) | ||||
| 	err2 := w.Close() | ||||
| 	if err1 != nil { | ||||
| 		return err1 | ||||
| 	} | ||||
| 	return err2 | ||||
| } | ||||
|  | ||||
| // ReadJSON is deprecated, use c.ReadJSON instead. | ||||
| func ReadJSON(c *Conn, v interface{}) error { | ||||
| 	return c.ReadJSON(v) | ||||
| } | ||||
|  | ||||
| // ReadJSON reads the next JSON-encoded message from the connection and stores | ||||
| // it in the value pointed to by v. | ||||
| // | ||||
| // See the documentation for the encoding/json Unmarshal function for details | ||||
| // about the conversion of JSON to a Go value. | ||||
| func (c *Conn) ReadJSON(v interface{}) error { | ||||
| 	_, r, err := c.NextReader() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = json.NewDecoder(r).Decode(v) | ||||
| 	if err == io.EOF { | ||||
| 		// One value is expected in the message. | ||||
| 		err = io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										260
									
								
								vendor/github.com/gorilla/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								vendor/github.com/gorilla/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,260 @@ | ||||
| // 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 websocket | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // HandshakeError describes an error with the handshake from the peer. | ||||
| type HandshakeError struct { | ||||
| 	message string | ||||
| } | ||||
|  | ||||
| func (e HandshakeError) Error() string { return e.message } | ||||
|  | ||||
| // Upgrader specifies parameters for upgrading an HTTP connection to a | ||||
| // WebSocket connection. | ||||
| type Upgrader struct { | ||||
| 	// HandshakeTimeout specifies the duration for the handshake to complete. | ||||
| 	HandshakeTimeout time.Duration | ||||
|  | ||||
| 	// 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 | ||||
| 	// do not limit the size of the messages that can be sent or received. | ||||
| 	ReadBufferSize, WriteBufferSize int | ||||
|  | ||||
| 	// Subprotocols specifies the server's supported protocols in order of | ||||
| 	// preference. If this field is set, then the Upgrade method negotiates a | ||||
| 	// subprotocol by selecting the first match in this list with a protocol | ||||
| 	// requested by the client. | ||||
| 	Subprotocols []string | ||||
|  | ||||
| 	// Error specifies the function for generating HTTP error responses. If Error | ||||
| 	// is nil, then http.Error is used to generate the HTTP response. | ||||
| 	Error func(w http.ResponseWriter, r *http.Request, status int, reason error) | ||||
|  | ||||
| 	// CheckOrigin returns true if the request Origin header is acceptable. If | ||||
| 	// CheckOrigin is nil, the host in the Origin header must not be set or | ||||
| 	// must match the host of the request. | ||||
| 	CheckOrigin func(r *http.Request) bool | ||||
| } | ||||
|  | ||||
| func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { | ||||
| 	err := HandshakeError{reason} | ||||
| 	if u.Error != nil { | ||||
| 		u.Error(w, r, status, err) | ||||
| 	} else { | ||||
| 		http.Error(w, http.StatusText(status), status) | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
|  | ||||
| // checkSameOrigin returns true if the origin is not set or is equal to the request host. | ||||
| func checkSameOrigin(r *http.Request) bool { | ||||
| 	origin := r.Header["Origin"] | ||||
| 	if len(origin) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	u, err := url.Parse(origin[0]) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return u.Host == r.Host | ||||
| } | ||||
|  | ||||
| func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { | ||||
| 	if u.Subprotocols != nil { | ||||
| 		clientProtocols := Subprotocols(r) | ||||
| 		for _, serverProtocol := range u.Subprotocols { | ||||
| 			for _, clientProtocol := range clientProtocols { | ||||
| 				if clientProtocol == serverProtocol { | ||||
| 					return clientProtocol | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else if responseHeader != nil { | ||||
| 		return responseHeader.Get("Sec-Websocket-Protocol") | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // Upgrade upgrades the HTTP server connection to the WebSocket protocol. | ||||
| // | ||||
| // The responseHeader is included in the response to the client's upgrade | ||||
| // request. Use the responseHeader to specify cookies (Set-Cookie) and the | ||||
| // application negotiated subprotocol (Sec-Websocket-Protocol). | ||||
| // | ||||
| // If the upgrade fails, then Upgrade replies to the client with an HTTP error | ||||
| // response. | ||||
| func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { | ||||
| 	if r.Method != "GET" { | ||||
| 		return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method 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 !tokenListContainsValue(r.Header, "Connection", "upgrade") { | ||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'") | ||||
| 	} | ||||
|  | ||||
| 	if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { | ||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'") | ||||
| 	} | ||||
|  | ||||
| 	checkOrigin := u.CheckOrigin | ||||
| 	if checkOrigin == nil { | ||||
| 		checkOrigin = checkSameOrigin | ||||
| 	} | ||||
| 	if !checkOrigin(r) { | ||||
| 		return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed") | ||||
| 	} | ||||
|  | ||||
| 	challengeKey := r.Header.Get("Sec-Websocket-Key") | ||||
| 	if challengeKey == "" { | ||||
| 		return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank") | ||||
| 	} | ||||
|  | ||||
| 	subprotocol := u.selectSubprotocol(r, responseHeader) | ||||
|  | ||||
| 	var ( | ||||
| 		netConn net.Conn | ||||
| 		br      *bufio.Reader | ||||
| 		err     error | ||||
| 	) | ||||
|  | ||||
| 	h, ok := w.(http.Hijacker) | ||||
| 	if !ok { | ||||
| 		return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") | ||||
| 	} | ||||
| 	var rw *bufio.ReadWriter | ||||
| 	netConn, rw, err = h.Hijack() | ||||
| 	if err != nil { | ||||
| 		return u.returnError(w, r, http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	br = rw.Reader | ||||
|  | ||||
| 	if br.Buffered() > 0 { | ||||
| 		netConn.Close() | ||||
| 		return nil, errors.New("websocket: client sent data before handshake is complete") | ||||
| 	} | ||||
|  | ||||
| 	c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) | ||||
| 	c.subprotocol = subprotocol | ||||
|  | ||||
| 	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, computeAcceptKey(challengeKey)...) | ||||
| 	p = append(p, "\r\n"...) | ||||
| 	if c.subprotocol != "" { | ||||
| 		p = append(p, "Sec-Websocket-Protocol: "...) | ||||
| 		p = append(p, c.subprotocol...) | ||||
| 		p = append(p, "\r\n"...) | ||||
| 	} | ||||
| 	for k, vs := range responseHeader { | ||||
| 		if k == "Sec-Websocket-Protocol" { | ||||
| 			continue | ||||
| 		} | ||||
| 		for _, v := range vs { | ||||
| 			p = append(p, k...) | ||||
| 			p = append(p, ": "...) | ||||
| 			for i := 0; i < len(v); i++ { | ||||
| 				b := v[i] | ||||
| 				if b <= 31 { | ||||
| 					// prevent response splitting. | ||||
| 					b = ' ' | ||||
| 				} | ||||
| 				p = append(p, b) | ||||
| 			} | ||||
| 			p = append(p, "\r\n"...) | ||||
| 		} | ||||
| 	} | ||||
| 	p = append(p, "\r\n"...) | ||||
|  | ||||
| 	// Clear deadlines set by HTTP server. | ||||
| 	netConn.SetDeadline(time.Time{}) | ||||
|  | ||||
| 	if u.HandshakeTimeout > 0 { | ||||
| 		netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) | ||||
| 	} | ||||
| 	if _, err = netConn.Write(p); err != nil { | ||||
| 		netConn.Close() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if u.HandshakeTimeout > 0 { | ||||
| 		netConn.SetWriteDeadline(time.Time{}) | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| // Upgrade upgrades the HTTP server connection to the WebSocket protocol. | ||||
| // | ||||
| // This function is deprecated, use websocket.Upgrader instead. | ||||
| // | ||||
| // The application is responsible for checking the request origin before | ||||
| // calling Upgrade. An example implementation of the same origin policy is: | ||||
| // | ||||
| //	if req.Header.Get("Origin") != "http://"+req.Host { | ||||
| //		http.Error(w, "Origin not allowed", 403) | ||||
| //		return | ||||
| //	} | ||||
| // | ||||
| // If the endpoint supports subprotocols, then the application is responsible | ||||
| // for negotiating the protocol used on the connection. Use the Subprotocols() | ||||
| // function to get the subprotocols requested by the client. Use the | ||||
| // Sec-Websocket-Protocol response header to specify the subprotocol selected | ||||
| // by the application. | ||||
| // | ||||
| // The responseHeader is included in the response to the client's upgrade | ||||
| // request. Use the responseHeader to specify cookies (Set-Cookie) and the | ||||
| // negotiated subprotocol (Sec-Websocket-Protocol). | ||||
| // | ||||
| // The connection buffers IO to the underlying network connection. The | ||||
| // readBufSize and writeBufSize parameters specify the size of the buffers to | ||||
| // use. Messages can be larger than the buffers. | ||||
| // | ||||
| // If the request is not a valid WebSocket handshake, then Upgrade returns an | ||||
| // error of type HandshakeError. Applications should handle this error by | ||||
| // replying to the client with an HTTP error response. | ||||
| func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { | ||||
| 	u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} | ||||
| 	u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { | ||||
| 		// don't return errors to maintain backwards compatibility | ||||
| 	} | ||||
| 	u.CheckOrigin = func(r *http.Request) bool { | ||||
| 		// allow all connections by default | ||||
| 		return true | ||||
| 	} | ||||
| 	return u.Upgrade(w, r, responseHeader) | ||||
| } | ||||
|  | ||||
| // Subprotocols returns the subprotocols requested by the client in the | ||||
| // Sec-Websocket-Protocol header. | ||||
| func Subprotocols(r *http.Request) []string { | ||||
| 	h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) | ||||
| 	if h == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	protocols := strings.Split(h, ",") | ||||
| 	for i := range protocols { | ||||
| 		protocols[i] = strings.TrimSpace(protocols[i]) | ||||
| 	} | ||||
| 	return protocols | ||||
| } | ||||
|  | ||||
| // IsWebSocketUpgrade returns true if the client requested upgrade to the | ||||
| // WebSocket protocol. | ||||
| func IsWebSocketUpgrade(r *http.Request) bool { | ||||
| 	return tokenListContainsValue(r.Header, "Connection", "upgrade") && | ||||
| 		tokenListContainsValue(r.Header, "Upgrade", "websocket") | ||||
| } | ||||
							
								
								
									
										44
									
								
								vendor/github.com/gorilla/websocket/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/gorilla/websocket/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| // 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 websocket | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha1" | ||||
| 	"encoding/base64" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"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") | ||||
|  | ||||
| func computeAcceptKey(challengeKey string) string { | ||||
| 	h := sha1.New() | ||||
| 	h.Write([]byte(challengeKey)) | ||||
| 	h.Write(keyGUID) | ||||
| 	return base64.StdEncoding.EncodeToString(h.Sum(nil)) | ||||
| } | ||||
|  | ||||
| func generateChallengeKey() (string, error) { | ||||
| 	p := make([]byte, 16) | ||||
| 	if _, err := io.ReadFull(rand.Reader, p); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return base64.StdEncoding.EncodeToString(p), nil | ||||
| } | ||||
							
								
								
									
										69
									
								
								vendor/github.com/jpillora/backoff/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								vendor/github.com/jpillora/backoff/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| package backoff | ||||
|  | ||||
| import ( | ||||
| 	"math" | ||||
| 	"math/rand" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| //Backoff is a time.Duration counter. It starts at Min. | ||||
| //After every call to Duration() it is  multiplied by Factor. | ||||
| //It is capped at Max. It returns to Min on every call to Reset(). | ||||
| //Used in conjunction with the time package. | ||||
| // | ||||
| // Backoff is not threadsafe, but the ForAttempt method can be | ||||
| // used concurrently if non-zero values for Factor, Max, and Min | ||||
| // are set on the Backoff shared among threads. | ||||
| type Backoff struct { | ||||
| 	//Factor is the multiplying factor for each increment step | ||||
| 	attempts, Factor float64 | ||||
| 	//Jitter eases contention by randomizing backoff steps | ||||
| 	Jitter bool | ||||
| 	//Min and Max are the minimum and maximum values of the counter | ||||
| 	Min, Max time.Duration | ||||
| } | ||||
|  | ||||
| //Returns the current value of the counter and then | ||||
| //multiplies it Factor | ||||
| func (b *Backoff) Duration() time.Duration { | ||||
| 	d := b.ForAttempt(b.attempts) | ||||
| 	b.attempts++ | ||||
| 	return d | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // unnecessary memory storing the Backoff parameters per Backoff. The first | ||||
| // attempt should be 0. | ||||
| // | ||||
| // ForAttempt is threadsafe iff non-zero values for Factor, Max, and Min | ||||
| // are set before any calls to ForAttempt are made. | ||||
| func (b *Backoff) ForAttempt(attempt float64) time.Duration { | ||||
| 	//Zero-values are nonsensical, so we use | ||||
| 	//them to apply defaults | ||||
| 	if b.Min == 0 { | ||||
| 		b.Min = 100 * time.Millisecond | ||||
| 	} | ||||
| 	if b.Max == 0 { | ||||
| 		b.Max = 10 * time.Second | ||||
| 	} | ||||
| 	if b.Factor == 0 { | ||||
| 		b.Factor = 2 | ||||
| 	} | ||||
| 	//calculate this duration | ||||
| 	dur := float64(b.Min) * math.Pow(b.Factor, attempt) | ||||
| 	if b.Jitter == true { | ||||
| 		dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min) | ||||
| 	} | ||||
| 	//cap! | ||||
| 	if dur > float64(b.Max) { | ||||
| 		return b.Max | ||||
| 	} | ||||
| 	//return as a time.Duration | ||||
| 	return time.Duration(dur) | ||||
| } | ||||
|  | ||||
| //Resets the current value of the counter back to Min | ||||
| func (b *Backoff) Reset() { | ||||
| 	b.attempts = 0 | ||||
| } | ||||
							
								
								
									
										897
									
								
								vendor/github.com/mattermost/platform/einterfaces/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										897
									
								
								vendor/github.com/mattermost/platform/einterfaces/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/>. | ||||
							
								
								
									
										20
									
								
								vendor/github.com/mattermost/platform/einterfaces/account_migration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/mattermost/platform/einterfaces/account_migration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import "github.com/mattermost/platform/model" | ||||
|  | ||||
| type AccountMigrationInterface interface { | ||||
| 	MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError | ||||
| } | ||||
|  | ||||
| var theAccountMigrationInterface AccountMigrationInterface | ||||
|  | ||||
| func RegisterAccountMigrationInterface(newInterface AccountMigrationInterface) { | ||||
| 	theAccountMigrationInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetAccountMigrationInterface() AccountMigrationInterface { | ||||
| 	return theAccountMigrationInterface | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/github.com/mattermost/platform/einterfaces/brand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/mattermost/platform/einterfaces/brand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| 	"mime/multipart" | ||||
| ) | ||||
|  | ||||
| type BrandInterface interface { | ||||
| 	SaveBrandImage(*multipart.FileHeader) *model.AppError | ||||
| 	GetBrandImage() ([]byte, *model.AppError) | ||||
| } | ||||
|  | ||||
| var theBrandInterface BrandInterface | ||||
|  | ||||
| func RegisterBrandInterface(newInterface BrandInterface) { | ||||
| 	theBrandInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetBrandInterface() BrandInterface { | ||||
| 	return theBrandInterface | ||||
| } | ||||
							
								
								
									
										32
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type ClusterInterface interface { | ||||
| 	StartInterNodeCommunication() | ||||
| 	StopInterNodeCommunication() | ||||
| 	GetClusterInfos() []*model.ClusterInfo | ||||
| 	RemoveAllSessionsForUserId(userId string) | ||||
| 	InvalidateCacheForUser(userId string) | ||||
| 	InvalidateCacheForChannel(channelId string) | ||||
| 	Publish(event *model.WebSocketEvent) | ||||
| 	UpdateStatus(status *model.Status) | ||||
| 	GetLogs() ([]string, *model.AppError) | ||||
| 	GetClusterId() string | ||||
| 	ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError | ||||
| } | ||||
|  | ||||
| var theClusterInterface ClusterInterface | ||||
|  | ||||
| func RegisterClusterInterface(newInterface ClusterInterface) { | ||||
| 	theClusterInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetClusterInterface() ClusterInterface { | ||||
| 	return theClusterInterface | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/github.com/mattermost/platform/einterfaces/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/mattermost/platform/einterfaces/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type ComplianceInterface interface { | ||||
| 	StartComplianceDailyJob() | ||||
| 	RunComplianceJob(job *model.Compliance) *model.AppError | ||||
| } | ||||
|  | ||||
| var theComplianceInterface ComplianceInterface | ||||
|  | ||||
| func RegisterComplianceInterface(newInterface ComplianceInterface) { | ||||
| 	theComplianceInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetComplianceInterface() ComplianceInterface { | ||||
| 	return theComplianceInterface | ||||
| } | ||||
							
								
								
									
										22
									
								
								vendor/github.com/mattermost/platform/einterfaces/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/mattermost/platform/einterfaces/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type EmojiInterface interface { | ||||
| 	CanUserCreateEmoji(string, []*model.TeamMember) bool | ||||
| } | ||||
|  | ||||
| var theEmojiInterface EmojiInterface | ||||
|  | ||||
| func RegisterEmojiInterface(newInterface EmojiInterface) { | ||||
| 	theEmojiInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetEmojiInterface() EmojiInterface { | ||||
| 	return theEmojiInterface | ||||
| } | ||||
							
								
								
									
										30
									
								
								vendor/github.com/mattermost/platform/einterfaces/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/mattermost/platform/einterfaces/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type LdapInterface interface { | ||||
| 	DoLogin(id string, password string) (*model.User, *model.AppError) | ||||
| 	GetUser(id string) (*model.User, *model.AppError) | ||||
| 	CheckPassword(id string, password string) *model.AppError | ||||
| 	SwitchToLdap(userId, ldapId, ldapPassword string) *model.AppError | ||||
| 	ValidateFilter(filter string) *model.AppError | ||||
| 	Syncronize() *model.AppError | ||||
| 	StartLdapSyncJob() | ||||
| 	SyncNow() | ||||
| 	GetAllLdapUsers() ([]*model.User, *model.AppError) | ||||
| } | ||||
|  | ||||
| var theLdapInterface LdapInterface | ||||
|  | ||||
| func RegisterLdapInterface(newInterface LdapInterface) { | ||||
| 	theLdapInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetLdapInterface() LdapInterface { | ||||
| 	return theLdapInterface | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/mfa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/mfa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type MfaInterface interface { | ||||
| 	GenerateQrCode(user *model.User) ([]byte, *model.AppError) | ||||
| 	Activate(user *model.User, token string) *model.AppError | ||||
| 	Deactivate(userId string) *model.AppError | ||||
| 	ValidateToken(secret, token string) (bool, *model.AppError) | ||||
| } | ||||
|  | ||||
| var theMfaInterface MfaInterface | ||||
|  | ||||
| func RegisterMfaInterface(newInterface MfaInterface) { | ||||
| 	theMfaInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetMfaInterface() MfaInterface { | ||||
| 	return theMfaInterface | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/mattermost/platform/einterfaces/oauthproviders.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/mattermost/platform/einterfaces/oauthproviders.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type OauthProvider interface { | ||||
| 	GetIdentifier() string | ||||
| 	GetUserFromJson(data io.Reader) *model.User | ||||
| 	GetAuthDataFromJson(data io.Reader) string | ||||
| } | ||||
|  | ||||
| var oauthProviders = make(map[string]OauthProvider) | ||||
|  | ||||
| func RegisterOauthProvider(name string, newProvider OauthProvider) { | ||||
| 	oauthProviders[name] = newProvider | ||||
| } | ||||
|  | ||||
| func GetOauthProvider(name string) OauthProvider { | ||||
| 	provider, ok := oauthProviders[name] | ||||
| 	if ok { | ||||
| 		return provider | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package einterfaces | ||||
|  | ||||
| import ( | ||||
| 	"github.com/mattermost/platform/model" | ||||
| ) | ||||
|  | ||||
| type SamlInterface interface { | ||||
| 	ConfigureSP() *model.AppError | ||||
| 	BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError) | ||||
| 	DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError) | ||||
| 	GetMetadata() (string, *model.AppError) | ||||
| } | ||||
|  | ||||
| var theSamlInterface SamlInterface | ||||
|  | ||||
| func RegisterSamlInterface(newInterface SamlInterface) { | ||||
| 	theSamlInterface = newInterface | ||||
| } | ||||
|  | ||||
| func GetSamlInterface() SamlInterface { | ||||
| 	return theSamlInterface | ||||
| } | ||||
							
								
								
									
										897
									
								
								vendor/github.com/mattermost/platform/model/LICENSE.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										897
									
								
								vendor/github.com/mattermost/platform/model/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/>. | ||||
							
								
								
									
										112
									
								
								vendor/github.com/mattermost/platform/model/access.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/mattermost/platform/model/access.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	ACCESS_TOKEN_GRANT_TYPE  = "authorization_code" | ||||
| 	ACCESS_TOKEN_TYPE        = "bearer" | ||||
| 	REFRESH_TOKEN_GRANT_TYPE = "refresh_token" | ||||
| ) | ||||
|  | ||||
| type AccessData struct { | ||||
| 	ClientId     string `json:"client_id"` | ||||
| 	UserId       string `json:"user_id"` | ||||
| 	Token        string `json:"token"` | ||||
| 	RefreshToken string `json:"refresh_token"` | ||||
| 	RedirectUri  string `json:"redirect_uri"` | ||||
| 	ExpiresAt    int64  `json:"expires_at"` | ||||
| } | ||||
|  | ||||
| type AccessResponse struct { | ||||
| 	AccessToken  string `json:"access_token"` | ||||
| 	TokenType    string `json:"token_type"` | ||||
| 	ExpiresIn    int32  `json:"expires_in"` | ||||
| 	Scope        string `json:"scope"` | ||||
| 	RefreshToken string `json:"refresh_token"` | ||||
| } | ||||
|  | ||||
| // IsValid validates the AccessData and returns an error if it isn't configured | ||||
| // correctly. | ||||
| func (ad *AccessData) IsValid() *AppError { | ||||
|  | ||||
| 	if len(ad.ClientId) == 0 || len(ad.ClientId) > 26 { | ||||
| 		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.client_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.UserId) == 0 || len(ad.UserId) > 26 { | ||||
| 		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.user_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.Token) != 26 { | ||||
| 		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.access_token.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.RefreshToken) > 26 { | ||||
| 		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.refresh_token.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.RedirectUri) > 256 { | ||||
| 		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.redirect_uri.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (me *AccessData) IsExpired() bool { | ||||
|  | ||||
| 	if me.ExpiresAt <= 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if GetMillis() > me.ExpiresAt { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (ad *AccessData) ToJson() string { | ||||
| 	b, err := json.Marshal(ad) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AccessDataFromJson(data io.Reader) *AccessData { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var ad AccessData | ||||
| 	err := decoder.Decode(&ad) | ||||
| 	if err == nil { | ||||
| 		return &ad | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ar *AccessResponse) ToJson() string { | ||||
| 	b, err := json.Marshal(ar) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AccessResponseFromJson(data io.Reader) *AccessResponse { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var ar AccessResponse | ||||
| 	err := decoder.Decode(&ar) | ||||
| 	if err == nil { | ||||
| 		return &ar | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										55
									
								
								vendor/github.com/mattermost/platform/model/analytics_row.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/mattermost/platform/model/analytics_row.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type AnalyticsRow struct { | ||||
| 	Name  string  `json:"name"` | ||||
| 	Value float64 `json:"value"` | ||||
| } | ||||
|  | ||||
| type AnalyticsRows []*AnalyticsRow | ||||
|  | ||||
| func (me *AnalyticsRow) ToJson() string { | ||||
| 	b, err := json.Marshal(me) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AnalyticsRowFromJson(data io.Reader) *AnalyticsRow { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var me AnalyticsRow | ||||
| 	err := decoder.Decode(&me) | ||||
| 	if err == nil { | ||||
| 		return &me | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (me AnalyticsRows) ToJson() string { | ||||
| 	if b, err := json.Marshal(me); err != nil { | ||||
| 		return "[]" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AnalyticsRowsFromJson(data io.Reader) AnalyticsRows { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var me AnalyticsRows | ||||
| 	err := decoder.Decode(&me) | ||||
| 	if err == nil { | ||||
| 		return me | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/mattermost/platform/model/audit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/mattermost/platform/model/audit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type Audit struct { | ||||
| 	Id        string `json:"id"` | ||||
| 	CreateAt  int64  `json:"create_at"` | ||||
| 	UserId    string `json:"user_id"` | ||||
| 	Action    string `json:"action"` | ||||
| 	ExtraInfo string `json:"extra_info"` | ||||
| 	IpAddress string `json:"ip_address"` | ||||
| 	SessionId string `json:"session_id"` | ||||
| } | ||||
|  | ||||
| func (o *Audit) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AuditFromJson(data io.Reader) *Audit { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o Audit | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/mattermost/platform/model/audits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/mattermost/platform/model/audits.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type Audits []Audit | ||||
|  | ||||
| func (o Audits) Etag() string { | ||||
| 	if len(o) > 0 { | ||||
| 		// the first in the list is always the most current | ||||
| 		return Etag(o[0].CreateAt) | ||||
| 	} else { | ||||
| 		return "" | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o Audits) ToJson() string { | ||||
| 	if b, err := json.Marshal(o); err != nil { | ||||
| 		return "[]" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AuditsFromJson(data io.Reader) Audits { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o Audits | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										108
									
								
								vendor/github.com/mattermost/platform/model/authorize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								vendor/github.com/mattermost/platform/model/authorize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	AUTHCODE_EXPIRE_TIME   = 60 * 10 // 10 minutes | ||||
| 	AUTHCODE_RESPONSE_TYPE = "code" | ||||
| 	DEFAULT_SCOPE          = "user" | ||||
| ) | ||||
|  | ||||
| type AuthData struct { | ||||
| 	ClientId    string `json:"client_id"` | ||||
| 	UserId      string `json:"user_id"` | ||||
| 	Code        string `json:"code"` | ||||
| 	ExpiresIn   int32  `json:"expires_in"` | ||||
| 	CreateAt    int64  `json:"create_at"` | ||||
| 	RedirectUri string `json:"redirect_uri"` | ||||
| 	State       string `json:"state"` | ||||
| 	Scope       string `json:"scope"` | ||||
| } | ||||
|  | ||||
| // IsValid validates the AuthData and returns an error if it isn't configured | ||||
| // correctly. | ||||
| func (ad *AuthData) IsValid() *AppError { | ||||
|  | ||||
| 	if len(ad.ClientId) != 26 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.UserId) != 26 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.user_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.Code) == 0 || len(ad.Code) > 128 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.auth_code.app_error", nil, "client_id="+ad.ClientId) | ||||
| 	} | ||||
|  | ||||
| 	if ad.ExpiresIn == 0 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.expires.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if ad.CreateAt <= 0 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.create_at.app_error", nil, "client_id="+ad.ClientId) | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.RedirectUri) > 256 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ad.ClientId) | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.State) > 128 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ad.ClientId) | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.Scope) > 128 { | ||||
| 		return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ad.ClientId) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (ad *AuthData) PreSave() { | ||||
| 	if ad.ExpiresIn == 0 { | ||||
| 		ad.ExpiresIn = AUTHCODE_EXPIRE_TIME | ||||
| 	} | ||||
|  | ||||
| 	if ad.CreateAt == 0 { | ||||
| 		ad.CreateAt = GetMillis() | ||||
| 	} | ||||
|  | ||||
| 	if len(ad.Scope) == 0 { | ||||
| 		ad.Scope = DEFAULT_SCOPE | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ad *AuthData) ToJson() string { | ||||
| 	b, err := json.Marshal(ad) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func AuthDataFromJson(data io.Reader) *AuthData { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var ad AuthData | ||||
| 	err := decoder.Decode(&ad) | ||||
| 	if err == nil { | ||||
| 		return &ad | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ad *AuthData) IsExpired() bool { | ||||
|  | ||||
| 	if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										133
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	CHANNEL_OPEN    = "O" | ||||
| 	CHANNEL_PRIVATE = "P" | ||||
| 	CHANNEL_DIRECT  = "D" | ||||
| 	DEFAULT_CHANNEL = "town-square" | ||||
| ) | ||||
|  | ||||
| type Channel struct { | ||||
| 	Id            string `json:"id"` | ||||
| 	CreateAt      int64  `json:"create_at"` | ||||
| 	UpdateAt      int64  `json:"update_at"` | ||||
| 	DeleteAt      int64  `json:"delete_at"` | ||||
| 	TeamId        string `json:"team_id"` | ||||
| 	Type          string `json:"type"` | ||||
| 	DisplayName   string `json:"display_name"` | ||||
| 	Name          string `json:"name"` | ||||
| 	Header        string `json:"header"` | ||||
| 	Purpose       string `json:"purpose"` | ||||
| 	LastPostAt    int64  `json:"last_post_at"` | ||||
| 	TotalMsgCount int64  `json:"total_msg_count"` | ||||
| 	ExtraUpdateAt int64  `json:"extra_update_at"` | ||||
| 	CreatorId     string `json:"creator_id"` | ||||
| } | ||||
|  | ||||
| func (o *Channel) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ChannelFromJson(data io.Reader) *Channel { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o Channel | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *Channel) Etag() string { | ||||
| 	return Etag(o.Id, o.UpdateAt) | ||||
| } | ||||
|  | ||||
| func (o *Channel) ExtraEtag(memberLimit int) string { | ||||
| 	return Etag(o.Id, o.ExtraUpdateAt, memberLimit) | ||||
| } | ||||
|  | ||||
| func (o *Channel) IsValid() *AppError { | ||||
|  | ||||
| 	if len(o.Id) != 26 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if o.CreateAt == 0 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if o.UpdateAt == 0 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if utf8.RuneCountInString(o.DisplayName) > 64 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if len(o.Name) > 64 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if !IsValidChannelIdentifier(o.Name) { | ||||
| 		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) { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if utf8.RuneCountInString(o.Header) > 1024 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if utf8.RuneCountInString(o.Purpose) > 128 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id) | ||||
| 	} | ||||
|  | ||||
| 	if len(o.CreatorId) > 26 { | ||||
| 		return NewLocAppError("Channel.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *Channel) PreSave() { | ||||
| 	if o.Id == "" { | ||||
| 		o.Id = NewId() | ||||
| 	} | ||||
|  | ||||
| 	o.CreateAt = GetMillis() | ||||
| 	o.UpdateAt = o.CreateAt | ||||
| 	o.ExtraUpdateAt = o.CreateAt | ||||
| } | ||||
|  | ||||
| func (o *Channel) PreUpdate() { | ||||
| 	o.UpdateAt = GetMillis() | ||||
| } | ||||
|  | ||||
| func (o *Channel) ExtraUpdated() { | ||||
| 	o.ExtraUpdateAt = GetMillis() | ||||
| } | ||||
|  | ||||
| func GetDMNameFromIds(userId1, userId2 string) string { | ||||
| 	if userId1 > userId2 { | ||||
| 		return userId2 + "__" + userId1 | ||||
| 	} else { | ||||
| 		return userId1 + "__" + userId2 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										63
									
								
								vendor/github.com/mattermost/platform/model/channel_count.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/mattermost/platform/model/channel_count.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"crypto/md5" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| type ChannelCounts struct { | ||||
| 	Counts      map[string]int64 `json:"counts"` | ||||
| 	UpdateTimes map[string]int64 `json:"update_times"` | ||||
| } | ||||
|  | ||||
| func (o *ChannelCounts) Etag() string { | ||||
|  | ||||
| 	ids := []string{} | ||||
| 	for id := range o.Counts { | ||||
| 		ids = append(ids, id) | ||||
| 	} | ||||
| 	sort.Strings(ids) | ||||
|  | ||||
| 	str := "" | ||||
| 	for _, id := range ids { | ||||
| 		str += id + strconv.FormatInt(o.Counts[id], 10) | ||||
| 	} | ||||
|  | ||||
| 	md5Counts := fmt.Sprintf("%x", md5.Sum([]byte(str))) | ||||
|  | ||||
| 	var update int64 = 0 | ||||
| 	for _, u := range o.UpdateTimes { | ||||
| 		if u > update { | ||||
| 			update = u | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return Etag(md5Counts, update) | ||||
| } | ||||
|  | ||||
| func (o *ChannelCounts) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ChannelCountsFromJson(data io.Reader) *ChannelCounts { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o ChannelCounts | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										43
									
								
								vendor/github.com/mattermost/platform/model/channel_data.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/mattermost/platform/model/channel_data.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type ChannelData struct { | ||||
| 	Channel *Channel       `json:"channel"` | ||||
| 	Member  *ChannelMember `json:"member"` | ||||
| } | ||||
|  | ||||
| func (o *ChannelData) Etag() string { | ||||
| 	var mt int64 = 0 | ||||
| 	if o.Member != nil { | ||||
| 		mt = o.Member.LastUpdateAt | ||||
| 	} | ||||
|  | ||||
| 	return Etag(o.Channel.Id, o.Channel.UpdateAt, o.Channel.LastPostAt, mt) | ||||
| } | ||||
|  | ||||
| func (o *ChannelData) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ChannelDataFromJson(data io.Reader) *ChannelData { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o ChannelData | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										49
									
								
								vendor/github.com/mattermost/platform/model/channel_extra.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/mattermost/platform/model/channel_extra.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type ExtraMember struct { | ||||
| 	Id       string `json:"id"` | ||||
| 	Nickname string `json:"nickname"` | ||||
| 	Email    string `json:"email"` | ||||
| 	Roles    string `json:"roles"` | ||||
| 	Username string `json:"username"` | ||||
| } | ||||
|  | ||||
| func (o *ExtraMember) Sanitize(options map[string]bool) { | ||||
| 	if len(options) == 0 || !options["email"] { | ||||
| 		o.Email = "" | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type ChannelExtra struct { | ||||
| 	Id          string        `json:"id"` | ||||
| 	Members     []ExtraMember `json:"members"` | ||||
| 	MemberCount int64         `json:"member_count"` | ||||
| } | ||||
|  | ||||
| func (o *ChannelExtra) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ChannelExtraFromJson(data io.Reader) *ChannelExtra { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o ChannelExtra | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										77
									
								
								vendor/github.com/mattermost/platform/model/channel_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/mattermost/platform/model/channel_list.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type ChannelList struct { | ||||
| 	Channels []*Channel                `json:"channels"` | ||||
| 	Members  map[string]*ChannelMember `json:"members"` | ||||
| } | ||||
|  | ||||
| func (o *ChannelList) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *ChannelList) Etag() string { | ||||
|  | ||||
| 	id := "0" | ||||
| 	var t int64 = 0 | ||||
| 	var delta int64 = 0 | ||||
|  | ||||
| 	for _, v := range o.Channels { | ||||
| 		if v.LastPostAt > t { | ||||
| 			t = v.LastPostAt | ||||
| 			id = v.Id | ||||
| 		} | ||||
|  | ||||
| 		if v.UpdateAt > t { | ||||
| 			t = v.UpdateAt | ||||
| 			id = v.Id | ||||
| 		} | ||||
|  | ||||
| 		member := o.Members[v.Id] | ||||
|  | ||||
| 		if member != nil { | ||||
| 			max := v.LastPostAt | ||||
| 			if v.UpdateAt > max { | ||||
| 				max = v.UpdateAt | ||||
| 			} | ||||
|  | ||||
| 			delta += max - member.LastViewedAt | ||||
|  | ||||
| 			if member.LastViewedAt > t { | ||||
| 				t = member.LastViewedAt | ||||
| 				id = v.Id | ||||
| 			} | ||||
|  | ||||
| 			if member.LastUpdateAt > t { | ||||
| 				t = member.LastUpdateAt | ||||
| 				id = v.Id | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return Etag(id, t, delta, len(o.Channels)) | ||||
| } | ||||
|  | ||||
| func ChannelListFromJson(data io.Reader) *ChannelList { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o ChannelList | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										108
									
								
								vendor/github.com/mattermost/platform/model/channel_member.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								vendor/github.com/mattermost/platform/model/channel_member.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	CHANNEL_ROLE_ADMIN          = "admin" | ||||
| 	CHANNEL_NOTIFY_DEFAULT      = "default" | ||||
| 	CHANNEL_NOTIFY_ALL          = "all" | ||||
| 	CHANNEL_NOTIFY_MENTION      = "mention" | ||||
| 	CHANNEL_NOTIFY_NONE         = "none" | ||||
| 	CHANNEL_MARK_UNREAD_ALL     = "all" | ||||
| 	CHANNEL_MARK_UNREAD_MENTION = "mention" | ||||
| ) | ||||
|  | ||||
| type ChannelMember struct { | ||||
| 	ChannelId    string    `json:"channel_id"` | ||||
| 	UserId       string    `json:"user_id"` | ||||
| 	Roles        string    `json:"roles"` | ||||
| 	LastViewedAt int64     `json:"last_viewed_at"` | ||||
| 	MsgCount     int64     `json:"msg_count"` | ||||
| 	MentionCount int64     `json:"mention_count"` | ||||
| 	NotifyProps  StringMap `json:"notify_props"` | ||||
| 	LastUpdateAt int64     `json:"last_update_at"` | ||||
| } | ||||
|  | ||||
| func (o *ChannelMember) ToJson() string { | ||||
| 	b, err := json.Marshal(o) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ChannelMemberFromJson(data io.Reader) *ChannelMember { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var o ChannelMember | ||||
| 	err := decoder.Decode(&o) | ||||
| 	if err == nil { | ||||
| 		return &o | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (o *ChannelMember) IsValid() *AppError { | ||||
|  | ||||
| 	if len(o.ChannelId) != 26 { | ||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.channel_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	if len(o.UserId) != 26 { | ||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") | ||||
| 	} | ||||
|  | ||||
| 	for _, role := range strings.Split(o.Roles, " ") { | ||||
| 		if !(role == "" || role == CHANNEL_ROLE_ADMIN) { | ||||
| 			return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.role.app_error", nil, "role="+role) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	notifyLevel := o.NotifyProps["desktop"] | ||||
| 	if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { | ||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", | ||||
| 			nil, "notify_level="+notifyLevel) | ||||
| 	} | ||||
|  | ||||
| 	markUnreadLevel := o.NotifyProps["mark_unread"] | ||||
| 	if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { | ||||
| 		return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", | ||||
| 			nil, "mark_unread_level="+markUnreadLevel) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *ChannelMember) PreSave() { | ||||
| 	o.LastUpdateAt = GetMillis() | ||||
| } | ||||
|  | ||||
| func (o *ChannelMember) PreUpdate() { | ||||
| 	o.LastUpdateAt = GetMillis() | ||||
| } | ||||
|  | ||||
| func IsChannelNotifyLevelValid(notifyLevel string) bool { | ||||
| 	return notifyLevel == CHANNEL_NOTIFY_DEFAULT || | ||||
| 		notifyLevel == CHANNEL_NOTIFY_ALL || | ||||
| 		notifyLevel == CHANNEL_NOTIFY_MENTION || | ||||
| 		notifyLevel == CHANNEL_NOTIFY_NONE | ||||
| } | ||||
|  | ||||
| func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool { | ||||
| 	return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION | ||||
| } | ||||
|  | ||||
| func GetDefaultChannelNotifyProps() StringMap { | ||||
| 	return StringMap{ | ||||
| 		"desktop":     CHANNEL_NOTIFY_DEFAULT, | ||||
| 		"mark_unread": CHANNEL_MARK_UNREAD_ALL, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1818
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1818
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										66
									
								
								vendor/github.com/mattermost/platform/model/cluster_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/mattermost/platform/model/cluster_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. | ||||
| // See License.txt for license information. | ||||
|  | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| type ClusterInfo struct { | ||||
| 	Id                 string `json:"id"` | ||||
| 	Version            string `json:"version"` | ||||
| 	ConfigHash         string `json:"config_hash"` | ||||
| 	InterNodeUrl       string `json:"internode_url"` | ||||
| 	Hostname           string `json:"hostname"` | ||||
| 	LastSuccessfulPing int64  `json:"last_ping"` | ||||
| 	IsAlive            bool   `json:"is_alive"` | ||||
| } | ||||
|  | ||||
| func (me *ClusterInfo) ToJson() string { | ||||
| 	b, err := json.Marshal(me) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ClusterInfoFromJson(data io.Reader) *ClusterInfo { | ||||
| 	decoder := json.NewDecoder(data) | ||||
| 	var me ClusterInfo | ||||
| 	err := decoder.Decode(&me) | ||||
| 	if err == nil { | ||||
| 		return &me | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (me *ClusterInfo) HaveEstablishedInitialContact() bool { | ||||
| 	if me.Id != "" { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func ClusterInfosToJson(objmap []*ClusterInfo) string { | ||||
| 	if b, err := json.Marshal(objmap); err != nil { | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		return string(b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ClusterInfosFromJson(data io.Reader) []*ClusterInfo { | ||||
| 	decoder := json.NewDecoder(data) | ||||
|  | ||||
| 	var objmap []*ClusterInfo | ||||
| 	if err := decoder.Decode(&objmap); err != nil { | ||||
| 		return make([]*ClusterInfo, 0) | ||||
| 	} else { | ||||
| 		return objmap | ||||
| 	} | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user