forked from lug/matterbridge
Compare commits
7 Commits
v0.10.2-de
...
v0.10.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d7e2db65 | ||
|
|
f935c573e9 | ||
|
|
4a25e66c00 | ||
|
|
95f4e3448e | ||
|
|
eacb1c1771 | ||
|
|
07fd825349 | ||
|
|
be15cc8a36 |
11
README.md
11
README.md
@@ -42,7 +42,7 @@ Accounts to one of the supported bridges
|
||||
# Installing
|
||||
## Binaries
|
||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
|
||||
* Latest release [v0.10.1](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* Latest release [v0.10.3](https://github.com/42wim/matterbridge/releases/latest)
|
||||
|
||||
## Building
|
||||
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
|
||||
@@ -122,9 +122,11 @@ RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
|
||||
```
|
||||
|
||||
# Running
|
||||
1) Copy the matterbridge.toml.sample to matterbridge.toml in the same directory as the matterbridge binary.
|
||||
1) Copy the matterbridge.toml.sample to matterbridge.toml
|
||||
2) Edit matterbridge.toml with the settings for your environment.
|
||||
3) Now you can run matterbridge. (```./matterbridge```)
|
||||
3) Now you can run matterbridge. (```./matterbridge```)
|
||||
|
||||
(Matterbridge will only look for the config file in your current directory, if it isn't there specify -conf "/path/toyour/matterbridge.toml")
|
||||
|
||||
```
|
||||
Usage of ./matterbridge:
|
||||
@@ -132,6 +134,8 @@ Usage of ./matterbridge:
|
||||
config file (default "matterbridge.toml")
|
||||
-debug
|
||||
enable debug
|
||||
-gops
|
||||
enable gops agent
|
||||
-version
|
||||
show version
|
||||
```
|
||||
@@ -165,6 +169,7 @@ Matterbridge wouldn't exist without these libraries:
|
||||
* discord - https://github.com/bwmarrin/discordgo
|
||||
* echo - https://github.com/labstack/echo
|
||||
* gitter - https://github.com/sromku/go-gitter
|
||||
* gops - https://github.com/google/gops
|
||||
* irc - https://github.com/thoj/go-ircevent
|
||||
* mattermost - https://github.com/mattermost/platform
|
||||
* matrix - https://github.com/matrix-org/gomatrix
|
||||
|
||||
@@ -73,6 +73,10 @@ func (b *Bslack) Disconnect() error {
|
||||
func (b *Bslack) JoinChannel(channel string) error {
|
||||
// we can only join channels using the API
|
||||
if b.Config.UseAPI {
|
||||
if strings.HasPrefix(b.Config.Token, "xoxb") {
|
||||
// TODO check if bot has already joined channel
|
||||
return nil
|
||||
}
|
||||
_, err := b.sc.JoinChannel(channel)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
15
changelog.md
15
changelog.md
@@ -1,3 +1,18 @@
|
||||
# v0.10.3
|
||||
## Bugfix
|
||||
* slack: Allow bot tokens for now without warning (slack). Closes #140 (fixes user_is_bot message on channel join)
|
||||
|
||||
# v0.10.2
|
||||
## New features
|
||||
* general: gops agent added. Allows for more debugging. See #134
|
||||
* general: toml inline table support added for config file
|
||||
|
||||
## Bugfix
|
||||
* all: vendored libs updated
|
||||
|
||||
## Changes
|
||||
* general: add more informative messages on startup
|
||||
|
||||
# v0.10.1
|
||||
## Bugfix
|
||||
* gitter: Fix sending messages on new channel join.
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.10.2-dev"
|
||||
version = "0.10.3"
|
||||
githash string
|
||||
)
|
||||
|
||||
|
||||
@@ -30,3 +30,12 @@ enable=true
|
||||
[[gateway.out]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
||||
#simpler config possible since v0.10.2
|
||||
#[[gateway]]
|
||||
#name="gateway2"
|
||||
#enable=true
|
||||
#inout = [
|
||||
# { account="irc.freenode", channel="#testing", options={key="channelkey"}},
|
||||
# { account="mattermost.work", channel="off-topic" },
|
||||
#]
|
||||
|
||||
@@ -159,11 +159,11 @@ func (m *MMClient) Login() error {
|
||||
m.Client.SetTeamId(m.Team.Id)
|
||||
|
||||
// setup websocket connection
|
||||
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX + "/users/websocket"
|
||||
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX_V3 + "/users/websocket"
|
||||
header := http.Header{}
|
||||
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
||||
|
||||
m.log.Debug("WsClient: making connection")
|
||||
m.log.Debugf("WsClient: making connection: %s", wsurl)
|
||||
for {
|
||||
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
||||
|
||||
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
@@ -4,7 +4,7 @@ files via reflection. There is also support for delaying decoding with
|
||||
the Primitive type, and querying the set of keys in a TOML document with the
|
||||
MetaData type.
|
||||
|
||||
The specification implemented: https://github.com/mojombo/toml
|
||||
The specification implemented: https://github.com/toml-lang/toml
|
||||
|
||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
||||
whether a file is a valid TOML document. It can also be used to print the
|
||||
|
||||
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
@@ -241,7 +241,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||
panicIfInvalidKey(key)
|
||||
if len(key) == 1 {
|
||||
// Output an extra new line between top-level tables.
|
||||
// Output an extra newline between top-level tables.
|
||||
// (The newline isn't written if nothing else has been written though.)
|
||||
enc.newline()
|
||||
}
|
||||
|
||||
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
@@ -30,24 +30,28 @@ const (
|
||||
itemArrayTableEnd
|
||||
itemKeyStart
|
||||
itemCommentStart
|
||||
itemInlineTableStart
|
||||
itemInlineTableEnd
|
||||
)
|
||||
|
||||
const (
|
||||
eof = 0
|
||||
tableStart = '['
|
||||
tableEnd = ']'
|
||||
arrayTableStart = '['
|
||||
arrayTableEnd = ']'
|
||||
tableSep = '.'
|
||||
keySep = '='
|
||||
arrayStart = '['
|
||||
arrayEnd = ']'
|
||||
arrayValTerm = ','
|
||||
commentStart = '#'
|
||||
stringStart = '"'
|
||||
stringEnd = '"'
|
||||
rawStringStart = '\''
|
||||
rawStringEnd = '\''
|
||||
eof = 0
|
||||
comma = ','
|
||||
tableStart = '['
|
||||
tableEnd = ']'
|
||||
arrayTableStart = '['
|
||||
arrayTableEnd = ']'
|
||||
tableSep = '.'
|
||||
keySep = '='
|
||||
arrayStart = '['
|
||||
arrayEnd = ']'
|
||||
commentStart = '#'
|
||||
stringStart = '"'
|
||||
stringEnd = '"'
|
||||
rawStringStart = '\''
|
||||
rawStringEnd = '\''
|
||||
inlineTableStart = '{'
|
||||
inlineTableEnd = '}'
|
||||
)
|
||||
|
||||
type stateFn func(lx *lexer) stateFn
|
||||
@@ -56,11 +60,18 @@ type lexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
|
||||
// Allow for backing up up to three runes.
|
||||
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
||||
prevWidths [3]int
|
||||
nprev int // how many of prevWidths are in use
|
||||
// If we emit an eof, we can still back up, but it is not OK to call
|
||||
// next again.
|
||||
atEOF bool
|
||||
|
||||
// A stack of state functions used to maintain context.
|
||||
// The idea is to reuse parts of the state machine in various places.
|
||||
// For example, values can appear at the top level or within arbitrarily
|
||||
@@ -88,7 +99,7 @@ func (lx *lexer) nextItem() item {
|
||||
|
||||
func lex(input string) *lexer {
|
||||
lx := &lexer{
|
||||
input: input + "\n",
|
||||
input: input,
|
||||
state: lexTop,
|
||||
line: 1,
|
||||
items: make(chan item, 10),
|
||||
@@ -103,7 +114,7 @@ func (lx *lexer) push(state stateFn) {
|
||||
|
||||
func (lx *lexer) pop() stateFn {
|
||||
if len(lx.stack) == 0 {
|
||||
return lx.errorf("BUG in lexer: no states to pop.")
|
||||
return lx.errorf("BUG in lexer: no states to pop")
|
||||
}
|
||||
last := lx.stack[len(lx.stack)-1]
|
||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
||||
@@ -125,16 +136,25 @@ func (lx *lexer) emitTrim(typ itemType) {
|
||||
}
|
||||
|
||||
func (lx *lexer) next() (r rune) {
|
||||
if lx.atEOF {
|
||||
panic("next called after EOF")
|
||||
}
|
||||
if lx.pos >= len(lx.input) {
|
||||
lx.width = 0
|
||||
lx.atEOF = true
|
||||
return eof
|
||||
}
|
||||
|
||||
if lx.input[lx.pos] == '\n' {
|
||||
lx.line++
|
||||
}
|
||||
r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:])
|
||||
lx.pos += lx.width
|
||||
lx.prevWidths[2] = lx.prevWidths[1]
|
||||
lx.prevWidths[1] = lx.prevWidths[0]
|
||||
if lx.nprev < 3 {
|
||||
lx.nprev++
|
||||
}
|
||||
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
||||
lx.prevWidths[0] = w
|
||||
lx.pos += w
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -143,9 +163,20 @@ func (lx *lexer) ignore() {
|
||||
lx.start = lx.pos
|
||||
}
|
||||
|
||||
// backup steps back one rune. Can be called only once per call of next.
|
||||
// backup steps back one rune. Can be called only twice between calls to next.
|
||||
func (lx *lexer) backup() {
|
||||
lx.pos -= lx.width
|
||||
if lx.atEOF {
|
||||
lx.atEOF = false
|
||||
return
|
||||
}
|
||||
if lx.nprev < 1 {
|
||||
panic("backed up too far")
|
||||
}
|
||||
w := lx.prevWidths[0]
|
||||
lx.prevWidths[0] = lx.prevWidths[1]
|
||||
lx.prevWidths[1] = lx.prevWidths[2]
|
||||
lx.nprev--
|
||||
lx.pos -= w
|
||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
||||
lx.line--
|
||||
}
|
||||
@@ -182,7 +213,7 @@ func (lx *lexer) skip(pred func(rune) bool) {
|
||||
|
||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
||||
// Note that any value that is a character is escaped if it's a special
|
||||
// character (new lines, tabs, etc.).
|
||||
// character (newlines, tabs, etc.).
|
||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
||||
lx.items <- item{
|
||||
itemError,
|
||||
@@ -198,7 +229,6 @@ func lexTop(lx *lexer) stateFn {
|
||||
if isWhitespace(r) || isNL(r) {
|
||||
return lexSkip(lx, lexTop)
|
||||
}
|
||||
|
||||
switch r {
|
||||
case commentStart:
|
||||
lx.push(lexTop)
|
||||
@@ -207,7 +237,7 @@ func lexTop(lx *lexer) stateFn {
|
||||
return lexTableStart
|
||||
case eof:
|
||||
if lx.pos > lx.start {
|
||||
return lx.errorf("Unexpected EOF.")
|
||||
return lx.errorf("unexpected EOF")
|
||||
}
|
||||
lx.emit(itemEOF)
|
||||
return nil
|
||||
@@ -222,12 +252,12 @@ func lexTop(lx *lexer) stateFn {
|
||||
|
||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
||||
// upon a new line. If it sees EOF, it will quit the lexer successfully.
|
||||
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
||||
func lexTopEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == commentStart:
|
||||
// a comment will read to a new line for us.
|
||||
// a comment will read to a newline for us.
|
||||
lx.push(lexTop)
|
||||
return lexCommentStart
|
||||
case isWhitespace(r):
|
||||
@@ -236,11 +266,11 @@ func lexTopEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
return lexTop
|
||||
case r == eof:
|
||||
lx.ignore()
|
||||
return lexTop
|
||||
lx.emit(itemEOF)
|
||||
return nil
|
||||
}
|
||||
return lx.errorf("Expected a top-level item to end with a new line, "+
|
||||
"comment or EOF, but got %q instead.", r)
|
||||
return lx.errorf("expected a top-level item to end with a newline, "+
|
||||
"comment, or EOF, but got %q instead", r)
|
||||
}
|
||||
|
||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
||||
@@ -267,8 +297,8 @@ func lexTableEnd(lx *lexer) stateFn {
|
||||
|
||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
||||
if r := lx.next(); r != arrayTableEnd {
|
||||
return lx.errorf("Expected end of table array name delimiter %q, "+
|
||||
"but got %q instead.", arrayTableEnd, r)
|
||||
return lx.errorf("expected end of table array name delimiter %q, "+
|
||||
"but got %q instead", arrayTableEnd, r)
|
||||
}
|
||||
lx.emit(itemArrayTableEnd)
|
||||
return lexTopEnd
|
||||
@@ -278,11 +308,11 @@ func lexTableNameStart(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.peek(); {
|
||||
case r == tableEnd || r == eof:
|
||||
return lx.errorf("Unexpected end of table name. (Table names cannot " +
|
||||
"be empty.)")
|
||||
return lx.errorf("unexpected end of table name " +
|
||||
"(table names cannot be empty)")
|
||||
case r == tableSep:
|
||||
return lx.errorf("Unexpected table separator. (Table names cannot " +
|
||||
"be empty.)")
|
||||
return lx.errorf("unexpected table separator " +
|
||||
"(table names cannot be empty)")
|
||||
case r == stringStart || r == rawStringStart:
|
||||
lx.ignore()
|
||||
lx.push(lexTableNameEnd)
|
||||
@@ -317,8 +347,8 @@ func lexTableNameEnd(lx *lexer) stateFn {
|
||||
case r == tableEnd:
|
||||
return lx.pop()
|
||||
default:
|
||||
return lx.errorf("Expected '.' or ']' to end table name, but got %q "+
|
||||
"instead.", r)
|
||||
return lx.errorf("expected '.' or ']' to end table name, "+
|
||||
"but got %q instead", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +358,7 @@ func lexKeyStart(lx *lexer) stateFn {
|
||||
r := lx.peek()
|
||||
switch {
|
||||
case r == keySep:
|
||||
return lx.errorf("Unexpected key separator %q.", keySep)
|
||||
return lx.errorf("unexpected key separator %q", keySep)
|
||||
case isWhitespace(r) || isNL(r):
|
||||
lx.next()
|
||||
return lexSkip(lx, lexKeyStart)
|
||||
@@ -359,7 +389,7 @@ func lexBareKey(lx *lexer) stateFn {
|
||||
lx.emit(itemText)
|
||||
return lexKeyEnd
|
||||
default:
|
||||
return lx.errorf("Bare keys cannot contain %q.", r)
|
||||
return lx.errorf("bare keys cannot contain %q", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +402,7 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexKeyEnd)
|
||||
default:
|
||||
return lx.errorf("Expected key separator %q, but got %q instead.",
|
||||
return lx.errorf("expected key separator %q, but got %q instead",
|
||||
keySep, r)
|
||||
}
|
||||
}
|
||||
@@ -381,9 +411,8 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
// lexValue will ignore whitespace.
|
||||
// After a value is lexed, the last state on the next is popped and returned.
|
||||
func lexValue(lx *lexer) stateFn {
|
||||
// We allow whitespace to precede a value, but NOT new lines.
|
||||
// In array syntax, the array states are responsible for ignoring new
|
||||
// lines.
|
||||
// We allow whitespace to precede a value, but NOT newlines.
|
||||
// In array syntax, the array states are responsible for ignoring newlines.
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
@@ -397,6 +426,10 @@ func lexValue(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemArray)
|
||||
return lexArrayValue
|
||||
case inlineTableStart:
|
||||
lx.ignore()
|
||||
lx.emit(itemInlineTableStart)
|
||||
return lexInlineTableValue
|
||||
case stringStart:
|
||||
if lx.accept(stringStart) {
|
||||
if lx.accept(stringStart) {
|
||||
@@ -420,7 +453,7 @@ func lexValue(lx *lexer) stateFn {
|
||||
case '+', '-':
|
||||
return lexNumberStart
|
||||
case '.': // special error case, be kind to users
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
}
|
||||
if unicode.IsLetter(r) {
|
||||
// Be permissive here; lexBool will give a nice error if the
|
||||
@@ -430,11 +463,11 @@ func lexValue(lx *lexer) stateFn {
|
||||
lx.backup()
|
||||
return lexBool
|
||||
}
|
||||
return lx.errorf("Expected value but found %q instead.", r)
|
||||
return lx.errorf("expected value but found %q instead", r)
|
||||
}
|
||||
|
||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
||||
// have already been consumed. All whitespace and new lines are ignored.
|
||||
// have already been consumed. All whitespace and newlines are ignored.
|
||||
func lexArrayValue(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
@@ -443,10 +476,11 @@ func lexArrayValue(lx *lexer) stateFn {
|
||||
case r == commentStart:
|
||||
lx.push(lexArrayValue)
|
||||
return lexCommentStart
|
||||
case r == arrayValTerm:
|
||||
return lx.errorf("Unexpected array value terminator %q.",
|
||||
arrayValTerm)
|
||||
case r == comma:
|
||||
return lx.errorf("unexpected comma")
|
||||
case r == arrayEnd:
|
||||
// NOTE(caleb): The spec isn't clear about whether you can have
|
||||
// a trailing comma or not, so we'll allow it.
|
||||
return lexArrayEnd
|
||||
}
|
||||
|
||||
@@ -455,8 +489,9 @@ func lexArrayValue(lx *lexer) stateFn {
|
||||
return lexValue
|
||||
}
|
||||
|
||||
// lexArrayValueEnd consumes the cruft between values of an array. Namely,
|
||||
// it ignores whitespace and expects either a ',' or a ']'.
|
||||
// lexArrayValueEnd consumes everything between the end of an array value and
|
||||
// the next value (or the end of the array): it ignores whitespace and newlines
|
||||
// and expects either a ',' or a ']'.
|
||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
@@ -465,31 +500,88 @@ func lexArrayValueEnd(lx *lexer) stateFn {
|
||||
case r == commentStart:
|
||||
lx.push(lexArrayValueEnd)
|
||||
return lexCommentStart
|
||||
case r == arrayValTerm:
|
||||
case r == comma:
|
||||
lx.ignore()
|
||||
return lexArrayValue // move on to the next value
|
||||
case r == arrayEnd:
|
||||
return lexArrayEnd
|
||||
}
|
||||
return lx.errorf("Expected an array value terminator %q or an array "+
|
||||
"terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r)
|
||||
return lx.errorf(
|
||||
"expected a comma or array terminator %q, but got %q instead",
|
||||
arrayEnd, r,
|
||||
)
|
||||
}
|
||||
|
||||
// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has
|
||||
// just been consumed.
|
||||
// lexArrayEnd finishes the lexing of an array.
|
||||
// It assumes that a ']' has just been consumed.
|
||||
func lexArrayEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemArrayEnd)
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexInlineTableValue consumes one key/value pair in an inline table.
|
||||
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
||||
func lexInlineTableValue(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
case isNL(r):
|
||||
return lx.errorf("newlines not allowed within inline tables")
|
||||
case r == commentStart:
|
||||
lx.push(lexInlineTableValue)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
return lx.errorf("unexpected comma")
|
||||
case r == inlineTableEnd:
|
||||
return lexInlineTableEnd
|
||||
}
|
||||
lx.backup()
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexKeyStart
|
||||
}
|
||||
|
||||
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
||||
// key/value pair and the next pair (or the end of the table):
|
||||
// it ignores whitespace and expects either a ',' or a '}'.
|
||||
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
case isNL(r):
|
||||
return lx.errorf("newlines not allowed within inline tables")
|
||||
case r == commentStart:
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
lx.ignore()
|
||||
return lexInlineTableValue
|
||||
case r == inlineTableEnd:
|
||||
return lexInlineTableEnd
|
||||
}
|
||||
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
||||
"but got %q instead", inlineTableEnd, r)
|
||||
}
|
||||
|
||||
// lexInlineTableEnd finishes the lexing of an inline table.
|
||||
// It assumes that a '}' has just been consumed.
|
||||
func lexInlineTableEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemInlineTableEnd)
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexString consumes the inner contents of a string. It assumes that the
|
||||
// beginning '"' has already been consumed and ignored.
|
||||
func lexString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case isNL(r):
|
||||
return lx.errorf("Strings cannot contain new lines.")
|
||||
return lx.errorf("strings cannot contain newlines")
|
||||
case r == '\\':
|
||||
lx.push(lexString)
|
||||
return lexStringEscape
|
||||
@@ -506,11 +598,12 @@ func lexString(lx *lexer) stateFn {
|
||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
||||
// the beginning '"""' has already been consumed and ignored.
|
||||
func lexMultilineString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == '\\':
|
||||
switch lx.next() {
|
||||
case eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case '\\':
|
||||
return lexMultilineStringEscape
|
||||
case r == stringEnd:
|
||||
case stringEnd:
|
||||
if lx.accept(stringEnd) {
|
||||
if lx.accept(stringEnd) {
|
||||
lx.backup()
|
||||
@@ -534,8 +627,10 @@ func lexMultilineString(lx *lexer) stateFn {
|
||||
func lexRawString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case isNL(r):
|
||||
return lx.errorf("Strings cannot contain new lines.")
|
||||
return lx.errorf("strings cannot contain newlines")
|
||||
case r == rawStringEnd:
|
||||
lx.backup()
|
||||
lx.emit(itemRawString)
|
||||
@@ -547,12 +642,13 @@ func lexRawString(lx *lexer) stateFn {
|
||||
}
|
||||
|
||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
||||
// a string. It assumes that the beginning "'" has already been consumed and
|
||||
// a string. It assumes that the beginning "'''" has already been consumed and
|
||||
// ignored.
|
||||
func lexMultilineRawString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == rawStringEnd:
|
||||
switch lx.next() {
|
||||
case eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case rawStringEnd:
|
||||
if lx.accept(rawStringEnd) {
|
||||
if lx.accept(rawStringEnd) {
|
||||
lx.backup()
|
||||
@@ -605,10 +701,9 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
case 'U':
|
||||
return lexLongUnicodeEscape
|
||||
}
|
||||
return lx.errorf("Invalid escape character %q. Only the following "+
|
||||
return lx.errorf("invalid escape character %q; only the following "+
|
||||
"escape characters are allowed: "+
|
||||
"\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, "+
|
||||
"\\uXXXX and \\UXXXXXXXX.", r)
|
||||
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
||||
}
|
||||
|
||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
||||
@@ -616,8 +711,8 @@ func lexShortUnicodeEscape(lx *lexer) stateFn {
|
||||
for i := 0; i < 4; i++ {
|
||||
r = lx.next()
|
||||
if !isHexadecimal(r) {
|
||||
return lx.errorf("Expected four hexadecimal digits after '\\u', "+
|
||||
"but got '%s' instead.", lx.current())
|
||||
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
||||
"but got %q instead", lx.current())
|
||||
}
|
||||
}
|
||||
return lx.pop()
|
||||
@@ -628,8 +723,8 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
|
||||
for i := 0; i < 8; i++ {
|
||||
r = lx.next()
|
||||
if !isHexadecimal(r) {
|
||||
return lx.errorf("Expected eight hexadecimal digits after '\\U', "+
|
||||
"but got '%s' instead.", lx.current())
|
||||
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
||||
"but got %q instead", lx.current())
|
||||
}
|
||||
}
|
||||
return lx.pop()
|
||||
@@ -647,9 +742,9 @@ func lexNumberOrDateStart(lx *lexer) stateFn {
|
||||
case 'e', 'E':
|
||||
return lexFloat
|
||||
case '.':
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
}
|
||||
return lx.errorf("Expected a digit but got %q.", r)
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
}
|
||||
|
||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
||||
@@ -697,9 +792,9 @@ func lexNumberStart(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
if !isDigit(r) {
|
||||
if r == '.' {
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
}
|
||||
return lx.errorf("Expected a digit but got %q.", r)
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
}
|
||||
return lexNumber
|
||||
}
|
||||
@@ -757,7 +852,7 @@ func lexBool(lx *lexer) stateFn {
|
||||
lx.emit(itemBool)
|
||||
return lx.pop()
|
||||
}
|
||||
return lx.errorf("Expected value but found %q instead.", s)
|
||||
return lx.errorf("expected value but found %q instead", s)
|
||||
}
|
||||
|
||||
// lexCommentStart begins the lexing of a comment. It will emit
|
||||
@@ -769,7 +864,7 @@ func lexCommentStart(lx *lexer) stateFn {
|
||||
}
|
||||
|
||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
||||
// It will consume *up to* the first new line character, and pass control
|
||||
// It will consume *up to* the first newline character, and pass control
|
||||
// back to the last state on the stack.
|
||||
func lexComment(lx *lexer) stateFn {
|
||||
r := lx.peek()
|
||||
|
||||
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
@@ -269,6 +269,41 @@ func (p *parser) value(it item) (interface{}, tomlType) {
|
||||
types = append(types, typ)
|
||||
}
|
||||
return array, p.typeOfArray(types)
|
||||
case itemInlineTableStart:
|
||||
var (
|
||||
hash = make(map[string]interface{})
|
||||
outerContext = p.context
|
||||
outerKey = p.currentKey
|
||||
)
|
||||
|
||||
p.context = append(p.context, p.currentKey)
|
||||
p.currentKey = ""
|
||||
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
||||
if it.typ != itemKeyStart {
|
||||
p.bug("Expected key start but instead found %q, around line %d",
|
||||
it.val, p.approxLine)
|
||||
}
|
||||
if it.typ == itemCommentStart {
|
||||
p.expect(itemText)
|
||||
continue
|
||||
}
|
||||
|
||||
// retrieve key
|
||||
k := p.next()
|
||||
p.approxLine = k.line
|
||||
kname := p.keyString(k)
|
||||
|
||||
// retrieve value
|
||||
p.currentKey = kname
|
||||
val, typ := p.value(p.next())
|
||||
// make sure we keep metadata up to date
|
||||
p.setType(kname, typ)
|
||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
||||
hash[kname] = val
|
||||
}
|
||||
p.context = outerContext
|
||||
p.currentKey = outerKey
|
||||
return hash, tomlHash
|
||||
}
|
||||
p.bug("Unexpected value type: %s", it.typ)
|
||||
panic("unreachable")
|
||||
|
||||
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://github.com/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
@@ -3,11 +3,21 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bufferPool *sync.Pool
|
||||
|
||||
func init() {
|
||||
bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
@@ -29,6 +39,9 @@ type Entry struct {
|
||||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
@@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
reader, err := entry.Reader()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return reader.String(), err
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
@@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
@@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
reader, err := entry.Reader()
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
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)
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
@@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
||||
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
// "os"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
@@ -9,6 +10,14 @@ var log = logrus.New()
|
||||
func init() {
|
||||
log.Formatter = new(logrus.JSONFormatter)
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
|
||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||
// if err == nil {
|
||||
// log.Out = file
|
||||
// } else {
|
||||
// log.Info("Failed to log to file, using default stderr")
|
||||
// }
|
||||
|
||||
log.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
|
||||
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
@@ -31,18 +31,15 @@ type Formatter interface {
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
}
|
||||
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
}
|
||||
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
}
|
||||
}
|
||||
|
||||
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
@@ -1,61 +0,0 @@
|
||||
package logstash
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Formatter generates json in logstash format.
|
||||
// Logstash site: http://logstash.net/
|
||||
type LogstashFormatter struct {
|
||||
Type string // if not empty use for logstash type field.
|
||||
|
||||
// TimestampFormat sets the format used for timestamps.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
fields := make(logrus.Fields)
|
||||
for k, v := range entry.Data {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
fields["@version"] = 1
|
||||
|
||||
if f.TimestampFormat == "" {
|
||||
f.TimestampFormat = logrus.DefaultTimestampFormat
|
||||
}
|
||||
|
||||
fields["@timestamp"] = entry.Time.Format(f.TimestampFormat)
|
||||
|
||||
// set message field
|
||||
v, ok := entry.Data["message"]
|
||||
if ok {
|
||||
fields["fields.message"] = v
|
||||
}
|
||||
fields["message"] = entry.Message
|
||||
|
||||
// set level field
|
||||
v, ok = entry.Data["level"]
|
||||
if ok {
|
||||
fields["fields.level"] = v
|
||||
}
|
||||
fields["level"] = entry.Level.String()
|
||||
|
||||
// set type field
|
||||
if f.Type != "" {
|
||||
v, ok = entry.Data["type"]
|
||||
if ok {
|
||||
fields["fields.type"] = v
|
||||
}
|
||||
fields["type"] = f.Type
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(fields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
@@ -5,9 +5,40 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
const (
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
)
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
}
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
DisableTimestamp bool
|
||||
|
||||
// FieldMap allows users to customize the names of keys for various fields.
|
||||
// As an example:
|
||||
// formatter := &JSONFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyLevel: "@message",
|
||||
// },
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
}
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
@@ -29,9 +60,11 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
|
||||
data["time"] = entry.Time.Format(timestampFormat)
|
||||
data["msg"] = entry.Message
|
||||
data["level"] = entry.Level.String()
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
@@ -26,8 +26,31 @@ type Logger struct {
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
}
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
@@ -51,162 +74,235 @@ func New() *Logger {
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
return NewEntry(logger).WithError(err)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debug(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Error(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatal(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panic(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infoln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
NewEntry(logger).Println(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
||||
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
Normal file
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
return true
|
||||
}
|
||||
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
@@ -1,4 +1,5 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
@@ -3,6 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
@@ -4,18 +4,25 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stderr
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
@@ -1,15 +1,21 @@
|
||||
// +build solaris
|
||||
// +build solaris,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"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
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
@@ -3,11 +3,13 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build windows,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -19,9 +21,13 @@ var (
|
||||
)
|
||||
|
||||
// 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
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
@@ -3,9 +3,9 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -20,16 +20,10 @@ const (
|
||||
|
||||
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 {
|
||||
@@ -54,10 +48,32 @@ type TextFormatter struct {
|
||||
// that log extremely frequently and don't use the JSON formatter this may not
|
||||
// be desired.
|
||||
DisableSorting bool
|
||||
|
||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||
QuoteEmptyFields bool
|
||||
|
||||
// QuoteCharacter can be set to the override the default quoting character "
|
||||
// with something else. For example: ', or `.
|
||||
QuoteCharacter string
|
||||
|
||||
// Whether the logger's out is to a terminal
|
||||
isTerminal bool
|
||||
|
||||
sync.Once
|
||||
}
|
||||
|
||||
func (f *TextFormatter) init(entry *Entry) {
|
||||
if len(f.QuoteCharacter) == 0 {
|
||||
f.QuoteCharacter = "\""
|
||||
}
|
||||
if entry.Logger != nil {
|
||||
f.isTerminal = IsTerminal(entry.Logger.Out)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
var b *bytes.Buffer
|
||||
keys := make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
@@ -65,13 +81,17 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
||||
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
||||
f.Do(func() { f.init(entry) })
|
||||
|
||||
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
@@ -111,51 +131,59 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||
|
||||
if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
if f.DisableTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||
} else if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||
} else {
|
||||
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)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
}
|
||||
|
||||
func needsQuoting(text string) bool {
|
||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||
if f.QuoteEmptyFields && len(text) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, ch := range text {
|
||||
if !((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if needsQuoting(value) {
|
||||
if !f.needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
|
||||
}
|
||||
case error:
|
||||
errmsg := value.Error()
|
||||
if needsQuoting(errmsg) {
|
||||
if !f.needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
@@ -7,21 +7,52 @@ import (
|
||||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
return NewEntry(logger).WriterLevel(level)
|
||||
}
|
||||
|
||||
func (entry *Entry) Writer() *io.PipeWriter {
|
||||
return entry.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
go logger.writerScanner(reader)
|
||||
var printFunc func(args ...interface{})
|
||||
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = entry.Debug
|
||||
case InfoLevel:
|
||||
printFunc = entry.Info
|
||||
case WarnLevel:
|
||||
printFunc = entry.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = entry.Error
|
||||
case FatalLevel:
|
||||
printFunc = entry.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = entry.Panic
|
||||
default:
|
||||
printFunc = entry.Print
|
||||
}
|
||||
|
||||
go entry.writerScanner(reader, printFunc)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
logger.Print(scanner.Text())
|
||||
printFunc(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
entry.Errorf("Error while reading from Writer: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
}
|
||||
|
||||
17
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
17
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
@@ -21,8 +21,10 @@ import (
|
||||
|
||||
// BotAPI allows you to interact with the Telegram Bot API.
|
||||
type BotAPI struct {
|
||||
Token string `json:"token"`
|
||||
Debug bool `json:"debug"`
|
||||
Token string `json:"token"`
|
||||
Debug bool `json:"debug"`
|
||||
Buffer int `json:"buffer"`
|
||||
|
||||
Self User `json:"-"`
|
||||
Client *http.Client `json:"-"`
|
||||
}
|
||||
@@ -42,11 +44,12 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
|
||||
bot := &BotAPI{
|
||||
Token: token,
|
||||
Client: client,
|
||||
Buffer: 100,
|
||||
}
|
||||
|
||||
self, err := bot.GetMe()
|
||||
if err != nil {
|
||||
return &BotAPI{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bot.Self = self
|
||||
@@ -68,6 +71,10 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
|
||||
return APIResponse{}, errors.New(ErrAPIForbidden)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return APIResponse{}, errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
@@ -457,7 +464,7 @@ func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
|
||||
|
||||
// GetUpdatesChan starts and returns a channel for getting updates.
|
||||
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
|
||||
ch := make(chan Update, 100)
|
||||
ch := make(chan Update, bot.Buffer)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
@@ -484,7 +491,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
|
||||
|
||||
// ListenForWebhook registers a http handler for a webhook.
|
||||
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
|
||||
ch := make(chan Update, 100)
|
||||
ch := make(chan Update, bot.Buffer)
|
||||
|
||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
bytes, _ := ioutil.ReadAll(r.Body)
|
||||
|
||||
4
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
4
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
@@ -768,8 +768,8 @@ type UpdateConfig struct {
|
||||
|
||||
// WebhookConfig contains information about a SetWebhook request.
|
||||
type WebhookConfig struct {
|
||||
URL *url.URL
|
||||
Certificate interface{}
|
||||
URL *url.URL
|
||||
Certificate interface{}
|
||||
MaxConnections int
|
||||
}
|
||||
|
||||
|
||||
15
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
15
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
@@ -318,21 +318,6 @@ func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewWebhookWithCert creates a new webhook with a certificate and max_connections.
|
||||
//
|
||||
// link is the url you wish to get webhooks,
|
||||
// file contains a string to a file, FileReader, or FileBytes.
|
||||
// maxConnections defines maximum number of connections from telegram to your server
|
||||
func NewWebhookWithCertAndMaxConnections(link string, file interface{}, maxConnections int) WebhookConfig {
|
||||
u, _ := url.Parse(link)
|
||||
|
||||
return WebhookConfig{
|
||||
URL: u,
|
||||
Certificate: file,
|
||||
MaxConnections: maxConnections,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInlineQueryResultArticle creates a new inline query article.
|
||||
func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
|
||||
return InlineQueryResultArticle{
|
||||
|
||||
2
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
2
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
@@ -194,7 +194,7 @@ func (m *Message) CommandArguments() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.SplitN(m.Text, " ", 2)[1]
|
||||
return split[1]
|
||||
}
|
||||
|
||||
// MessageEntity contains information about data in a Message.
|
||||
|
||||
63
vendor/github.com/gorilla/schema/cache.go
generated
vendored
63
vendor/github.com/gorilla/schema/cache.go
generated
vendored
@@ -138,7 +138,12 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
if ft.Kind() == reflect.Struct {
|
||||
bef := len(info.fields)
|
||||
c.create(ft, info)
|
||||
for _, fi := range info.fields[bef:len(info.fields)] {
|
||||
// exclude required check because duplicated to embedded field
|
||||
fi.required = false
|
||||
}
|
||||
}
|
||||
}
|
||||
c.createField(field, info)
|
||||
@@ -148,7 +153,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
|
||||
|
||||
// createField creates a fieldInfo for the given field.
|
||||
func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
||||
alias := fieldAlias(field, c.tag)
|
||||
alias, options := fieldAlias(field, c.tag)
|
||||
if alias == "-" {
|
||||
// Ignore this field.
|
||||
return
|
||||
@@ -173,17 +178,19 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
||||
}
|
||||
}
|
||||
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
|
||||
if conv := c.conv[ft.Kind()]; conv == nil {
|
||||
if conv := c.converter(ft); conv == nil {
|
||||
// Type is not supported.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
info.fields = append(info.fields, &fieldInfo{
|
||||
typ: field.Type,
|
||||
name: field.Name,
|
||||
ss: isSlice && isStruct,
|
||||
alias: alias,
|
||||
typ: field.Type,
|
||||
name: field.Name,
|
||||
ss: isSlice && isStruct,
|
||||
alias: alias,
|
||||
anon: field.Anonymous,
|
||||
required: options.Contains("required"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -212,10 +219,12 @@ func (i *structInfo) get(alias string) *fieldInfo {
|
||||
}
|
||||
|
||||
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
|
||||
typ reflect.Type
|
||||
name string // field name in the struct.
|
||||
ss bool // true if this is a slice of structs.
|
||||
alias string
|
||||
anon bool // is an embedded field
|
||||
required bool // tag option
|
||||
}
|
||||
|
||||
type pathPart struct {
|
||||
@@ -227,19 +236,33 @@ type pathPart struct {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// fieldAlias parses a field tag to get a field alias.
|
||||
func fieldAlias(field reflect.StructField, tagName string) string {
|
||||
var alias string
|
||||
func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
|
||||
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]
|
||||
}
|
||||
alias, options = parseTag(tag)
|
||||
}
|
||||
if alias == "" {
|
||||
alias = field.Name
|
||||
}
|
||||
return alias
|
||||
return alias, options
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's tag, or
|
||||
// the empty string. It does not include the leading comma.
|
||||
type tagOptions []string
|
||||
|
||||
// parseTag splits a struct field's url tag into its name and comma-separated
|
||||
// options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
s := strings.Split(tag, ",")
|
||||
return s[0], s[1:]
|
||||
}
|
||||
|
||||
// Contains checks whether the tagOptions contains the specified option.
|
||||
func (o tagOptions) Contains(option string) bool {
|
||||
for _, s := range o {
|
||||
if s == option {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
51
vendor/github.com/gorilla/schema/decoder.go
generated
vendored
51
vendor/github.com/gorilla/schema/decoder.go
generated
vendored
@@ -87,9 +87,60 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
|
||||
if len(errors) > 0 {
|
||||
return errors
|
||||
}
|
||||
return d.checkRequired(t, src, "")
|
||||
}
|
||||
|
||||
// checkRequired checks whether requred field empty
|
||||
//
|
||||
// check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation
|
||||
//
|
||||
// src is the source map for decoding, we use it here to see if those required fields are included in src
|
||||
func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix string) error {
|
||||
struc := d.cache.get(t)
|
||||
if struc == nil {
|
||||
// unexpect, cache.get never return nil
|
||||
return errors.New("cache fail")
|
||||
}
|
||||
|
||||
for _, f := range struc.fields {
|
||||
if f.typ.Kind() == reflect.Struct {
|
||||
err := d.checkRequired(f.typ, src, prefix+f.alias+".")
|
||||
if err != nil {
|
||||
if !f.anon {
|
||||
return err
|
||||
}
|
||||
// check embedded parent field.
|
||||
err2 := d.checkRequired(f.typ, src, prefix)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if f.required {
|
||||
key := f.alias
|
||||
if prefix != "" {
|
||||
key = prefix + key
|
||||
}
|
||||
if isEmpty(f.typ, src[key]) {
|
||||
return fmt.Errorf("%v is empty", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isEmpty returns true if value is empty for specific type
|
||||
func isEmpty(t reflect.Type, value []string) bool {
|
||||
if len(value) == 0 {
|
||||
return true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type:
|
||||
return len(value[0]) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// decode fills a struct field using a parsed path.
|
||||
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
|
||||
// Get the field walking the struct fields by index.
|
||||
|
||||
2
vendor/github.com/gorilla/schema/doc.go
generated
vendored
2
vendor/github.com/gorilla/schema/doc.go
generated
vendored
@@ -12,7 +12,7 @@ The basic usage is really simple. Given this struct:
|
||||
Phone string
|
||||
}
|
||||
|
||||
...we can fill it passing a map to the Load() function:
|
||||
...we can fill it passing a map to the Decode() function:
|
||||
|
||||
values := map[string][]string{
|
||||
"Name": {"John"},
|
||||
|
||||
161
vendor/github.com/gorilla/schema/encoder.go
generated
vendored
Normal file
161
vendor/github.com/gorilla/schema/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type encoderFunc func(reflect.Value) string
|
||||
|
||||
// Encoder encodes values from a struct into url.Values.
|
||||
type Encoder struct {
|
||||
cache *cache
|
||||
regenc map[reflect.Type]encoderFunc
|
||||
}
|
||||
|
||||
// NewEncoder returns a new Encoder with defaults.
|
||||
func NewEncoder() *Encoder {
|
||||
return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
|
||||
}
|
||||
|
||||
// Encode encodes a struct into map[string][]string.
|
||||
//
|
||||
// Intended for use with url.Values.
|
||||
func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
|
||||
v := reflect.ValueOf(src)
|
||||
|
||||
return e.encode(v, dst)
|
||||
}
|
||||
|
||||
// RegisterEncoder registers a converter for encoding a custom type.
|
||||
func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
|
||||
e.regenc[reflect.TypeOf(value)] = encoder
|
||||
}
|
||||
|
||||
// SetAliasTag changes the tag used to locate custom field aliases.
|
||||
// The default tag is "schema".
|
||||
func (e *Encoder) SetAliasTag(tag string) {
|
||||
e.cache.tag = tag
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return errors.New("schema: interface must be a struct")
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
errors := MultiError{}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
name, opts := fieldAlias(t.Field(i), e.cache.tag)
|
||||
if name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Field(i).Type().Kind() == reflect.Struct {
|
||||
e.encode(v.Field(i), dst)
|
||||
continue
|
||||
}
|
||||
|
||||
encFunc := typeEncoder(v.Field(i).Type(), e.regenc)
|
||||
|
||||
// Encode non-slice types and custom implementations immediately.
|
||||
if encFunc != nil {
|
||||
value := encFunc(v.Field(i))
|
||||
if value == "" && opts.Contains("omitempty") {
|
||||
continue
|
||||
}
|
||||
|
||||
dst[name] = append(dst[name], value)
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Field(i).Type().Kind() == reflect.Slice {
|
||||
encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc)
|
||||
}
|
||||
|
||||
if encFunc == nil {
|
||||
errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i))
|
||||
continue
|
||||
}
|
||||
|
||||
// Encode a slice.
|
||||
if v.Field(i).Len() == 0 && opts.Contains("omitempty") {
|
||||
continue
|
||||
}
|
||||
|
||||
dst[name] = []string{}
|
||||
for j := 0; j < v.Field(i).Len(); j++ {
|
||||
dst[name] = append(dst[name], encFunc(v.Field(i).Index(j)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
|
||||
if f, ok := reg[t]; ok {
|
||||
return f
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return encodeBool
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return encodeInt
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return encodeUint
|
||||
case reflect.Float32:
|
||||
return encodeFloat32
|
||||
case reflect.Float64:
|
||||
return encodeFloat64
|
||||
case reflect.Ptr:
|
||||
f := typeEncoder(t.Elem(), reg)
|
||||
return func(v reflect.Value) string {
|
||||
if v.IsNil() {
|
||||
return "null"
|
||||
}
|
||||
return f(v.Elem())
|
||||
}
|
||||
case reflect.String:
|
||||
return encodeString
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func encodeBool(v reflect.Value) string {
|
||||
return strconv.FormatBool(v.Bool())
|
||||
}
|
||||
|
||||
func encodeInt(v reflect.Value) string {
|
||||
return strconv.FormatInt(int64(v.Int()), 10)
|
||||
}
|
||||
|
||||
func encodeUint(v reflect.Value) string {
|
||||
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||
}
|
||||
|
||||
func encodeFloat(v reflect.Value, bits int) string {
|
||||
return strconv.FormatFloat(v.Float(), 'f', 6, bits)
|
||||
}
|
||||
|
||||
func encodeFloat32(v reflect.Value) string {
|
||||
return encodeFloat(v, 32)
|
||||
}
|
||||
|
||||
func encodeFloat64(v reflect.Value) string {
|
||||
return encodeFloat(v, 64)
|
||||
}
|
||||
|
||||
func encodeString(v reflect.Value) string {
|
||||
return v.String()
|
||||
}
|
||||
60
vendor/github.com/gorilla/websocket/client.go
generated
vendored
60
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@@ -23,6 +23,8 @@ import (
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
@@ -64,12 +66,24 @@ type Dialer struct {
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// Input and output buffer sizes. If the buffer size is zero, then a
|
||||
// default value of 4096 is used.
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||
// do not limit the size of the messages that can be sent or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
@@ -83,7 +97,6 @@ func parseURL(s string) (*url.URL, error) {
|
||||
//
|
||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||
|
||||
var u url.URL
|
||||
switch {
|
||||
case strings.HasPrefix(s, "ws://"):
|
||||
@@ -193,6 +206,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
Host: u.Host,
|
||||
}
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||
// servers that depend on it. The Header.Set method is not used because the
|
||||
@@ -214,6 +234,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
default:
|
||||
@@ -221,6 +242,10 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
|
||||
var proxyURL *url.URL
|
||||
@@ -298,12 +323,8 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
}
|
||||
|
||||
if u.Scheme == "https" {
|
||||
cfg := d.TLSClientConfig
|
||||
if cfg == nil {
|
||||
cfg = &tls.Config{ServerName: hostNoPort}
|
||||
} else if cfg.ServerName == "" {
|
||||
shallowCopy := *cfg
|
||||
cfg = &shallowCopy
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
@@ -328,6 +349,13 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||
@@ -341,6 +369,20 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(resp.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
|
||||
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
||||
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// cloneTLSConfig clones all public fields except the fields
|
||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||
// config in active use.
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return &tls.Config{
|
||||
Rand: cfg.Rand,
|
||||
Time: cfg.Time,
|
||||
Certificates: cfg.Certificates,
|
||||
NameToCertificate: cfg.NameToCertificate,
|
||||
GetCertificate: cfg.GetCertificate,
|
||||
RootCAs: cfg.RootCAs,
|
||||
NextProtos: cfg.NextProtos,
|
||||
ServerName: cfg.ServerName,
|
||||
ClientAuth: cfg.ClientAuth,
|
||||
ClientCAs: cfg.ClientCAs,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CipherSuites: cfg.CipherSuites,
|
||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||
ClientSessionCache: cfg.ClientSessionCache,
|
||||
MinVersion: cfg.MinVersion,
|
||||
MaxVersion: cfg.MaxVersion,
|
||||
CurvePreferences: cfg.CurvePreferences,
|
||||
}
|
||||
}
|
||||
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
||||
maxCompressionLevel = flate.BestCompression
|
||||
defaultCompressionLevel = 1
|
||||
)
|
||||
|
||||
var (
|
||||
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
||||
flateReaderPool = sync.Pool{New: func() interface{} {
|
||||
return flate.NewReader(nil)
|
||||
}}
|
||||
)
|
||||
|
||||
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||
const tail =
|
||||
// Add four bytes as specified in RFC
|
||||
"\x00\x00\xff\xff" +
|
||||
// Add final block to squelch unexpected EOF error from flate reader.
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||
return &flateReadWrapper{fr}
|
||||
}
|
||||
|
||||
func isValidCompressionLevel(level int) bool {
|
||||
return minCompressionLevel <= level && level <= maxCompressionLevel
|
||||
}
|
||||
|
||||
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
||||
p := &flateWriterPools[level-minCompressionLevel]
|
||||
tw := &truncWriter{w: w}
|
||||
fw, _ := p.Get().(*flate.Writer)
|
||||
if fw == nil {
|
||||
fw, _ = flate.NewWriter(tw, level)
|
||||
} else {
|
||||
fw.Reset(tw)
|
||||
}
|
||||
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
||||
}
|
||||
|
||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||
// stream to another io.Writer.
|
||||
type truncWriter struct {
|
||||
w io.WriteCloser
|
||||
n int
|
||||
p [4]byte
|
||||
}
|
||||
|
||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||
n := 0
|
||||
|
||||
// fill buffer first for simplicity.
|
||||
if w.n < len(w.p) {
|
||||
n = copy(w.p[w.n:], p)
|
||||
p = p[n:]
|
||||
w.n += n
|
||||
if len(p) == 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
m := len(p)
|
||||
if m > len(w.p) {
|
||||
m = len(w.p)
|
||||
}
|
||||
|
||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
copy(w.p[:], w.p[m:])
|
||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||
nn, err := w.w.Write(p[:len(p)-m])
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
type flateWriteWrapper struct {
|
||||
fw *flate.Writer
|
||||
tw *truncWriter
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
||||
if w.fw == nil {
|
||||
return 0, errWriteClosed
|
||||
}
|
||||
return w.fw.Write(p)
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Close() error {
|
||||
if w.fw == nil {
|
||||
return errWriteClosed
|
||||
}
|
||||
err1 := w.fw.Flush()
|
||||
w.p.Put(w.fw)
|
||||
w.fw = nil
|
||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||
}
|
||||
err2 := w.tw.w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
type flateReadWrapper struct {
|
||||
fr io.ReadCloser
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||
if r.fr == nil {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
n, err := r.fr.Read(p)
|
||||
if err == io.EOF {
|
||||
// Preemptively place the reader back in the pool. This helps with
|
||||
// scenarios where the application does not call NextReader() soon after
|
||||
// this final read.
|
||||
r.Close()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Close() error {
|
||||
if r.fr == nil {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
err := r.fr.Close()
|
||||
flateReaderPool.Put(r.fr)
|
||||
r.fr = nil
|
||||
return err
|
||||
}
|
||||
632
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
632
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@@ -13,15 +13,25 @@ import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// Frame header byte 0 bits from Section 5.2 of RFC 6455
|
||||
finalBit = 1 << 7
|
||||
rsv1Bit = 1 << 6
|
||||
rsv2Bit = 1 << 5
|
||||
rsv3Bit = 1 << 4
|
||||
|
||||
// Frame header byte 1 bits from Section 5.2 of RFC 6455
|
||||
maskBit = 1 << 7
|
||||
|
||||
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
||||
maxControlFramePayloadSize = 125
|
||||
finalBit = 1 << 7
|
||||
maskBit = 1 << 7
|
||||
writeWait = time.Second
|
||||
|
||||
writeWait = time.Second
|
||||
|
||||
defaultReadBufferSize = 4096
|
||||
defaultWriteBufferSize = 4096
|
||||
@@ -43,6 +53,8 @@ const (
|
||||
CloseMessageTooBig = 1009
|
||||
CloseMandatoryExtension = 1010
|
||||
CloseInternalServerErr = 1011
|
||||
CloseServiceRestart = 1012
|
||||
CloseTryAgainLater = 1013
|
||||
CloseTLSHandshake = 1015
|
||||
)
|
||||
|
||||
@@ -169,6 +181,11 @@ var (
|
||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||
)
|
||||
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
}
|
||||
|
||||
func hideTempErr(err error) error {
|
||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||
@@ -184,74 +201,138 @@ 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
|
||||
var validReceivedCloseCodes = map[int]bool{
|
||||
// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
|
||||
|
||||
CloseNormalClosure: true,
|
||||
CloseGoingAway: true,
|
||||
CloseProtocolError: true,
|
||||
CloseUnsupportedData: true,
|
||||
CloseNoStatusReceived: false,
|
||||
CloseAbnormalClosure: false,
|
||||
CloseInvalidFramePayloadData: true,
|
||||
ClosePolicyViolation: true,
|
||||
CloseMessageTooBig: true,
|
||||
CloseMandatoryExtension: true,
|
||||
CloseInternalServerErr: true,
|
||||
CloseServiceRestart: true,
|
||||
CloseTryAgainLater: true,
|
||||
CloseTLSHandshake: false,
|
||||
}
|
||||
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
func isValidReceivedCloseCode(code int) bool {
|
||||
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
||||
}
|
||||
|
||||
// Conn represents a WebSocket connection.
|
||||
// The Conn type 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
|
||||
mu chan bool // used as mutex to protect write to conn
|
||||
writeBuf []byte // frame is constructed in this buffer.
|
||||
writeDeadline time.Time
|
||||
writer io.WriteCloser // the current writer returned to the application
|
||||
isWriting bool // for best-effort concurrent write detection
|
||||
|
||||
// Message writer fields.
|
||||
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
|
||||
writeErrMu sync.Mutex
|
||||
writeErr error
|
||||
|
||||
enableWriteCompression bool
|
||||
compressionLevel int
|
||||
newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
|
||||
|
||||
// Read fields
|
||||
reader io.ReadCloser // the current reader returned to the application
|
||||
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
|
||||
handleClose func(int, string) error
|
||||
readErrCount int
|
||||
messageReader *messageReader // the current low-level reader
|
||||
|
||||
readDecompress bool // whether last read frame had RSV1 set
|
||||
newDecompressionReader func(io.Reader) io.ReadCloser
|
||||
}
|
||||
|
||||
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
||||
return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil)
|
||||
}
|
||||
|
||||
type writeHook struct {
|
||||
p []byte
|
||||
}
|
||||
|
||||
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||
wh.p = p
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn {
|
||||
mu := make(chan bool, 1)
|
||||
mu <- true
|
||||
|
||||
if readBufferSize == 0 {
|
||||
readBufferSize = defaultReadBufferSize
|
||||
var br *bufio.Reader
|
||||
if readBufferSize == 0 && brw != nil && brw.Reader != nil {
|
||||
// Reuse the supplied bufio.Reader if the buffer has a useful size.
|
||||
// This code assumes that peek on a reader returns
|
||||
// bufio.Reader.buf[:0].
|
||||
brw.Reader.Reset(conn)
|
||||
if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
|
||||
br = brw.Reader
|
||||
}
|
||||
}
|
||||
if writeBufferSize == 0 {
|
||||
writeBufferSize = defaultWriteBufferSize
|
||||
if br == nil {
|
||||
if readBufferSize == 0 {
|
||||
readBufferSize = defaultReadBufferSize
|
||||
}
|
||||
if readBufferSize < maxControlFramePayloadSize {
|
||||
readBufferSize = maxControlFramePayloadSize
|
||||
}
|
||||
br = bufio.NewReaderSize(conn, readBufferSize)
|
||||
}
|
||||
|
||||
var writeBuf []byte
|
||||
if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
|
||||
// Use the bufio.Writer's buffer if the buffer has a useful size. This
|
||||
// code assumes that bufio.Writer.buf[:1] is passed to the
|
||||
// bufio.Writer's underlying writer.
|
||||
var wh writeHook
|
||||
brw.Writer.Reset(&wh)
|
||||
brw.Writer.WriteByte(0)
|
||||
brw.Flush()
|
||||
if cap(wh.p) >= maxFrameHeaderSize+256 {
|
||||
writeBuf = wh.p[:cap(wh.p)]
|
||||
}
|
||||
}
|
||||
|
||||
if writeBuf == nil {
|
||||
if writeBufferSize == 0 {
|
||||
writeBufferSize = defaultWriteBufferSize
|
||||
}
|
||||
writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize)
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
isServer: isServer,
|
||||
br: bufio.NewReaderSize(conn, readBufferSize),
|
||||
conn: conn,
|
||||
mu: mu,
|
||||
readFinal: true,
|
||||
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
|
||||
writeFrameType: noFrame,
|
||||
writePos: maxFrameHeaderSize,
|
||||
isServer: isServer,
|
||||
br: br,
|
||||
conn: conn,
|
||||
mu: mu,
|
||||
readFinal: true,
|
||||
writeBuf: writeBuf,
|
||||
enableWriteCompression: true,
|
||||
compressionLevel: defaultCompressionLevel,
|
||||
}
|
||||
c.SetCloseHandler(nil)
|
||||
c.SetPingHandler(nil)
|
||||
c.SetPongHandler(nil)
|
||||
return c
|
||||
@@ -279,29 +360,40 @@ func (c *Conn) RemoteAddr() net.Addr {
|
||||
|
||||
// Write methods
|
||||
|
||||
func (c *Conn) writeFatal(err error) error {
|
||||
err = hideTempErr(err)
|
||||
c.writeErrMu.Lock()
|
||||
if c.writeErr == nil {
|
||||
c.writeErr = err
|
||||
}
|
||||
c.writeErrMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
|
||||
<-c.mu
|
||||
defer func() { c.mu <- true }()
|
||||
|
||||
if c.closeSent {
|
||||
return ErrCloseSent
|
||||
} else if frameType == CloseMessage {
|
||||
c.closeSent = true
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
_, err := c.conn.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if frameType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -350,60 +442,104 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
}
|
||||
defer func() { c.mu <- true }()
|
||||
|
||||
if c.closeSent {
|
||||
return ErrCloseSent
|
||||
} else if messageType == CloseMessage {
|
||||
c.closeSent = true
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
n, err := c.conn.Write(buf)
|
||||
if n != 0 && n != len(buf) {
|
||||
c.conn.Close()
|
||||
_, err = c.conn.Write(buf)
|
||||
if err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
return hideTempErr(err)
|
||||
if messageType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NextWriter returns a writer for the next message to send. The writer's
|
||||
// Close method flushes the complete message to the network.
|
||||
func (c *Conn) prepWrite(messageType int) error {
|
||||
// Close previous writer if not already closed by the application. It's
|
||||
// probably better to return an error in this situation, but we cannot
|
||||
// change this without breaking existing applications.
|
||||
if c.writer != nil {
|
||||
c.writer.Close()
|
||||
c.writer = nil
|
||||
}
|
||||
|
||||
if !isControl(messageType) && !isData(messageType) {
|
||||
return errBadWriteOpCode
|
||||
}
|
||||
|
||||
c.writeErrMu.Lock()
|
||||
err := c.writeErr
|
||||
c.writeErrMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// NextWriter returns a writer for the next message to send. The writer's Close
|
||||
// method flushes the complete message to the network.
|
||||
//
|
||||
// There can be at most one open writer on a connection. NextWriter closes the
|
||||
// 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 err := c.prepWrite(messageType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.writeFrameType != noFrame {
|
||||
if err := c.flushFrame(true, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mw := &messageWriter{
|
||||
c: c,
|
||||
frameType: messageType,
|
||||
pos: maxFrameHeaderSize,
|
||||
}
|
||||
|
||||
if !isControl(messageType) && !isData(messageType) {
|
||||
return nil, errBadWriteOpCode
|
||||
c.writer = mw
|
||||
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
|
||||
w := c.newCompressionWriter(c.writer, c.compressionLevel)
|
||||
mw.compress = true
|
||||
c.writer = w
|
||||
}
|
||||
|
||||
c.writeFrameType = messageType
|
||||
return messageWriter{c, c.writeSeq}, nil
|
||||
return c.writer, nil
|
||||
}
|
||||
|
||||
func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
length := c.writePos - maxFrameHeaderSize + len(extra)
|
||||
type messageWriter struct {
|
||||
c *Conn
|
||||
compress bool // whether next call to flushFrame should set RSV1
|
||||
pos int // end of data in writeBuf.
|
||||
frameType int // type of the current frame.
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *messageWriter) fatal(err error) error {
|
||||
if w.err != nil {
|
||||
w.err = err
|
||||
w.c.writer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// flushFrame writes buffered data and extra as a frame to the network. The
|
||||
// final argument indicates that this is the last frame in the message.
|
||||
func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||
c := w.c
|
||||
length := w.pos - maxFrameHeaderSize + len(extra)
|
||||
|
||||
// Check for invalid control frames.
|
||||
if isControl(c.writeFrameType) &&
|
||||
if isControl(w.frameType) &&
|
||||
(!final || length > maxControlFramePayloadSize) {
|
||||
c.writeSeq++
|
||||
c.writeFrameType = noFrame
|
||||
c.writePos = maxFrameHeaderSize
|
||||
return errInvalidControlFrame
|
||||
return w.fatal(errInvalidControlFrame)
|
||||
}
|
||||
|
||||
b0 := byte(c.writeFrameType)
|
||||
b0 := byte(w.frameType)
|
||||
if final {
|
||||
b0 |= finalBit
|
||||
}
|
||||
if w.compress {
|
||||
b0 |= rsv1Bit
|
||||
}
|
||||
w.compress = false
|
||||
|
||||
b1 := byte(0)
|
||||
if !c.isServer {
|
||||
b1 |= maskBit
|
||||
@@ -435,10 +571,9 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
if !c.isServer {
|
||||
key := newMaskKey()
|
||||
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
|
||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
|
||||
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])
|
||||
if len(extra) > 0 {
|
||||
c.writeErr = errors.New("websocket: internal error, extra used in client mode")
|
||||
return c.writeErr
|
||||
return c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,46 +586,35 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
||||
}
|
||||
c.isWriting = true
|
||||
|
||||
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
||||
err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
|
||||
|
||||
if !c.isWriting {
|
||||
panic("concurrent write to websocket connection")
|
||||
}
|
||||
c.isWriting = false
|
||||
|
||||
// Setup for next frame.
|
||||
c.writePos = maxFrameHeaderSize
|
||||
c.writeFrameType = continuationFrame
|
||||
if err != nil {
|
||||
return w.fatal(err)
|
||||
}
|
||||
|
||||
if final {
|
||||
c.writeSeq++
|
||||
c.writeFrameType = noFrame
|
||||
c.writer = nil
|
||||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
// Setup for next frame.
|
||||
w.pos = maxFrameHeaderSize
|
||||
w.frameType = continuationFrame
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w messageWriter) ncopy(max int) (int, error) {
|
||||
n := len(w.c.writeBuf) - w.c.writePos
|
||||
func (w *messageWriter) ncopy(max int) (int, error) {
|
||||
n := len(w.c.writeBuf) - w.pos
|
||||
if n <= 0 {
|
||||
if err := w.c.flushFrame(false, nil); err != nil {
|
||||
if err := w.flushFrame(false, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = len(w.c.writeBuf) - w.c.writePos
|
||||
n = len(w.c.writeBuf) - w.pos
|
||||
}
|
||||
if n > max {
|
||||
n = max
|
||||
@@ -498,14 +622,14 @@ func (w messageWriter) ncopy(max int) (int, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w messageWriter) write(final bool, p []byte) (int, error) {
|
||||
if err := w.err(); err != nil {
|
||||
return 0, err
|
||||
func (w *messageWriter) Write(p []byte) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
|
||||
// Don't buffer large messages.
|
||||
err := w.c.flushFrame(final, p)
|
||||
err := w.flushFrame(false, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -518,20 +642,16 @@ func (w messageWriter) write(final bool, p []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
||||
w.c.writePos += n
|
||||
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||
w.pos += 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
|
||||
func (w *messageWriter) WriteString(p string) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
nn := len(p)
|
||||
@@ -540,27 +660,27 @@ func (w messageWriter) WriteString(p string) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(w.c.writeBuf[w.c.writePos:], p[:n])
|
||||
w.c.writePos += n
|
||||
copy(w.c.writeBuf[w.pos:], p[:n])
|
||||
w.pos += 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
|
||||
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for {
|
||||
if w.c.writePos == len(w.c.writeBuf) {
|
||||
err = w.c.flushFrame(false, nil)
|
||||
if w.pos == len(w.c.writeBuf) {
|
||||
err = w.flushFrame(false, nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
var n int
|
||||
n, err = r.Read(w.c.writeBuf[w.c.writePos:])
|
||||
w.c.writePos += n
|
||||
n, err = r.Read(w.c.writeBuf[w.pos:])
|
||||
w.pos += n
|
||||
nn += int64(n)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -572,30 +692,64 @@ func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
|
||||
return nn, err
|
||||
}
|
||||
|
||||
func (w messageWriter) Close() error {
|
||||
if err := w.err(); err != nil {
|
||||
func (w *messageWriter) Close() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if err := w.flushFrame(true, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return w.c.flushFrame(true, nil)
|
||||
w.err = errWriteClosed
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePreparedMessage writes prepared message into connection.
|
||||
func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {
|
||||
frameType, frameData, err := pm.frame(prepareKey{
|
||||
isServer: c.isServer,
|
||||
compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),
|
||||
compressionLevel: c.compressionLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.isWriting {
|
||||
panic("concurrent write to websocket connection")
|
||||
}
|
||||
c.isWriting = true
|
||||
err = c.write(frameType, c.writeDeadline, frameData, nil)
|
||||
if !c.isWriting {
|
||||
panic("concurrent write to websocket connection")
|
||||
}
|
||||
c.isWriting = false
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteMessage is a helper method for getting a writer using NextWriter,
|
||||
// writing the message and closing the writer.
|
||||
func (c *Conn) WriteMessage(messageType int, data []byte) error {
|
||||
wr, err := c.NextWriter(messageType)
|
||||
|
||||
if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
|
||||
// Fast path with no allocations and single frame.
|
||||
|
||||
if err := c.prepWrite(messageType); err != nil {
|
||||
return err
|
||||
}
|
||||
mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
|
||||
n := copy(c.writeBuf[mw.pos:], data)
|
||||
mw.pos += n
|
||||
data = data[n:]
|
||||
return mw.flushFrame(true, data)
|
||||
}
|
||||
|
||||
w, err := c.NextWriter(messageType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := wr.(messageWriter)
|
||||
if _, err := w.write(true, data); err != nil {
|
||||
if _, err = w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.writeSeq == w.seq {
|
||||
if err := c.flushFrame(true, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline on the underlying network
|
||||
@@ -609,22 +763,6 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
|
||||
// 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.
|
||||
@@ -637,19 +775,24 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
// 2. Read and parse first two bytes of frame header.
|
||||
|
||||
var b [8]byte
|
||||
if err := c.readFull(b[:2]); err != nil {
|
||||
p, err := c.read(2)
|
||||
if 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)
|
||||
final := p[0]&finalBit != 0
|
||||
frameType := int(p[0] & 0xf)
|
||||
mask := p[1]&maskBit != 0
|
||||
c.readRemaining = int64(p[1] & 0x7f)
|
||||
|
||||
if reserved != 0 {
|
||||
return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
|
||||
c.readDecompress = false
|
||||
if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {
|
||||
c.readDecompress = true
|
||||
p[0] &^= rsv1Bit
|
||||
}
|
||||
|
||||
if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
|
||||
return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16))
|
||||
}
|
||||
|
||||
switch frameType {
|
||||
@@ -678,15 +821,17 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
switch c.readRemaining {
|
||||
case 126:
|
||||
if err := c.readFull(b[:2]); err != nil {
|
||||
p, err := c.read(2)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
c.readRemaining = int64(binary.BigEndian.Uint16(b[:2]))
|
||||
c.readRemaining = int64(binary.BigEndian.Uint16(p))
|
||||
case 127:
|
||||
if err := c.readFull(b[:8]); err != nil {
|
||||
p, err := c.read(8)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
c.readRemaining = int64(binary.BigEndian.Uint64(b[:8]))
|
||||
c.readRemaining = int64(binary.BigEndian.Uint64(p))
|
||||
}
|
||||
|
||||
// 4. Handle frame masking.
|
||||
@@ -697,9 +842,11 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
if mask {
|
||||
c.readMaskPos = 0
|
||||
if err := c.readFull(c.readMaskKey[:]); err != nil {
|
||||
p, err := c.read(len(c.readMaskKey))
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
copy(c.readMaskKey[:], p)
|
||||
}
|
||||
|
||||
// 5. For text and binary messages, enforce read limit and return.
|
||||
@@ -719,9 +866,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
var payload []byte
|
||||
if c.readRemaining > 0 {
|
||||
payload = make([]byte, c.readRemaining)
|
||||
payload, err = c.read(int(c.readRemaining))
|
||||
c.readRemaining = 0
|
||||
if err := c.readFull(payload); err != nil {
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
if c.isServer {
|
||||
@@ -741,15 +888,21 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
return noFrame, err
|
||||
}
|
||||
case CloseMessage:
|
||||
echoMessage := []byte{}
|
||||
closeCode := CloseNoStatusReceived
|
||||
closeText := ""
|
||||
if len(payload) >= 2 {
|
||||
echoMessage = payload[:2]
|
||||
closeCode = int(binary.BigEndian.Uint16(payload))
|
||||
if !isValidReceivedCloseCode(closeCode) {
|
||||
return noFrame, c.handleProtocolError("invalid close code")
|
||||
}
|
||||
closeText = string(payload[2:])
|
||||
if !utf8.ValidString(closeText) {
|
||||
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
|
||||
}
|
||||
}
|
||||
if err := c.handleClose(closeCode, closeText); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
||||
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
||||
}
|
||||
|
||||
@@ -772,8 +925,13 @@ func (c *Conn) handleProtocolError(message string) error {
|
||||
// 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) {
|
||||
// Close previous reader, only relevant for decompression.
|
||||
if c.reader != nil {
|
||||
c.reader.Close()
|
||||
c.reader = nil
|
||||
}
|
||||
|
||||
c.readSeq++
|
||||
c.messageReader = nil
|
||||
c.readLength = 0
|
||||
|
||||
for c.readErr == nil {
|
||||
@@ -783,7 +941,12 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
break
|
||||
}
|
||||
if frameType == TextMessage || frameType == BinaryMessage {
|
||||
return frameType, messageReader{c, c.readSeq}, nil
|
||||
c.messageReader = &messageReader{c}
|
||||
c.reader = c.messageReader
|
||||
if c.readDecompress {
|
||||
c.reader = c.newDecompressionReader(c.reader)
|
||||
}
|
||||
return frameType, c.reader, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,53 +961,57 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
return noFrame, nil, c.readErr
|
||||
}
|
||||
|
||||
type messageReader struct {
|
||||
c *Conn
|
||||
seq int
|
||||
}
|
||||
type messageReader struct{ c *Conn }
|
||||
|
||||
func (r messageReader) Read(b []byte) (int, error) {
|
||||
|
||||
if r.seq != r.c.readSeq {
|
||||
func (r *messageReader) Read(b []byte) (int, error) {
|
||||
c := r.c
|
||||
if c.messageReader != r {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
for r.c.readErr == nil {
|
||||
for c.readErr == nil {
|
||||
|
||||
if r.c.readRemaining > 0 {
|
||||
if int64(len(b)) > r.c.readRemaining {
|
||||
b = b[:r.c.readRemaining]
|
||||
if c.readRemaining > 0 {
|
||||
if int64(len(b)) > c.readRemaining {
|
||||
b = b[: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])
|
||||
n, err := c.br.Read(b)
|
||||
c.readErr = hideTempErr(err)
|
||||
if c.isServer {
|
||||
c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
|
||||
}
|
||||
r.c.readRemaining -= int64(n)
|
||||
return n, r.c.readErr
|
||||
c.readRemaining -= int64(n)
|
||||
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||
c.readErr = errUnexpectedEOF
|
||||
}
|
||||
return n, c.readErr
|
||||
}
|
||||
|
||||
if r.c.readFinal {
|
||||
r.c.readSeq++
|
||||
if c.readFinal {
|
||||
c.messageReader = nil
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
frameType, err := r.c.advanceFrame()
|
||||
frameType, err := c.advanceFrame()
|
||||
switch {
|
||||
case err != nil:
|
||||
r.c.readErr = hideTempErr(err)
|
||||
c.readErr = hideTempErr(err)
|
||||
case frameType == TextMessage || frameType == BinaryMessage:
|
||||
r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||
c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
|
||||
}
|
||||
}
|
||||
|
||||
err := r.c.readErr
|
||||
if err == io.EOF && r.seq == r.c.readSeq {
|
||||
err := c.readErr
|
||||
if err == io.EOF && c.messageReader == r {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (r *messageReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -872,9 +1039,49 @@ func (c *Conn) SetReadLimit(limit int64) {
|
||||
c.readLimit = limit
|
||||
}
|
||||
|
||||
// CloseHandler returns the current close handler
|
||||
func (c *Conn) CloseHandler() func(code int, text string) error {
|
||||
return c.handleClose
|
||||
}
|
||||
|
||||
// SetCloseHandler sets the handler for close messages received from the peer.
|
||||
// The code argument to h is the received close code or CloseNoStatusReceived
|
||||
// if the close message is empty. The default close handler sends a close frame
|
||||
// back to the peer.
|
||||
//
|
||||
// The application must read the connection to process close messages as
|
||||
// described in the section on Control Frames above.
|
||||
//
|
||||
// The connection read methods return a CloseError when a close frame is
|
||||
// received. Most applications should handle close messages as part of their
|
||||
// normal error handling. Applications should only set a close handler when the
|
||||
// application must perform some action before sending a close frame back to
|
||||
// the peer.
|
||||
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||
if h == nil {
|
||||
h = func(code int, text string) error {
|
||||
message := []byte{}
|
||||
if code != CloseNoStatusReceived {
|
||||
message = FormatCloseMessage(code, "")
|
||||
}
|
||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
c.handleClose = h
|
||||
}
|
||||
|
||||
// PingHandler returns the current ping handler
|
||||
func (c *Conn) PingHandler() func(appData string) error {
|
||||
return c.handlePing
|
||||
}
|
||||
|
||||
// SetPingHandler sets the handler for ping messages received from the peer.
|
||||
// The appData argument to h is the PING frame application data. The default
|
||||
// ping handler sends a pong to the peer.
|
||||
//
|
||||
// The application must read the connection to process ping messages as
|
||||
// described in the section on Control Frames above.
|
||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
if h == nil {
|
||||
h = func(message string) error {
|
||||
@@ -890,9 +1097,17 @@ func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
c.handlePing = h
|
||||
}
|
||||
|
||||
// PongHandler returns the current pong handler
|
||||
func (c *Conn) PongHandler() func(appData string) error {
|
||||
return c.handlePong
|
||||
}
|
||||
|
||||
// SetPongHandler sets the handler for pong messages received from the peer.
|
||||
// The appData argument to h is the PONG frame application data. The default
|
||||
// pong handler does nothing.
|
||||
//
|
||||
// The application must read the connection to process ping messages as
|
||||
// described in the section on Control Frames above.
|
||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||
if h == nil {
|
||||
h = func(string) error { return nil }
|
||||
@@ -906,6 +1121,25 @@ func (c *Conn) UnderlyingConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
// EnableWriteCompression enables and disables write compression of
|
||||
// subsequent text and binary messages. This function is a noop if
|
||||
// compression was not negotiated with the peer.
|
||||
func (c *Conn) EnableWriteCompression(enable bool) {
|
||||
c.enableWriteCompression = enable
|
||||
}
|
||||
|
||||
// SetCompressionLevel sets the flate compression level for subsequent text and
|
||||
// binary messages. This function is a noop if compression was not negotiated
|
||||
// with the peer. See the compress/flate package for a description of
|
||||
// compression levels.
|
||||
func (c *Conn) SetCompressionLevel(level int) error {
|
||||
if !isValidCompressionLevel(level) {
|
||||
return errors.New("websocket: invalid compression level")
|
||||
}
|
||||
c.compressionLevel = level
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
|
||||
func FormatCloseMessage(closeCode int, text string) []byte {
|
||||
buf := make([]byte, 2+len(text))
|
||||
|
||||
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
c.br.Discard(len(p))
|
||||
return p, err
|
||||
}
|
||||
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
if len(p) > 0 {
|
||||
// advance over the bytes just read
|
||||
io.ReadFull(c.br, p)
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
34
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@@ -118,9 +118,10 @@
|
||||
//
|
||||
// Applications are responsible for ensuring that no more than one goroutine
|
||||
// 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.
|
||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) 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.
|
||||
@@ -149,4 +150,31 @@
|
||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||
// application's responsibility to check the Origin header before calling
|
||||
// Upgrade.
|
||||
//
|
||||
// Compression EXPERIMENTAL
|
||||
//
|
||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||
// by this package in a limited capacity. Setting the EnableCompression option
|
||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||
// support.
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// EnableCompression: true,
|
||||
// }
|
||||
//
|
||||
// If compression was successfully negotiated with the connection's peer, any
|
||||
// message received in compressed form will be automatically decompressed.
|
||||
// All Read methods will return uncompressed bytes.
|
||||
//
|
||||
// Per message compression of messages written to a connection can be enabled
|
||||
// or disabled by calling the corresponding Conn method:
|
||||
//
|
||||
// conn.EnableWriteCompression(false)
|
||||
//
|
||||
// Currently this package does not support compression with "context takeover".
|
||||
// This means that messages must be compressed and decompressed in isolation,
|
||||
// without retaining sliding window or dictionary state across messages. For
|
||||
// more details refer to RFC 7692.
|
||||
//
|
||||
// Use of compression is experimental and may result in decreased performance.
|
||||
package websocket
|
||||
|
||||
37
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
generated
vendored
37
vendor/github.com/gorilla/websocket/examples/autobahn/server.go
generated
vendored
@@ -8,17 +8,19 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"github.com/gorilla/websocket"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
EnableCompression: true,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
@@ -83,7 +85,7 @@ func echoCopyFull(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// echoReadAll echoes messages from the client by reading the entire message
|
||||
// with ioutil.ReadAll.
|
||||
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
||||
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println("Upgrade:", err)
|
||||
@@ -107,9 +109,21 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
||||
}
|
||||
}
|
||||
if writeMessage {
|
||||
err = conn.WriteMessage(mt, b)
|
||||
if err != nil {
|
||||
log.Println("WriteMessage:", err)
|
||||
if !writePrepared {
|
||||
err = conn.WriteMessage(mt, b)
|
||||
if err != nil {
|
||||
log.Println("WriteMessage:", err)
|
||||
}
|
||||
} else {
|
||||
pm, err := websocket.NewPreparedMessage(mt, b)
|
||||
if err != nil {
|
||||
log.Println("NewPreparedMessage:", err)
|
||||
return
|
||||
}
|
||||
err = conn.WritePreparedMessage(pm)
|
||||
if err != nil {
|
||||
log.Println("WritePreparedMessage:", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w, err := conn.NextWriter(mt)
|
||||
@@ -130,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
|
||||
}
|
||||
|
||||
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) {
|
||||
echoReadAll(w, r, false)
|
||||
echoReadAll(w, r, false, false)
|
||||
}
|
||||
|
||||
func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {
|
||||
echoReadAll(w, r, true)
|
||||
echoReadAll(w, r, true, false)
|
||||
}
|
||||
|
||||
func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) {
|
||||
echoReadAll(w, r, true, true)
|
||||
}
|
||||
|
||||
func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -159,6 +177,7 @@ func main() {
|
||||
http.HandleFunc("/f", echoCopyFull)
|
||||
http.HandleFunc("/r", echoReadAllWriter)
|
||||
http.HandleFunc("/m", echoReadAllWriteMessage)
|
||||
http.HandleFunc("/p", echoReadAllWritePreparedMessage)
|
||||
err := http.ListenAndServe(*addr, nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
|
||||
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
Normal file
134
vendor/github.com/gorilla/websocket/examples/chat/client.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to write a message to the peer.
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
// Time allowed to read the next pong message from the peer.
|
||||
pongWait = 60 * time.Second
|
||||
|
||||
// Send pings to peer with this period. Must be less than pongWait.
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
// Maximum message size allowed from peer.
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
var (
|
||||
newline = []byte{'\n'}
|
||||
space = []byte{' '}
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
// Client is a middleman between the websocket connection and the hub.
|
||||
type Client struct {
|
||||
hub *Hub
|
||||
|
||||
// The websocket connection.
|
||||
conn *websocket.Conn
|
||||
|
||||
// Buffered channel of outbound messages.
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
// readPump pumps messages from the websocket connection to the hub.
|
||||
//
|
||||
// The application runs readPump in a per-connection goroutine. The application
|
||||
// ensures that there is at most one reader on a connection by executing all
|
||||
// reads from this goroutine.
|
||||
func (c *Client) readPump() {
|
||||
defer func() {
|
||||
c.hub.unregister <- c
|
||||
c.conn.Close()
|
||||
}()
|
||||
c.conn.SetReadLimit(maxMessageSize)
|
||||
c.conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Printf("error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
|
||||
c.hub.broadcast <- message
|
||||
}
|
||||
}
|
||||
|
||||
// writePump pumps messages from the hub to the websocket connection.
|
||||
//
|
||||
// A goroutine running writePump is started for each connection. The
|
||||
// application ensures that there is at most one writer to a connection by
|
||||
// executing all writes from this goroutine.
|
||||
func (c *Client) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.conn.Close()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.send:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if !ok {
|
||||
// The hub closed the channel.
|
||||
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
w, err := c.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.Write(message)
|
||||
|
||||
// Add queued chat messages to the current websocket message.
|
||||
n := len(c.send)
|
||||
for i := 0; i < n; i++ {
|
||||
w.Write(newline)
|
||||
w.Write(<-c.send)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serveWs handles websocket requests from the peer.
|
||||
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
||||
client.hub.register <- client
|
||||
go client.writePump()
|
||||
client.readPump()
|
||||
}
|
||||
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
generated
vendored
105
vendor/github.com/gorilla/websocket/examples/chat/conn.go
generated
vendored
@@ -1,105 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to write a message to the peer.
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
// Time allowed to read the next pong message from the peer.
|
||||
pongWait = 60 * time.Second
|
||||
|
||||
// Send pings to peer with this period. Must be less than pongWait.
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
// Maximum message size allowed from peer.
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
// connection is an middleman between the websocket connection and the hub.
|
||||
type connection struct {
|
||||
// The websocket connection.
|
||||
ws *websocket.Conn
|
||||
|
||||
// Buffered channel of outbound messages.
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
// readPump pumps messages from the websocket connection to the hub.
|
||||
func (c *connection) readPump() {
|
||||
defer func() {
|
||||
h.unregister <- c
|
||||
c.ws.Close()
|
||||
}()
|
||||
c.ws.SetReadLimit(maxMessageSize)
|
||||
c.ws.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.ws.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Printf("error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
h.broadcast <- message
|
||||
}
|
||||
}
|
||||
|
||||
// write writes a message with the given message type and payload.
|
||||
func (c *connection) write(mt int, payload []byte) error {
|
||||
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
return c.ws.WriteMessage(mt, payload)
|
||||
}
|
||||
|
||||
// writePump pumps messages from the hub to the websocket connection.
|
||||
func (c *connection) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.ws.Close()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.send:
|
||||
if !ok {
|
||||
c.write(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
if err := c.write(websocket.TextMessage, message); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serveWs handles websocket requests from the peer.
|
||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
c := &connection{send: make(chan []byte, 256), ws: ws}
|
||||
h.register <- c
|
||||
go c.writePump()
|
||||
c.readPump()
|
||||
}
|
||||
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
generated
vendored
56
vendor/github.com/gorilla/websocket/examples/chat/hub.go
generated
vendored
@@ -4,46 +4,48 @@
|
||||
|
||||
package main
|
||||
|
||||
// hub maintains the set of active connections and broadcasts messages to the
|
||||
// connections.
|
||||
type hub struct {
|
||||
// Registered connections.
|
||||
connections map[*connection]bool
|
||||
// hub maintains the set of active clients and broadcasts messages to the
|
||||
// clients.
|
||||
type Hub struct {
|
||||
// Registered clients.
|
||||
clients map[*Client]bool
|
||||
|
||||
// Inbound messages from the connections.
|
||||
// Inbound messages from the clients.
|
||||
broadcast chan []byte
|
||||
|
||||
// Register requests from the connections.
|
||||
register chan *connection
|
||||
// Register requests from the clients.
|
||||
register chan *Client
|
||||
|
||||
// Unregister requests from connections.
|
||||
unregister chan *connection
|
||||
// Unregister requests from clients.
|
||||
unregister chan *Client
|
||||
}
|
||||
|
||||
var h = hub{
|
||||
broadcast: make(chan []byte),
|
||||
register: make(chan *connection),
|
||||
unregister: make(chan *connection),
|
||||
connections: make(map[*connection]bool),
|
||||
func newHub() *Hub {
|
||||
return &Hub{
|
||||
broadcast: make(chan []byte),
|
||||
register: make(chan *Client),
|
||||
unregister: make(chan *Client),
|
||||
clients: make(map[*Client]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hub) run() {
|
||||
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 client := <-h.register:
|
||||
h.clients[client] = true
|
||||
case client := <-h.unregister:
|
||||
if _, ok := h.clients[client]; ok {
|
||||
delete(h.clients, client)
|
||||
close(client.send)
|
||||
}
|
||||
case m := <-h.broadcast:
|
||||
for c := range h.connections {
|
||||
case message := <-h.broadcast:
|
||||
for client := range h.clients {
|
||||
select {
|
||||
case c.send <- m:
|
||||
case client.send <- message:
|
||||
default:
|
||||
close(c.send)
|
||||
delete(h.connections, c)
|
||||
close(client.send)
|
||||
delete(h.clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
vendor/github.com/gorilla/websocket/examples/chat/main.go
generated
vendored
13
vendor/github.com/gorilla/websocket/examples/chat/main.go
generated
vendored
@@ -8,13 +8,12 @@ import (
|
||||
"flag"
|
||||
"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) {
|
||||
log.Println(r.URL)
|
||||
if r.URL.Path != "/" {
|
||||
http.Error(w, "Not found", 404)
|
||||
return
|
||||
@@ -23,15 +22,17 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
homeTempl.Execute(w, r.Host)
|
||||
http.ServeFile(w, r, "home.html")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
go h.run()
|
||||
hub := newHub()
|
||||
go hub.run()
|
||||
http.HandleFunc("/", serveHome)
|
||||
http.HandleFunc("/ws", serveWs)
|
||||
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveWs(hub, w, r)
|
||||
})
|
||||
err := http.ListenAndServe(*addr, nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
|
||||
21
vendor/github.com/gorilla/websocket/examples/command/main.go
generated
vendored
21
vendor/github.com/gorilla/websocket/examples/command/main.go
generated
vendored
@@ -12,16 +12,14 @@ import (
|
||||
"net/http"
|
||||
"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"))
|
||||
addr = flag.String("addr", "127.0.0.1:8080", "http service address")
|
||||
cmdPath string
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,6 +34,9 @@ const (
|
||||
|
||||
// Send pings to peer with this period. Must be less than pongWait.
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
|
||||
// Time to wait before force close on connection.
|
||||
closeGracePeriod = 10 * time.Second
|
||||
)
|
||||
|
||||
func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
||||
@@ -57,19 +58,24 @@ func pumpStdin(ws *websocket.Conn, w io.Writer) {
|
||||
|
||||
func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {
|
||||
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 {
|
||||
ws.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.Err() != nil {
|
||||
log.Println("scan:", s.Err())
|
||||
}
|
||||
close(done)
|
||||
|
||||
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
time.Sleep(closeGracePeriod)
|
||||
ws.Close()
|
||||
}
|
||||
|
||||
func ping(ws *websocket.Conn, done chan struct{}) {
|
||||
@@ -168,8 +174,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
homeTempl.Execute(w, r.Host)
|
||||
http.ServeFile(w, r, "home.html")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
4
vendor/github.com/gorilla/websocket/examples/filewatch/main.go
generated
vendored
4
vendor/github.com/gorilla/websocket/examples/filewatch/main.go
generated
vendored
@@ -6,12 +6,12 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -120,7 +120,7 @@ func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var lastMod time.Time
|
||||
if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil {
|
||||
if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil {
|
||||
lastMod = time.Unix(0, n)
|
||||
}
|
||||
|
||||
|
||||
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package websocket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
|
||||
// Create aligned word size key.
|
||||
var k [wordSize]byte
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
// Mask one byte at a time for remaining bytes.
|
||||
b = b[n:]
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
|
||||
return pos & 3
|
||||
}
|
||||
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package websocket
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PreparedMessage caches on the wire representations of a message payload.
|
||||
// Use PreparedMessage to efficiently send a message payload to multiple
|
||||
// connections. PreparedMessage is especially useful when compression is used
|
||||
// because the CPU and memory expensive compression operation can be executed
|
||||
// once for a given set of compression options.
|
||||
type PreparedMessage struct {
|
||||
messageType int
|
||||
data []byte
|
||||
err error
|
||||
mu sync.Mutex
|
||||
frames map[prepareKey]*preparedFrame
|
||||
}
|
||||
|
||||
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
|
||||
type prepareKey struct {
|
||||
isServer bool
|
||||
compress bool
|
||||
compressionLevel int
|
||||
}
|
||||
|
||||
// preparedFrame contains data in wire representation.
|
||||
type preparedFrame struct {
|
||||
once sync.Once
|
||||
data []byte
|
||||
}
|
||||
|
||||
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
|
||||
// it to connection using WritePreparedMessage method. Valid wire
|
||||
// representation will be calculated lazily only once for a set of current
|
||||
// connection options.
|
||||
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
|
||||
pm := &PreparedMessage{
|
||||
messageType: messageType,
|
||||
frames: make(map[prepareKey]*preparedFrame),
|
||||
data: data,
|
||||
}
|
||||
|
||||
// Prepare a plain server frame.
|
||||
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// To protect against caller modifying the data argument, remember the data
|
||||
// copied to the plain server frame.
|
||||
pm.data = frameData[len(frameData)-len(data):]
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
|
||||
pm.mu.Lock()
|
||||
frame, ok := pm.frames[key]
|
||||
if !ok {
|
||||
frame = &preparedFrame{}
|
||||
pm.frames[key] = frame
|
||||
}
|
||||
pm.mu.Unlock()
|
||||
|
||||
var err error
|
||||
frame.once.Do(func() {
|
||||
// Prepare a frame using a 'fake' connection.
|
||||
// TODO: Refactor code in conn.go to allow more direct construction of
|
||||
// the frame.
|
||||
mu := make(chan bool, 1)
|
||||
mu <- true
|
||||
var nc prepareConn
|
||||
c := &Conn{
|
||||
conn: &nc,
|
||||
mu: mu,
|
||||
isServer: key.isServer,
|
||||
compressionLevel: key.compressionLevel,
|
||||
enableWriteCompression: true,
|
||||
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
|
||||
}
|
||||
if key.compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
}
|
||||
err = c.WriteMessage(pm.messageType, pm.data)
|
||||
frame.data = nc.buf.Bytes()
|
||||
})
|
||||
return pm.messageType, frame.data, err
|
||||
}
|
||||
|
||||
type prepareConn struct {
|
||||
buf bytes.Buffer
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
|
||||
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
61
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@@ -28,8 +28,9 @@ type Upgrader struct {
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// 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.
|
||||
// size is zero, then buffers allocated by the HTTP server are 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
|
||||
@@ -46,6 +47,12 @@ type Upgrader struct {
|
||||
// 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
|
||||
|
||||
// EnableCompression specify if the server should attempt to negotiate per
|
||||
// message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
}
|
||||
|
||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||
@@ -53,6 +60,7 @@ func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status in
|
||||
if u.Error != nil {
|
||||
u.Error(w, r, status, err)
|
||||
} else {
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
return nil, err
|
||||
@@ -97,18 +105,23 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
||||
// 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")
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||
}
|
||||
if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||
}
|
||||
|
||||
checkOrigin := u.CheckOrigin
|
||||
@@ -116,19 +129,30 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
checkOrigin = checkSameOrigin
|
||||
}
|
||||
if !checkOrigin(r) {
|
||||
return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
|
||||
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
// Negotiate PMCE
|
||||
var compress bool
|
||||
if u.EnableCompression {
|
||||
for _, ext := range parseExtensions(r.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
compress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
netConn net.Conn
|
||||
br *bufio.Reader
|
||||
err error
|
||||
)
|
||||
|
||||
@@ -136,21 +160,25 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
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()
|
||||
var brw *bufio.ReadWriter
|
||||
netConn, brw, err = h.Hijack()
|
||||
if err != nil {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
br = rw.Reader
|
||||
|
||||
if br.Buffered() > 0 {
|
||||
if brw.Reader.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 := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
|
||||
c.subprotocol = subprotocol
|
||||
|
||||
if compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
c.newDecompressionReader = decompressNoContextTakeover
|
||||
}
|
||||
|
||||
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)...)
|
||||
@@ -160,6 +188,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
p = append(p, c.subprotocol...)
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
if compress {
|
||||
p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||
}
|
||||
for k, vs := range responseHeader {
|
||||
if k == "Sec-Websocket-Protocol" {
|
||||
continue
|
||||
|
||||
196
vendor/github.com/gorilla/websocket/util.go
generated
vendored
196
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@@ -13,19 +13,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
@@ -42,3 +29,186 @@ func generateChallengeKey() (string, error) {
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(p), nil
|
||||
}
|
||||
|
||||
// Octet types from RFC 2616.
|
||||
var octetTypes [256]byte
|
||||
|
||||
const (
|
||||
isTokenOctet = 1 << iota
|
||||
isSpaceOctet
|
||||
)
|
||||
|
||||
func init() {
|
||||
// From RFC 2616
|
||||
//
|
||||
// OCTET = <any 8-bit sequence of data>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
// CR = <US-ASCII CR, carriage return (13)>
|
||||
// LF = <US-ASCII LF, linefeed (10)>
|
||||
// SP = <US-ASCII SP, space (32)>
|
||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
// <"> = <US-ASCII double-quote mark (34)>
|
||||
// CRLF = CR LF
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||
// token = 1*<any CHAR except CTLs or separators>
|
||||
// qdtext = <any TEXT except <">>
|
||||
|
||||
for c := 0; c < 256; c++ {
|
||||
var t byte
|
||||
isCtl := c <= 31 || c == 127
|
||||
isChar := 0 <= c && c <= 127
|
||||
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||
t |= isSpaceOctet
|
||||
}
|
||||
if isChar && !isCtl && !isSeparator {
|
||||
t |= isTokenOctet
|
||||
}
|
||||
octetTypes[c] = t
|
||||
}
|
||||
}
|
||||
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isSpaceOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
func nextToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isTokenOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return nextToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + 1; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j += 1
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains token.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if strings.EqualFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensiosn parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
|
||||
// From RFC 6455:
|
||||
//
|
||||
// Sec-WebSocket-Extensions = extension-list
|
||||
// extension-list = 1#extension
|
||||
// extension = extension-token *( ";" extension-param )
|
||||
// extension-token = registered-token
|
||||
// registered-token = token
|
||||
// extension-param = token [ "=" (token | quoted-string) ]
|
||||
// ;When using the quoted-string syntax variant, the value
|
||||
// ;after quoted-string unescaping MUST conform to the
|
||||
// ;'token' ABNF.
|
||||
|
||||
var result []map[string]string
|
||||
headers:
|
||||
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
ext := map[string]string{"": t}
|
||||
for {
|
||||
s = skipSpace(s)
|
||||
if !strings.HasPrefix(s, ";") {
|
||||
break
|
||||
}
|
||||
var k string
|
||||
k, s = nextToken(skipSpace(s[1:]))
|
||||
if k == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
var v string
|
||||
if strings.HasPrefix(s, "=") {
|
||||
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||
s = skipSpace(s)
|
||||
}
|
||||
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||
continue headers
|
||||
}
|
||||
ext[k] = v
|
||||
}
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
result = append(result, ext)
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
21
vendor/github.com/jpillora/backoff/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jpillora/backoff/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Jaime Pillora
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
83
vendor/github.com/jpillora/backoff/backoff.go
generated
vendored
83
vendor/github.com/jpillora/backoff/backoff.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
// Package backoff provides an exponential-backoff implementation.
|
||||
package backoff
|
||||
|
||||
import (
|
||||
@@ -6,64 +7,82 @@ import (
|
||||
"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 a time.Duration counter, starting at Min. After every call to
|
||||
// the Duration method the current timing is multiplied by Factor, but it
|
||||
// never exceeds Max.
|
||||
//
|
||||
// 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.
|
||||
// Backoff is not generally concurrent-safe, but the ForAttempt method can
|
||||
// be used concurrently.
|
||||
type Backoff struct {
|
||||
//Factor is the multiplying factor for each increment step
|
||||
attempts, Factor float64
|
||||
attempt, 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
|
||||
// Duration returns the duration for the current attempt before incrementing
|
||||
// the attempt counter. See ForAttempt.
|
||||
func (b *Backoff) Duration() time.Duration {
|
||||
d := b.ForAttempt(b.attempts)
|
||||
b.attempts++
|
||||
d := b.ForAttempt(b.attempt)
|
||||
b.attempt++
|
||||
return d
|
||||
}
|
||||
|
||||
const maxInt64 = float64(math.MaxInt64 - 512)
|
||||
|
||||
// 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.
|
||||
// ForAttempt is concurrent-safe.
|
||||
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
|
||||
// Zero-values are nonsensical, so we use
|
||||
// them to apply defaults
|
||||
min := b.Min
|
||||
if min <= 0 {
|
||||
min = 100 * time.Millisecond
|
||||
}
|
||||
if b.Max == 0 {
|
||||
b.Max = 10 * time.Second
|
||||
max := b.Max
|
||||
if max <= 0 {
|
||||
max = 10 * time.Second
|
||||
}
|
||||
if b.Factor == 0 {
|
||||
b.Factor = 2
|
||||
if min >= max {
|
||||
// short-circuit
|
||||
return max
|
||||
}
|
||||
factor := b.Factor
|
||||
if factor <= 0 {
|
||||
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)
|
||||
minf := float64(min)
|
||||
durf := minf * math.Pow(factor, attempt)
|
||||
if b.Jitter {
|
||||
durf = rand.Float64()*(durf-minf) + minf
|
||||
}
|
||||
//cap!
|
||||
if dur > float64(b.Max) {
|
||||
return b.Max
|
||||
//ensure float64 wont overflow int64
|
||||
if durf > maxInt64 {
|
||||
return max
|
||||
}
|
||||
//return as a time.Duration
|
||||
return time.Duration(dur)
|
||||
dur := time.Duration(durf)
|
||||
//keep within bounds
|
||||
if dur < min {
|
||||
return min
|
||||
} else if dur > max {
|
||||
return max
|
||||
}
|
||||
return dur
|
||||
}
|
||||
|
||||
//Resets the current value of the counter back to Min
|
||||
// Reset restarts the current attempt counter at zero.
|
||||
func (b *Backoff) Reset() {
|
||||
b.attempts = 0
|
||||
b.attempt = 0
|
||||
}
|
||||
|
||||
// Attempt returns the current attempt counter value.
|
||||
func (b *Backoff) Attempt() float64 {
|
||||
return b.attempt
|
||||
}
|
||||
|
||||
7
vendor/github.com/mattermost/platform/einterfaces/cluster.go
generated
vendored
7
vendor/github.com/mattermost/platform/einterfaces/cluster.go
generated
vendored
@@ -12,10 +12,15 @@ type ClusterInterface interface {
|
||||
StopInterNodeCommunication()
|
||||
GetClusterInfos() []*model.ClusterInfo
|
||||
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
|
||||
RemoveAllSessionsForUserId(userId string)
|
||||
ClearSessionCacheForUser(userId string)
|
||||
InvalidateCacheForUser(userId string)
|
||||
InvalidateCacheForChannel(channelId string)
|
||||
InvalidateCacheForChannelByName(teamId, name string)
|
||||
InvalidateCacheForChannelMembers(channelId string)
|
||||
InvalidateCacheForChannelMembersNotifyProps(channelId string)
|
||||
InvalidateCacheForChannelPosts(channelId string)
|
||||
InvalidateCacheForWebhook(webhookId string)
|
||||
InvalidateCacheForReactions(postId string)
|
||||
Publish(event *model.WebSocketEvent)
|
||||
UpdateStatus(status *model.Status)
|
||||
GetLogs() ([]string, *model.AppError)
|
||||
|
||||
8
vendor/github.com/mattermost/platform/einterfaces/metrics.go
generated
vendored
8
vendor/github.com/mattermost/platform/einterfaces/metrics.go
generated
vendored
@@ -8,6 +8,7 @@ type MetricsInterface interface {
|
||||
StopServer()
|
||||
|
||||
IncrementPostCreate()
|
||||
IncrementWebhookPost()
|
||||
IncrementPostSentEmail()
|
||||
IncrementPostSentPush()
|
||||
IncrementPostBroadcast()
|
||||
@@ -17,6 +18,9 @@ type MetricsInterface interface {
|
||||
IncrementHttpError()
|
||||
ObserveHttpRequestDuration(elapsed float64)
|
||||
|
||||
IncrementClusterRequest()
|
||||
ObserveClusterRequestDuration(elapsed float64)
|
||||
|
||||
IncrementLogin()
|
||||
IncrementLoginFail()
|
||||
|
||||
@@ -25,6 +29,10 @@ type MetricsInterface interface {
|
||||
|
||||
IncrementMemCacheHitCounter(cacheName string)
|
||||
IncrementMemCacheMissCounter(cacheName string)
|
||||
IncrementMemCacheMissCounterSession()
|
||||
IncrementMemCacheHitCounterSession()
|
||||
|
||||
IncrementWebsocketEvent(eventType string)
|
||||
|
||||
AddMemCacheHitCounter(cacheName string, amount float64)
|
||||
AddMemCacheMissCounter(cacheName string, amount float64)
|
||||
|
||||
2
vendor/github.com/mattermost/platform/einterfaces/saml.go
generated
vendored
2
vendor/github.com/mattermost/platform/einterfaces/saml.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
||||
type SamlInterface interface {
|
||||
ConfigureSP() *model.AppError
|
||||
BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError)
|
||||
DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError)
|
||||
DoLogin(encodedXML string, relayState map[string]string, siteURL string) (*model.User, *model.AppError)
|
||||
GetMetadata() (string, *model.AppError)
|
||||
}
|
||||
|
||||
|
||||
55
vendor/github.com/mattermost/platform/model/authorization.go
generated
vendored
55
vendor/github.com/mattermost/platform/model/authorization.go
generated
vendored
@@ -27,7 +27,10 @@ var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission
|
||||
var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission
|
||||
var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission
|
||||
var PERMISSION_MANAGE_ROLES *Permission
|
||||
var PERMISSION_MANAGE_TEAM_ROLES *Permission
|
||||
var PERMISSION_MANAGE_CHANNEL_ROLES *Permission
|
||||
var PERMISSION_CREATE_DIRECT_CHANNEL *Permission
|
||||
var PERMISSION_CREATE_GROUP_CHANNEL *Permission
|
||||
var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission
|
||||
var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission
|
||||
var PERMISSION_LIST_TEAM_CHANNELS *Permission
|
||||
@@ -46,9 +49,13 @@ var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
|
||||
var PERMISSION_CREATE_POST *Permission
|
||||
var PERMISSION_EDIT_POST *Permission
|
||||
var PERMISSION_EDIT_OTHERS_POSTS *Permission
|
||||
var PERMISSION_DELETE_POST *Permission
|
||||
var PERMISSION_DELETE_OTHERS_POSTS *Permission
|
||||
var PERMISSION_REMOVE_USER_FROM_TEAM *Permission
|
||||
var PERMISSION_CREATE_TEAM *Permission
|
||||
var PERMISSION_MANAGE_TEAM *Permission
|
||||
var PERMISSION_IMPORT_TEAM *Permission
|
||||
var PERMISSION_VIEW_TEAM *Permission
|
||||
|
||||
// General permission that encompases all system admin functions
|
||||
// in the future this could be broken up to allow access to some
|
||||
@@ -123,6 +130,16 @@ func InitalizePermissions() {
|
||||
"authentication.permissions.manage_roles.name",
|
||||
"authentication.permissions.manage_roles.description",
|
||||
}
|
||||
PERMISSION_MANAGE_TEAM_ROLES = &Permission{
|
||||
"manage_team_roles",
|
||||
"authentication.permissions.manage_team_roles.name",
|
||||
"authentication.permissions.manage_team_roles.description",
|
||||
}
|
||||
PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{
|
||||
"manage_channel_roles",
|
||||
"authentication.permissions.manage_channel_roles.name",
|
||||
"authentication.permissions.manage_channel_roles.description",
|
||||
}
|
||||
PERMISSION_MANAGE_SYSTEM = &Permission{
|
||||
"manage_system",
|
||||
"authentication.permissions.manage_system.name",
|
||||
@@ -133,6 +150,11 @@ func InitalizePermissions() {
|
||||
"authentication.permissions.create_direct_channel.name",
|
||||
"authentication.permissions.create_direct_channel.description",
|
||||
}
|
||||
PERMISSION_CREATE_GROUP_CHANNEL = &Permission{
|
||||
"create_group_channel",
|
||||
"authentication.permissions.create_group_channel.name",
|
||||
"authentication.permissions.create_group_channel.description",
|
||||
}
|
||||
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
|
||||
"manage__publicchannel_properties",
|
||||
"authentication.permissions.manage_public_channel_properties.name",
|
||||
@@ -223,11 +245,26 @@ func InitalizePermissions() {
|
||||
"authentication.permissions.edit_others_posts.name",
|
||||
"authentication.permissions.edit_others_posts.description",
|
||||
}
|
||||
PERMISSION_DELETE_POST = &Permission{
|
||||
"delete_post",
|
||||
"authentication.permissions.delete_post.name",
|
||||
"authentication.permissions.delete_post.description",
|
||||
}
|
||||
PERMISSION_DELETE_OTHERS_POSTS = &Permission{
|
||||
"delete_others_posts",
|
||||
"authentication.permissions.delete_others_posts.name",
|
||||
"authentication.permissions.delete_others_posts.description",
|
||||
}
|
||||
PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
|
||||
"remove_user_from_team",
|
||||
"authentication.permissions.remove_user_from_team.name",
|
||||
"authentication.permissions.remove_user_from_team.description",
|
||||
}
|
||||
PERMISSION_CREATE_TEAM = &Permission{
|
||||
"create_team",
|
||||
"authentication.permissions.create_team.name",
|
||||
"authentication.permissions.create_team.description",
|
||||
}
|
||||
PERMISSION_MANAGE_TEAM = &Permission{
|
||||
"manage_team",
|
||||
"authentication.permissions.manage_team.name",
|
||||
@@ -238,6 +275,11 @@ func InitalizePermissions() {
|
||||
"authentication.permissions.import_team.name",
|
||||
"authentication.permissions.import_team.description",
|
||||
}
|
||||
PERMISSION_VIEW_TEAM = &Permission{
|
||||
"view_team",
|
||||
"authentication.permissions.view_team.name",
|
||||
"authentication.permissions.view_team.description",
|
||||
}
|
||||
}
|
||||
|
||||
func InitalizeRoles() {
|
||||
@@ -264,7 +306,9 @@ func InitalizeRoles() {
|
||||
"channel_admin",
|
||||
"authentication.roles.channel_admin.name",
|
||||
"authentication.roles.channel_admin.description",
|
||||
[]string{},
|
||||
[]string{
|
||||
PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||
},
|
||||
}
|
||||
BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN
|
||||
ROLE_CHANNEL_GUEST = &Role{
|
||||
@@ -282,6 +326,7 @@ func InitalizeRoles() {
|
||||
[]string{
|
||||
PERMISSION_LIST_TEAM_CHANNELS.Id,
|
||||
PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
|
||||
PERMISSION_VIEW_TEAM.Id,
|
||||
},
|
||||
}
|
||||
BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER
|
||||
@@ -295,7 +340,8 @@ func InitalizeRoles() {
|
||||
PERMISSION_REMOVE_USER_FROM_TEAM.Id,
|
||||
PERMISSION_MANAGE_TEAM.Id,
|
||||
PERMISSION_IMPORT_TEAM.Id,
|
||||
PERMISSION_MANAGE_ROLES.Id,
|
||||
PERMISSION_MANAGE_TEAM_ROLES.Id,
|
||||
PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||
PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
|
||||
PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
||||
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
|
||||
@@ -310,6 +356,7 @@ func InitalizeRoles() {
|
||||
"authentication.roles.global_user.description",
|
||||
[]string{
|
||||
PERMISSION_CREATE_DIRECT_CHANNEL.Id,
|
||||
PERMISSION_CREATE_GROUP_CHANNEL.Id,
|
||||
PERMISSION_PERMANENT_DELETE_USER.Id,
|
||||
PERMISSION_MANAGE_OAUTH.Id,
|
||||
},
|
||||
@@ -329,6 +376,7 @@ func InitalizeRoles() {
|
||||
[]string{
|
||||
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
|
||||
PERMISSION_MANAGE_SYSTEM.Id,
|
||||
PERMISSION_MANAGE_ROLES.Id,
|
||||
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
|
||||
PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
|
||||
PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
|
||||
@@ -340,6 +388,9 @@ func InitalizeRoles() {
|
||||
PERMISSION_EDIT_OTHER_USERS.Id,
|
||||
PERMISSION_MANAGE_OAUTH.Id,
|
||||
PERMISSION_INVITE_USER.Id,
|
||||
PERMISSION_DELETE_POST.Id,
|
||||
PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
PERMISSION_CREATE_TEAM.Id,
|
||||
},
|
||||
ROLE_TEAM_USER.Permissions...,
|
||||
),
|
||||
|
||||
47
vendor/github.com/mattermost/platform/model/channel.go
generated
vendored
47
vendor/github.com/mattermost/platform/model/channel.go
generated
vendored
@@ -4,8 +4,12 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
@@ -13,11 +17,16 @@ const (
|
||||
CHANNEL_OPEN = "O"
|
||||
CHANNEL_PRIVATE = "P"
|
||||
CHANNEL_DIRECT = "D"
|
||||
CHANNEL_GROUP = "G"
|
||||
CHANNEL_GROUP_MAX_USERS = 8
|
||||
CHANNEL_GROUP_MIN_USERS = 3
|
||||
DEFAULT_CHANNEL = "town-square"
|
||||
CHANNEL_DISPLAY_NAME_MAX_RUNES = 64
|
||||
CHANNEL_NAME_MIN_LENGTH = 2
|
||||
CHANNEL_NAME_MAX_LENGTH = 64
|
||||
CHANNEL_HEADER_MAX_RUNES = 1024
|
||||
CHANNEL_PURPOSE_MAX_RUNES = 250
|
||||
CHANNEL_CACHE_SIZE = 25000
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
@@ -83,15 +92,11 @@ func (o *Channel) IsValid() *AppError {
|
||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Name) > CHANNEL_NAME_MAX_LENGTH {
|
||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id)
|
||||
}
|
||||
|
||||
if !IsValidChannelIdentifier(o.Name) {
|
||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id)
|
||||
}
|
||||
|
||||
if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) {
|
||||
if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP) {
|
||||
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id)
|
||||
}
|
||||
|
||||
@@ -128,6 +133,10 @@ func (o *Channel) ExtraUpdated() {
|
||||
o.ExtraUpdateAt = GetMillis()
|
||||
}
|
||||
|
||||
func (o *Channel) IsGroupOrDirect() bool {
|
||||
return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP
|
||||
}
|
||||
|
||||
func GetDMNameFromIds(userId1, userId2 string) string {
|
||||
if userId1 > userId2 {
|
||||
return userId2 + "__" + userId1
|
||||
@@ -135,3 +144,31 @@ func GetDMNameFromIds(userId1, userId2 string) string {
|
||||
return userId1 + "__" + userId2
|
||||
}
|
||||
}
|
||||
|
||||
func GetGroupDisplayNameFromUsers(users []*User, truncate bool) string {
|
||||
usernames := make([]string, len(users))
|
||||
for index, user := range users {
|
||||
usernames[index] = user.Username
|
||||
}
|
||||
|
||||
sort.Strings(usernames)
|
||||
|
||||
name := strings.Join(usernames, ", ")
|
||||
|
||||
if truncate && len(name) > CHANNEL_NAME_MAX_LENGTH {
|
||||
name = name[:CHANNEL_NAME_MAX_LENGTH]
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func GetGroupNameFromUserIds(userIds []string) string {
|
||||
sort.Strings(userIds)
|
||||
|
||||
h := sha1.New()
|
||||
for _, id := range userIds {
|
||||
io.WriteString(h, id)
|
||||
}
|
||||
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
28
vendor/github.com/mattermost/platform/model/channel_member.go
generated
vendored
28
vendor/github.com/mattermost/platform/model/channel_member.go
generated
vendored
@@ -88,18 +88,32 @@ func (o *ChannelMember) IsValid() *AppError {
|
||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "")
|
||||
}
|
||||
|
||||
notifyLevel := o.NotifyProps["desktop"]
|
||||
notifyLevel := o.NotifyProps[DESKTOP_NOTIFY_PROP]
|
||||
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"]
|
||||
markUnreadLevel := o.NotifyProps[MARK_UNREAD_NOTIFY_PROP]
|
||||
if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) {
|
||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error",
|
||||
nil, "mark_unread_level="+markUnreadLevel)
|
||||
}
|
||||
|
||||
if pushLevel, ok := o.NotifyProps[PUSH_NOTIFY_PROP]; ok {
|
||||
if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) {
|
||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error",
|
||||
nil, "push_notification_level="+pushLevel)
|
||||
}
|
||||
}
|
||||
|
||||
if sendEmail, ok := o.NotifyProps[EMAIL_NOTIFY_PROP]; ok {
|
||||
if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) {
|
||||
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error",
|
||||
nil, "push_notification_level="+sendEmail)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -126,9 +140,15 @@ func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool {
|
||||
return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION
|
||||
}
|
||||
|
||||
func IsSendEmailValid(sendEmail string) bool {
|
||||
return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false"
|
||||
}
|
||||
|
||||
func GetDefaultChannelNotifyProps() StringMap {
|
||||
return StringMap{
|
||||
"desktop": CHANNEL_NOTIFY_DEFAULT,
|
||||
"mark_unread": CHANNEL_MARK_UNREAD_ALL,
|
||||
DESKTOP_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||
MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL,
|
||||
PUSH_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||
EMAIL_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
|
||||
}
|
||||
}
|
||||
|
||||
76
vendor/github.com/mattermost/platform/model/client.go
generated
vendored
76
vendor/github.com/mattermost/platform/model/client.go
generated
vendored
@@ -35,12 +35,14 @@ const (
|
||||
STATUS = "status"
|
||||
STATUS_OK = "OK"
|
||||
STATUS_FAIL = "FAIL"
|
||||
STATUS_REMOVE = "REMOVE"
|
||||
|
||||
CLIENT_DIR = "webapp/dist"
|
||||
|
||||
API_URL_SUFFIX_V1 = "/api/v1"
|
||||
API_URL_SUFFIX_V3 = "/api/v3"
|
||||
API_URL_SUFFIX = API_URL_SUFFIX_V3
|
||||
API_URL_SUFFIX_V4 = "/api/v4"
|
||||
API_URL_SUFFIX = API_URL_SUFFIX_V4
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
@@ -71,7 +73,7 @@ type Client struct {
|
||||
// NewClient constructs a new client with convienence methods for talking to
|
||||
// the server.
|
||||
func NewClient(url string) *Client {
|
||||
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", "", "", "", "", ""}
|
||||
return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""}
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
@@ -782,7 +784,7 @@ func (c *Client) GetSessions(id string) (*Result, *AppError) {
|
||||
}
|
||||
|
||||
func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/users/claim/email_to_sso", MapToJson(m)); err != nil {
|
||||
if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
@@ -1119,6 +1121,16 @@ func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil {
|
||||
return nil, err
|
||||
@@ -1471,6 +1483,21 @@ func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMe
|
||||
}
|
||||
}
|
||||
|
||||
// GetPermalink returns a post list, based on the provided channel and post ID.
|
||||
func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) {
|
||||
if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil {
|
||||
return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return PostListFromJson(r.Body),
|
||||
&ResponseMetadata{
|
||||
StatusCode: r.StatusCode,
|
||||
RequestId: r.Header.Get(HEADER_REQUEST_ID),
|
||||
Etag: r.Header.Get(HEADER_ETAG_SERVER),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
|
||||
return nil, err
|
||||
@@ -1991,6 +2018,16 @@ func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppErro
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) {
|
||||
if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil {
|
||||
return nil, err
|
||||
@@ -2082,6 +2119,16 @@ func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppErro
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) {
|
||||
data := make(map[string]string)
|
||||
data["id"] = id
|
||||
@@ -2319,3 +2366,26 @@ func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *A
|
||||
return ReactionsFromJson(r.Body), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the user's roles in the channel by replacing them with the roles provided.
|
||||
func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) {
|
||||
data := make(map[string]string)
|
||||
data["new_roles"] = roles
|
||||
data["user_id"] = userId
|
||||
|
||||
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil {
|
||||
metadata := ResponseMetadata{Error: err}
|
||||
if r != nil {
|
||||
metadata.StatusCode = r.StatusCode
|
||||
}
|
||||
return nil, &metadata
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return MapFromJson(r.Body),
|
||||
&ResponseMetadata{
|
||||
StatusCode: r.StatusCode,
|
||||
RequestId: r.Header.Get(HEADER_REQUEST_ID),
|
||||
Etag: r.Header.Get(HEADER_ETAG_SERVER),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1006
vendor/github.com/mattermost/platform/model/client4.go
generated
vendored
Normal file
1006
vendor/github.com/mattermost/platform/model/client4.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
30
vendor/github.com/mattermost/platform/model/command_response.go
generated
vendored
30
vendor/github.com/mattermost/platform/model/command_response.go
generated
vendored
@@ -5,6 +5,7 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
@@ -14,12 +15,12 @@ const (
|
||||
)
|
||||
|
||||
type CommandResponse struct {
|
||||
ResponseType string `json:"response_type"`
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
GotoLocation string `json:"goto_location"`
|
||||
Attachments interface{} `json:"attachments"`
|
||||
ResponseType string `json:"response_type"`
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
GotoLocation string `json:"goto_location"`
|
||||
Attachments []*SlackAttachment `json:"attachments"`
|
||||
}
|
||||
|
||||
func (o *CommandResponse) ToJson() string {
|
||||
@@ -34,10 +35,19 @@ func (o *CommandResponse) ToJson() string {
|
||||
func CommandResponseFromJson(data io.Reader) *CommandResponse {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o CommandResponse
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
|
||||
if err := decoder.Decode(&o); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure attachment fields are stored as strings
|
||||
for _, attachment := range o.Attachments {
|
||||
for _, field := range attachment.Fields {
|
||||
if field.Value != nil {
|
||||
field.Value = fmt.Sprintf("%v", field.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &o
|
||||
}
|
||||
|
||||
245
vendor/github.com/mattermost/platform/model/config.go
generated
vendored
245
vendor/github.com/mattermost/platform/model/config.go
generated
vendored
@@ -49,49 +49,109 @@ const (
|
||||
RESTRICT_EMOJI_CREATION_ADMIN = "admin"
|
||||
RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
|
||||
|
||||
PERMISSIONS_DELETE_POST_ALL = "all"
|
||||
PERMISSIONS_DELETE_POST_TEAM_ADMIN = "team_admin"
|
||||
PERMISSIONS_DELETE_POST_SYSTEM_ADMIN = "system_admin"
|
||||
|
||||
ALLOW_EDIT_POST_ALWAYS = "always"
|
||||
ALLOW_EDIT_POST_NEVER = "never"
|
||||
ALLOW_EDIT_POST_TIME_LIMIT = "time_limit"
|
||||
|
||||
EMAIL_BATCHING_BUFFER_SIZE = 256
|
||||
EMAIL_BATCHING_INTERVAL = 30
|
||||
|
||||
SITENAME_MAX_LENGTH = 30
|
||||
|
||||
SERVICE_SETTINGS_DEFAULT_SITE_URL = ""
|
||||
SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE = ""
|
||||
SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE = ""
|
||||
SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT = 300
|
||||
SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT = 300
|
||||
SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = ""
|
||||
|
||||
TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT = ""
|
||||
TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT = ""
|
||||
TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT = 300
|
||||
|
||||
EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = ""
|
||||
|
||||
SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/"
|
||||
SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/"
|
||||
SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/"
|
||||
SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/"
|
||||
SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/"
|
||||
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
|
||||
|
||||
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
||||
LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = ""
|
||||
|
||||
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
||||
|
||||
NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK = "https://about.mattermost.com/downloads/"
|
||||
NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-android-app/"
|
||||
NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK = "https://about.mattermost.com/mattermost-ios-app/"
|
||||
|
||||
WEBRTC_SETTINGS_DEFAULT_STUN_URI = ""
|
||||
WEBRTC_SETTINGS_DEFAULT_TURN_URI = ""
|
||||
|
||||
ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500
|
||||
)
|
||||
|
||||
type ServiceSettings struct {
|
||||
SiteURL *string
|
||||
ListenAddress string
|
||||
ConnectionSecurity *string
|
||||
TLSCertFile *string
|
||||
TLSKeyFile *string
|
||||
UseLetsEncrypt *bool
|
||||
LetsEncryptCertificateCacheFile *string
|
||||
Forward80To443 *bool
|
||||
ReadTimeout *int
|
||||
WriteTimeout *int
|
||||
MaximumLoginAttempts int
|
||||
SegmentDeveloperKey string
|
||||
GoogleDeveloperKey string
|
||||
EnableOAuthServiceProvider bool
|
||||
EnableIncomingWebhooks bool
|
||||
EnableOutgoingWebhooks bool
|
||||
EnableCommands *bool
|
||||
EnableOnlyAdminIntegrations *bool
|
||||
EnablePostUsernameOverride bool
|
||||
EnablePostIconOverride bool
|
||||
EnableTesting bool
|
||||
EnableDeveloper *bool
|
||||
EnableSecurityFixAlert *bool
|
||||
EnableInsecureOutgoingConnections *bool
|
||||
EnableMultifactorAuthentication *bool
|
||||
EnforceMultifactorAuthentication *bool
|
||||
AllowCorsFrom *string
|
||||
SessionLengthWebInDays *int
|
||||
SessionLengthMobileInDays *int
|
||||
SessionLengthSSOInDays *int
|
||||
SessionCacheInMinutes *int
|
||||
WebsocketSecurePort *int
|
||||
WebsocketPort *int
|
||||
WebserverMode *string
|
||||
EnableCustomEmoji *bool
|
||||
RestrictCustomEmojiCreation *string
|
||||
SiteURL *string
|
||||
ListenAddress string
|
||||
ConnectionSecurity *string
|
||||
TLSCertFile *string
|
||||
TLSKeyFile *string
|
||||
UseLetsEncrypt *bool
|
||||
LetsEncryptCertificateCacheFile *string
|
||||
Forward80To443 *bool
|
||||
ReadTimeout *int
|
||||
WriteTimeout *int
|
||||
MaximumLoginAttempts int
|
||||
GoogleDeveloperKey string
|
||||
EnableOAuthServiceProvider bool
|
||||
EnableIncomingWebhooks bool
|
||||
EnableOutgoingWebhooks bool
|
||||
EnableCommands *bool
|
||||
EnableOnlyAdminIntegrations *bool
|
||||
EnablePostUsernameOverride bool
|
||||
EnablePostIconOverride bool
|
||||
EnableLinkPreviews *bool
|
||||
EnableTesting bool
|
||||
EnableDeveloper *bool
|
||||
EnableSecurityFixAlert *bool
|
||||
EnableInsecureOutgoingConnections *bool
|
||||
EnableMultifactorAuthentication *bool
|
||||
EnforceMultifactorAuthentication *bool
|
||||
AllowCorsFrom *string
|
||||
SessionLengthWebInDays *int
|
||||
SessionLengthMobileInDays *int
|
||||
SessionLengthSSOInDays *int
|
||||
SessionCacheInMinutes *int
|
||||
WebsocketSecurePort *int
|
||||
WebsocketPort *int
|
||||
WebserverMode *string
|
||||
EnableCustomEmoji *bool
|
||||
RestrictCustomEmojiCreation *string
|
||||
RestrictPostDelete *string
|
||||
AllowEditPost *string
|
||||
PostEditTimeLimit *int
|
||||
TimeBetweenUserTypingUpdatesMilliseconds *int64
|
||||
EnableUserTypingMessages *bool
|
||||
ClusterLogTimeoutMilliseconds *int
|
||||
}
|
||||
|
||||
type ClusterSettings struct {
|
||||
@@ -433,7 +493,12 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.ServiceSettings.SiteURL == nil {
|
||||
o.ServiceSettings.SiteURL = new(string)
|
||||
*o.ServiceSettings.SiteURL = ""
|
||||
*o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL
|
||||
}
|
||||
|
||||
if o.ServiceSettings.EnableLinkPreviews == nil {
|
||||
o.ServiceSettings.EnableLinkPreviews = new(bool)
|
||||
*o.ServiceSettings.EnableLinkPreviews = false
|
||||
}
|
||||
|
||||
if o.ServiceSettings.EnableDeveloper == nil {
|
||||
@@ -493,12 +558,12 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.TeamSettings.CustomBrandText == nil {
|
||||
o.TeamSettings.CustomBrandText = new(string)
|
||||
*o.TeamSettings.CustomBrandText = ""
|
||||
*o.TeamSettings.CustomBrandText = TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT
|
||||
}
|
||||
|
||||
if o.TeamSettings.CustomDescriptionText == nil {
|
||||
o.TeamSettings.CustomDescriptionText = new(string)
|
||||
*o.TeamSettings.CustomDescriptionText = ""
|
||||
*o.TeamSettings.CustomDescriptionText = TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT
|
||||
}
|
||||
|
||||
if o.TeamSettings.EnableOpenServer == nil {
|
||||
@@ -552,7 +617,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.TeamSettings.UserStatusAwayTimeout == nil {
|
||||
o.TeamSettings.UserStatusAwayTimeout = new(int64)
|
||||
*o.TeamSettings.UserStatusAwayTimeout = 300
|
||||
*o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT
|
||||
}
|
||||
|
||||
if o.TeamSettings.MaxChannelsPerTeam == nil {
|
||||
@@ -597,7 +662,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.EmailSettings.FeedbackOrganization == nil {
|
||||
o.EmailSettings.FeedbackOrganization = new(string)
|
||||
*o.EmailSettings.FeedbackOrganization = ""
|
||||
*o.EmailSettings.FeedbackOrganization = EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION
|
||||
}
|
||||
|
||||
if o.EmailSettings.EnableEmailBatching == nil {
|
||||
@@ -621,7 +686,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SupportSettings.TermsOfServiceLink == nil {
|
||||
o.SupportSettings.TermsOfServiceLink = new(string)
|
||||
*o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/"
|
||||
*o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK
|
||||
}
|
||||
|
||||
if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
|
||||
@@ -630,7 +695,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SupportSettings.PrivacyPolicyLink == nil {
|
||||
o.SupportSettings.PrivacyPolicyLink = new(string)
|
||||
*o.SupportSettings.PrivacyPolicyLink = ""
|
||||
*o.SupportSettings.PrivacyPolicyLink = SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK
|
||||
}
|
||||
|
||||
if !IsSafeLink(o.SupportSettings.AboutLink) {
|
||||
@@ -639,7 +704,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SupportSettings.AboutLink == nil {
|
||||
o.SupportSettings.AboutLink = new(string)
|
||||
*o.SupportSettings.AboutLink = ""
|
||||
*o.SupportSettings.AboutLink = SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK
|
||||
}
|
||||
|
||||
if !IsSafeLink(o.SupportSettings.HelpLink) {
|
||||
@@ -648,7 +713,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SupportSettings.HelpLink == nil {
|
||||
o.SupportSettings.HelpLink = new(string)
|
||||
*o.SupportSettings.HelpLink = ""
|
||||
*o.SupportSettings.HelpLink = SUPPORT_SETTINGS_DEFAULT_HELP_LINK
|
||||
}
|
||||
|
||||
if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
|
||||
@@ -657,12 +722,12 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SupportSettings.ReportAProblemLink == nil {
|
||||
o.SupportSettings.ReportAProblemLink = new(string)
|
||||
*o.SupportSettings.ReportAProblemLink = ""
|
||||
*o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK
|
||||
}
|
||||
|
||||
if o.SupportSettings.SupportEmail == nil {
|
||||
o.SupportSettings.SupportEmail = new(string)
|
||||
*o.SupportSettings.SupportEmail = "feedback@mattermost.com"
|
||||
*o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL
|
||||
}
|
||||
|
||||
if o.LdapSettings.Enable == nil {
|
||||
@@ -707,37 +772,37 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.LdapSettings.FirstNameAttribute == nil {
|
||||
o.LdapSettings.FirstNameAttribute = new(string)
|
||||
*o.LdapSettings.FirstNameAttribute = ""
|
||||
*o.LdapSettings.FirstNameAttribute = LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.LastNameAttribute == nil {
|
||||
o.LdapSettings.LastNameAttribute = new(string)
|
||||
*o.LdapSettings.LastNameAttribute = ""
|
||||
*o.LdapSettings.LastNameAttribute = LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.EmailAttribute == nil {
|
||||
o.LdapSettings.EmailAttribute = new(string)
|
||||
*o.LdapSettings.EmailAttribute = ""
|
||||
*o.LdapSettings.EmailAttribute = LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.UsernameAttribute == nil {
|
||||
o.LdapSettings.UsernameAttribute = new(string)
|
||||
*o.LdapSettings.UsernameAttribute = ""
|
||||
*o.LdapSettings.UsernameAttribute = LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.NicknameAttribute == nil {
|
||||
o.LdapSettings.NicknameAttribute = new(string)
|
||||
*o.LdapSettings.NicknameAttribute = ""
|
||||
*o.LdapSettings.NicknameAttribute = LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.IdAttribute == nil {
|
||||
o.LdapSettings.IdAttribute = new(string)
|
||||
*o.LdapSettings.IdAttribute = ""
|
||||
*o.LdapSettings.IdAttribute = LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.PositionAttribute == nil {
|
||||
o.LdapSettings.PositionAttribute = new(string)
|
||||
*o.LdapSettings.PositionAttribute = ""
|
||||
*o.LdapSettings.PositionAttribute = LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.LdapSettings.SyncIntervalMinutes == nil {
|
||||
@@ -762,7 +827,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.LdapSettings.LoginFieldName == nil {
|
||||
o.LdapSettings.LoginFieldName = new(string)
|
||||
*o.LdapSettings.LoginFieldName = ""
|
||||
*o.LdapSettings.LoginFieldName = LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME
|
||||
}
|
||||
|
||||
if o.ServiceSettings.SessionLengthWebInDays == nil {
|
||||
@@ -807,7 +872,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.ServiceSettings.AllowCorsFrom == nil {
|
||||
o.ServiceSettings.AllowCorsFrom = new(string)
|
||||
*o.ServiceSettings.AllowCorsFrom = ""
|
||||
*o.ServiceSettings.AllowCorsFrom = SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM
|
||||
}
|
||||
|
||||
if o.ServiceSettings.WebserverMode == nil {
|
||||
@@ -827,6 +892,21 @@ func (o *Config) SetDefaults() {
|
||||
*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL
|
||||
}
|
||||
|
||||
if o.ServiceSettings.RestrictPostDelete == nil {
|
||||
o.ServiceSettings.RestrictPostDelete = new(string)
|
||||
*o.ServiceSettings.RestrictPostDelete = PERMISSIONS_DELETE_POST_ALL
|
||||
}
|
||||
|
||||
if o.ServiceSettings.AllowEditPost == nil {
|
||||
o.ServiceSettings.AllowEditPost = new(string)
|
||||
*o.ServiceSettings.AllowEditPost = ALLOW_EDIT_POST_ALWAYS
|
||||
}
|
||||
|
||||
if o.ServiceSettings.PostEditTimeLimit == nil {
|
||||
o.ServiceSettings.PostEditTimeLimit = new(int)
|
||||
*o.ServiceSettings.PostEditTimeLimit = 300
|
||||
}
|
||||
|
||||
if o.ClusterSettings.InterNodeListenAddress == nil {
|
||||
o.ClusterSettings.InterNodeListenAddress = new(string)
|
||||
*o.ClusterSettings.InterNodeListenAddress = ":8075"
|
||||
@@ -853,7 +933,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.AnalyticsSettings.MaxUsersForStatistics == nil {
|
||||
o.AnalyticsSettings.MaxUsersForStatistics = new(int)
|
||||
*o.AnalyticsSettings.MaxUsersForStatistics = 2500
|
||||
*o.AnalyticsSettings.MaxUsersForStatistics = ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS
|
||||
}
|
||||
|
||||
if o.ComplianceSettings.Enable == nil {
|
||||
@@ -943,52 +1023,52 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.SamlSettings.FirstNameAttribute == nil {
|
||||
o.SamlSettings.FirstNameAttribute = new(string)
|
||||
*o.SamlSettings.FirstNameAttribute = ""
|
||||
*o.SamlSettings.FirstNameAttribute = SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.LastNameAttribute == nil {
|
||||
o.SamlSettings.LastNameAttribute = new(string)
|
||||
*o.SamlSettings.LastNameAttribute = ""
|
||||
*o.SamlSettings.LastNameAttribute = SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.EmailAttribute == nil {
|
||||
o.SamlSettings.EmailAttribute = new(string)
|
||||
*o.SamlSettings.EmailAttribute = ""
|
||||
*o.SamlSettings.EmailAttribute = SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.UsernameAttribute == nil {
|
||||
o.SamlSettings.UsernameAttribute = new(string)
|
||||
*o.SamlSettings.UsernameAttribute = ""
|
||||
*o.SamlSettings.UsernameAttribute = SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.NicknameAttribute == nil {
|
||||
o.SamlSettings.NicknameAttribute = new(string)
|
||||
*o.SamlSettings.NicknameAttribute = ""
|
||||
*o.SamlSettings.NicknameAttribute = SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.PositionAttribute == nil {
|
||||
o.SamlSettings.PositionAttribute = new(string)
|
||||
*o.SamlSettings.PositionAttribute = ""
|
||||
*o.SamlSettings.PositionAttribute = SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.SamlSettings.LocaleAttribute == nil {
|
||||
o.SamlSettings.LocaleAttribute = new(string)
|
||||
*o.SamlSettings.LocaleAttribute = ""
|
||||
*o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE
|
||||
}
|
||||
|
||||
if o.NativeAppSettings.AppDownloadLink == nil {
|
||||
o.NativeAppSettings.AppDownloadLink = new(string)
|
||||
*o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/"
|
||||
*o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK
|
||||
}
|
||||
|
||||
if o.NativeAppSettings.AndroidAppDownloadLink == nil {
|
||||
o.NativeAppSettings.AndroidAppDownloadLink = new(string)
|
||||
*o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/"
|
||||
*o.NativeAppSettings.AndroidAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK
|
||||
}
|
||||
|
||||
if o.NativeAppSettings.IosAppDownloadLink == nil {
|
||||
o.NativeAppSettings.IosAppDownloadLink = new(string)
|
||||
*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
|
||||
*o.NativeAppSettings.IosAppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK
|
||||
}
|
||||
|
||||
if o.RateLimitSettings.Enable == nil {
|
||||
@@ -1008,12 +1088,12 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.ServiceSettings.TLSKeyFile == nil {
|
||||
o.ServiceSettings.TLSKeyFile = new(string)
|
||||
*o.ServiceSettings.TLSKeyFile = ""
|
||||
*o.ServiceSettings.TLSKeyFile = SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE
|
||||
}
|
||||
|
||||
if o.ServiceSettings.TLSCertFile == nil {
|
||||
o.ServiceSettings.TLSCertFile = new(string)
|
||||
*o.ServiceSettings.TLSCertFile = ""
|
||||
*o.ServiceSettings.TLSCertFile = SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE
|
||||
}
|
||||
|
||||
if o.ServiceSettings.UseLetsEncrypt == nil {
|
||||
@@ -1028,12 +1108,12 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.ServiceSettings.ReadTimeout == nil {
|
||||
o.ServiceSettings.ReadTimeout = new(int)
|
||||
*o.ServiceSettings.ReadTimeout = 300
|
||||
*o.ServiceSettings.ReadTimeout = SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT
|
||||
}
|
||||
|
||||
if o.ServiceSettings.WriteTimeout == nil {
|
||||
o.ServiceSettings.WriteTimeout = new(int)
|
||||
*o.ServiceSettings.WriteTimeout = 300
|
||||
*o.ServiceSettings.WriteTimeout = SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT
|
||||
}
|
||||
|
||||
if o.ServiceSettings.Forward80To443 == nil {
|
||||
@@ -1046,6 +1126,21 @@ func (o *Config) SetDefaults() {
|
||||
*o.MetricsSettings.BlockProfileRate = 0
|
||||
}
|
||||
|
||||
if o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds == nil {
|
||||
o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = new(int64)
|
||||
*o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000
|
||||
}
|
||||
|
||||
if o.ServiceSettings.EnableUserTypingMessages == nil {
|
||||
o.ServiceSettings.EnableUserTypingMessages = new(bool)
|
||||
*o.ServiceSettings.EnableUserTypingMessages = true
|
||||
}
|
||||
|
||||
if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil {
|
||||
o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int)
|
||||
*o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000
|
||||
}
|
||||
|
||||
o.defaultWebrtcSettings()
|
||||
}
|
||||
|
||||
@@ -1277,6 +1372,10 @@ func (o *Config) IsValid() *AppError {
|
||||
return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "")
|
||||
}
|
||||
|
||||
if *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds < 1000 {
|
||||
return NewLocAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1339,12 +1438,12 @@ func (o *Config) defaultWebrtcSettings() {
|
||||
|
||||
if o.WebrtcSettings.StunURI == nil {
|
||||
o.WebrtcSettings.StunURI = new(string)
|
||||
*o.WebrtcSettings.StunURI = ""
|
||||
*o.WebrtcSettings.StunURI = WEBRTC_SETTINGS_DEFAULT_STUN_URI
|
||||
}
|
||||
|
||||
if o.WebrtcSettings.TurnURI == nil {
|
||||
o.WebrtcSettings.TurnURI = new(string)
|
||||
*o.WebrtcSettings.TurnURI = ""
|
||||
*o.WebrtcSettings.TurnURI = WEBRTC_SETTINGS_DEFAULT_TURN_URI
|
||||
}
|
||||
|
||||
if o.WebrtcSettings.TurnUsername == nil {
|
||||
|
||||
4
vendor/github.com/mattermost/platform/model/file.go
generated
vendored
4
vendor/github.com/mattermost/platform/model/file.go
generated
vendored
@@ -8,6 +8,10 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image
|
||||
)
|
||||
|
||||
var (
|
||||
IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"}
|
||||
IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"}
|
||||
|
||||
9
vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
generated
vendored
9
vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
generated
vendored
@@ -65,6 +65,15 @@ func gitLabUserFromJson(data io.Reader) *GitLabUser {
|
||||
}
|
||||
}
|
||||
|
||||
func (glu *GitLabUser) ToJson() string {
|
||||
b, err := json.Marshal(glu)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (glu *GitLabUser) IsValid() bool {
|
||||
if glu.Id == 0 {
|
||||
return false
|
||||
|
||||
46
vendor/github.com/mattermost/platform/model/incoming_webhook.go
generated
vendored
46
vendor/github.com/mattermost/platform/model/incoming_webhook.go
generated
vendored
@@ -29,13 +29,13 @@ type IncomingWebhook struct {
|
||||
}
|
||||
|
||||
type IncomingWebhookRequest struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
ChannelName string `json:"channel"`
|
||||
Props StringInterface `json:"props"`
|
||||
Attachments interface{} `json:"attachments"`
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
ChannelName string `json:"channel"`
|
||||
Props StringInterface `json:"props"`
|
||||
Attachments []*SlackAttachment `json:"attachments"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (o *IncomingWebhook) ToJson() string {
|
||||
@@ -212,31 +212,15 @@ func expandAnnouncement(text string) string {
|
||||
func expandAnnouncements(i *IncomingWebhookRequest) {
|
||||
i.Text = expandAnnouncement(i.Text)
|
||||
|
||||
if i.Attachments != nil {
|
||||
attachments := i.Attachments.([]interface{})
|
||||
for _, attachment := range attachments {
|
||||
a := attachment.(map[string]interface{})
|
||||
for _, attachment := range i.Attachments {
|
||||
attachment.Pretext = expandAnnouncement(attachment.Pretext)
|
||||
attachment.Text = expandAnnouncement(attachment.Text)
|
||||
attachment.Title = expandAnnouncement(attachment.Title)
|
||||
|
||||
if a["pretext"] != nil {
|
||||
a["pretext"] = expandAnnouncement(a["pretext"].(string))
|
||||
}
|
||||
|
||||
if a["text"] != nil {
|
||||
a["text"] = expandAnnouncement(a["text"].(string))
|
||||
}
|
||||
|
||||
if a["title"] != nil {
|
||||
a["title"] = expandAnnouncement(a["title"].(string))
|
||||
}
|
||||
|
||||
if a["fields"] != nil {
|
||||
fields := a["fields"].([]interface{})
|
||||
for _, field := range fields {
|
||||
f := field.(map[string]interface{})
|
||||
if f["value"] != nil {
|
||||
f["value"] = expandAnnouncement(fmt.Sprintf("%v", f["value"]))
|
||||
}
|
||||
}
|
||||
for _, field := range attachment.Fields {
|
||||
if field.Value != nil {
|
||||
// Ensure the value is set to a string if it is set
|
||||
field.Value = expandAnnouncement(fmt.Sprintf("%v", field.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/github.com/mattermost/platform/model/job.go
generated
vendored
4
vendor/github.com/mattermost/platform/model/job.go
generated
vendored
@@ -14,8 +14,8 @@ type ScheduledTask struct {
|
||||
Name string `json:"name"`
|
||||
Interval time.Duration `json:"interval"`
|
||||
Recurring bool `json:"recurring"`
|
||||
function TaskFunc `json:",omitempty"`
|
||||
timer *time.Timer `json:",omitempty"`
|
||||
function TaskFunc
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
var tasks = make(map[string]*ScheduledTask)
|
||||
|
||||
5
vendor/github.com/mattermost/platform/model/license.go
generated
vendored
5
vendor/github.com/mattermost/platform/model/license.go
generated
vendored
@@ -8,6 +8,11 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error"
|
||||
INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error"
|
||||
)
|
||||
|
||||
type LicenseRecord struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
|
||||
14
vendor/github.com/mattermost/platform/model/post.go
generated
vendored
14
vendor/github.com/mattermost/platform/model/post.go
generated
vendored
@@ -14,10 +14,15 @@ const (
|
||||
POST_DEFAULT = ""
|
||||
POST_SLACK_ATTACHMENT = "slack_attachment"
|
||||
POST_SYSTEM_GENERIC = "system_generic"
|
||||
POST_JOIN_LEAVE = "system_join_leave"
|
||||
POST_ADD_REMOVE = "system_add_remove"
|
||||
POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead
|
||||
POST_JOIN_CHANNEL = "system_join_channel"
|
||||
POST_LEAVE_CHANNEL = "system_leave_channel"
|
||||
POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead
|
||||
POST_ADD_TO_CHANNEL = "system_add_to_channel"
|
||||
POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel"
|
||||
POST_HEADER_CHANGE = "system_header_change"
|
||||
POST_DISPLAYNAME_CHANGE = "system_displayname_change"
|
||||
POST_PURPOSE_CHANGE = "system_purpose_change"
|
||||
POST_CHANNEL_DELETED = "system_channel_deleted"
|
||||
POST_EPHEMERAL = "system_ephemeral"
|
||||
POST_FILEIDS_MAX_RUNES = 150
|
||||
@@ -31,6 +36,7 @@ type Post struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
EditAt int64 `json:"edit_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
UserId string `json:"user_id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
@@ -119,7 +125,9 @@ func (o *Post) IsValid() *AppError {
|
||||
|
||||
// should be removed once more message types are supported
|
||||
if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE ||
|
||||
o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE ||
|
||||
o.Type == POST_JOIN_CHANNEL || o.Type == POST_LEAVE_CHANNEL ||
|
||||
o.Type == POST_REMOVE_FROM_CHANNEL || o.Type == POST_ADD_TO_CHANNEL ||
|
||||
o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE || o.Type == POST_PURPOSE_CHANGE ||
|
||||
o.Type == POST_DISPLAYNAME_CHANGE || o.Type == POST_CHANNEL_DELETED) {
|
||||
return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type)
|
||||
}
|
||||
|
||||
17
vendor/github.com/mattermost/platform/model/post_list.go
generated
vendored
17
vendor/github.com/mattermost/platform/model/post_list.go
generated
vendored
@@ -13,6 +13,13 @@ type PostList struct {
|
||||
Posts map[string]*Post `json:"posts"`
|
||||
}
|
||||
|
||||
func NewPostList() *PostList {
|
||||
return &PostList{
|
||||
Order: make([]string, 0),
|
||||
Posts: make(map[string]*Post),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *PostList) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
@@ -72,10 +79,18 @@ func (o *PostList) Etag() string {
|
||||
if v.UpdateAt > t {
|
||||
t = v.UpdateAt
|
||||
id = v.Id
|
||||
} else if v.UpdateAt == t && v.Id > id {
|
||||
t = v.UpdateAt
|
||||
id = v.Id
|
||||
}
|
||||
}
|
||||
|
||||
return Etag(id, t)
|
||||
orderId := ""
|
||||
if len(o.Order) > 0 {
|
||||
orderId = o.Order[0]
|
||||
}
|
||||
|
||||
return Etag(orderId, id, t)
|
||||
}
|
||||
|
||||
func (o *PostList) IsChannelId(channelId string) bool {
|
||||
|
||||
18
vendor/github.com/mattermost/platform/model/push_notification.go
generated
vendored
18
vendor/github.com/mattermost/platform/model/push_notification.go
generated
vendored
@@ -10,8 +10,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PUSH_NOTIFY_APPLE = "apple"
|
||||
PUSH_NOTIFY_ANDROID = "android"
|
||||
PUSH_NOTIFY_APPLE = "apple"
|
||||
PUSH_NOTIFY_ANDROID = "android"
|
||||
PUSH_NOTIFY_APPLE_REACT_NATIVE = "apple_rn"
|
||||
PUSH_NOTIFY_ANDROID_REACT_NATIVE = "android_rn"
|
||||
|
||||
PUSH_TYPE_MESSAGE = "message"
|
||||
PUSH_TYPE_CLEAR = "clear"
|
||||
@@ -46,12 +48,12 @@ func (me *PushNotification) ToJson() string {
|
||||
}
|
||||
|
||||
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
|
||||
if strings.HasPrefix(deviceId, PUSH_NOTIFY_APPLE+":") {
|
||||
me.Platform = PUSH_NOTIFY_APPLE
|
||||
me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_APPLE+":")
|
||||
} else if strings.HasPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") {
|
||||
me.Platform = PUSH_NOTIFY_ANDROID
|
||||
me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_ANDROID+":")
|
||||
|
||||
index := strings.Index(deviceId, ":")
|
||||
|
||||
if index > -1 {
|
||||
me.Platform = deviceId[:index]
|
||||
me.DeviceId = deviceId[index+1:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
vendor/github.com/mattermost/platform/model/push_response.go
generated
vendored
Normal file
57
vendor/github.com/mattermost/platform/model/push_response.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
PUSH_STATUS = "status"
|
||||
PUSH_STATUS_OK = "OK"
|
||||
PUSH_STATUS_FAIL = "FAIL"
|
||||
PUSH_STATUS_REMOVE = "REMOVE"
|
||||
PUSH_STATUS_ERROR_MSG = "error"
|
||||
)
|
||||
|
||||
type PushResponse map[string]string
|
||||
|
||||
func NewOkPushResponse() PushResponse {
|
||||
m := make(map[string]string)
|
||||
m[PUSH_STATUS] = PUSH_STATUS_OK
|
||||
return m
|
||||
}
|
||||
|
||||
func NewRemovePushResponse() PushResponse {
|
||||
m := make(map[string]string)
|
||||
m[PUSH_STATUS] = PUSH_STATUS_REMOVE
|
||||
return m
|
||||
}
|
||||
|
||||
func NewErrorPushResponse(message string) PushResponse {
|
||||
m := make(map[string]string)
|
||||
m[PUSH_STATUS] = PUSH_STATUS_FAIL
|
||||
m[PUSH_STATUS_ERROR_MSG] = message
|
||||
return m
|
||||
}
|
||||
|
||||
func (me *PushResponse) ToJson() string {
|
||||
if b, err := json.Marshal(me); err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func PushResponseFromJson(data io.Reader) PushResponse {
|
||||
decoder := json.NewDecoder(data)
|
||||
|
||||
var objmap PushResponse
|
||||
if err := decoder.Decode(&objmap); err != nil {
|
||||
return make(map[string]string)
|
||||
} else {
|
||||
return objmap
|
||||
}
|
||||
}
|
||||
5
vendor/github.com/mattermost/platform/model/session.go
generated
vendored
5
vendor/github.com/mattermost/platform/model/session.go
generated
vendored
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
const (
|
||||
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
|
||||
SESSION_CACHE_SIZE = 25000
|
||||
SESSION_CACHE_SIZE = 35000
|
||||
SESSION_PROP_PLATFORM = "platform"
|
||||
SESSION_PROP_OS = "os"
|
||||
SESSION_PROP_BROWSER = "browser"
|
||||
@@ -111,8 +111,7 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember {
|
||||
}
|
||||
|
||||
func (me *Session) IsMobileApp() bool {
|
||||
return len(me.DeviceId) > 0 &&
|
||||
(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":"))
|
||||
return len(me.DeviceId) > 0
|
||||
}
|
||||
|
||||
func (me *Session) GetUserRoles() []string {
|
||||
|
||||
29
vendor/github.com/mattermost/platform/model/slack_attachment.go
generated
vendored
Normal file
29
vendor/github.com/mattermost/platform/model/slack_attachment.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
type SlackAttachment struct {
|
||||
Id int64 `json:"id"`
|
||||
Fallback string `json:"fallback"`
|
||||
Color string `json:"color"`
|
||||
Pretext string `json:"pretext"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorLink string `json:"author_link"`
|
||||
AuthorIcon string `json:"author_icon"`
|
||||
Title string `json:"title"`
|
||||
TitleLink string `json:"title_link"`
|
||||
Text string `json:"text"`
|
||||
Fields []*SlackAttachmentField `json:"fields"`
|
||||
ImageURL string `json:"image_url"`
|
||||
ThumbURL string `json:"thumb_url"`
|
||||
Footer string `json:"footer"`
|
||||
FooterIcon string `json:"footer_icon"`
|
||||
Timestamp interface{} `json:"ts"` // This is either a string or an int64
|
||||
}
|
||||
|
||||
type SlackAttachmentField struct {
|
||||
Title string `json:"title"`
|
||||
Value interface{} `json:"value"`
|
||||
Short bool `json:"short"`
|
||||
}
|
||||
2
vendor/github.com/mattermost/platform/model/status.go
generated
vendored
2
vendor/github.com/mattermost/platform/model/status.go
generated
vendored
@@ -12,7 +12,7 @@ const (
|
||||
STATUS_OFFLINE = "offline"
|
||||
STATUS_AWAY = "away"
|
||||
STATUS_ONLINE = "online"
|
||||
STATUS_CACHE_SIZE = 25000
|
||||
STATUS_CACHE_SIZE = SESSION_CACHE_SIZE
|
||||
STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds
|
||||
STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes
|
||||
)
|
||||
|
||||
80
vendor/github.com/mattermost/platform/model/team.go
generated
vendored
80
vendor/github.com/mattermost/platform/model/team.go
generated
vendored
@@ -7,14 +7,22 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
TEAM_OPEN = "O"
|
||||
TEAM_INVITE = "I"
|
||||
TEAM_OPEN = "O"
|
||||
TEAM_INVITE = "I"
|
||||
TEAM_ALLOWED_DOMAINS_MAX_LENGTH = 500
|
||||
TEAM_COMPANY_NAME_MAX_LENGTH = 64
|
||||
TEAM_DESCRIPTION_MAX_LENGTH = 255
|
||||
TEAM_DISPLAY_NAME_MAX_RUNES = 64
|
||||
TEAM_EMAIL_MAX_LENGTH = 128
|
||||
TEAM_NAME_MAX_LENGTH = 64
|
||||
TEAM_NAME_MIN_LENGTH = 2
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
@@ -48,6 +56,14 @@ func InvitesFromJson(data io.Reader) *Invites {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Invites) ToEmailList() []string {
|
||||
emailList := make([]string, len(o.Invites))
|
||||
for _, invite := range o.Invites {
|
||||
emailList = append(emailList, invite["email"])
|
||||
}
|
||||
return emailList
|
||||
}
|
||||
|
||||
func (o *Invites) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
@@ -97,6 +113,26 @@ func TeamMapFromJson(data io.Reader) map[string]*Team {
|
||||
}
|
||||
}
|
||||
|
||||
func TeamListToJson(t []*Team) string {
|
||||
b, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TeamListFromJson(data io.Reader) []*Team {
|
||||
decoder := json.NewDecoder(data)
|
||||
var teams []*Team
|
||||
err := decoder.Decode(&teams)
|
||||
if err == nil {
|
||||
return teams
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Team) Etag() string {
|
||||
return Etag(o.Id, o.UpdateAt)
|
||||
}
|
||||
@@ -104,55 +140,55 @@ func (o *Team) Etag() string {
|
||||
func (o *Team) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "")
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if o.CreateAt == 0 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if o.UpdateAt == 0 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.Email) > 128 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id)
|
||||
if len(o.Email) > TEAM_EMAIL_MAX_LENGTH {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.Email) > 0 && !IsValidEmail(o.Email) {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > 64 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id)
|
||||
if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > TEAM_DISPLAY_NAME_MAX_RUNES {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.Name) > 64 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
|
||||
if len(o.Name) > TEAM_NAME_MAX_LENGTH {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.Description) > 255 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id)
|
||||
if len(o.Description) > TEAM_DESCRIPTION_MAX_LENGTH {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if IsReservedTeamName(o.Name) {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !IsValidTeamName(o.Name) {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id)
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.CompanyName) > 64 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id)
|
||||
if len(o.CompanyName) > TEAM_COMPANY_NAME_MAX_LENGTH {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(o.AllowedDomains) > 500 {
|
||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id)
|
||||
if len(o.AllowedDomains) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH {
|
||||
return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -193,7 +229,7 @@ func IsValidTeamName(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(s) <= 1 {
|
||||
if len(s) < TEAM_NAME_MIN_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
143
vendor/github.com/mattermost/platform/model/user.go
generated
vendored
143
vendor/github.com/mattermost/platform/model/user.go
generated
vendored
@@ -7,20 +7,36 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
USER_NOTIFY_ALL = "all"
|
||||
USER_NOTIFY_MENTION = "mention"
|
||||
USER_NOTIFY_NONE = "none"
|
||||
USER_NOTIFY_ALL = "all"
|
||||
USER_NOTIFY_MENTION = "mention"
|
||||
USER_NOTIFY_NONE = "none"
|
||||
DESKTOP_NOTIFY_PROP = "desktop"
|
||||
MARK_UNREAD_NOTIFY_PROP = "mark_unread"
|
||||
PUSH_NOTIFY_PROP = "push"
|
||||
EMAIL_NOTIFY_PROP = "email"
|
||||
|
||||
DEFAULT_LOCALE = "en"
|
||||
USER_AUTH_SERVICE_EMAIL = "email"
|
||||
USER_AUTH_SERVICE_USERNAME = "username"
|
||||
|
||||
USER_EMAIL_MAX_LENGTH = 128
|
||||
USER_NICKNAME_MAX_RUNES = 64
|
||||
USER_POSITION_MAX_RUNES = 35
|
||||
USER_FIRST_NAME_MAX_RUNES = 64
|
||||
USER_LAST_NAME_MAX_RUNES = 64
|
||||
USER_AUTH_DATA_MAX_LENGTH = 128
|
||||
USER_NAME_MAX_LENGTH = 64
|
||||
USER_NAME_MIN_LENGTH = 1
|
||||
)
|
||||
|
||||
type User struct {
|
||||
@@ -51,56 +67,68 @@ type User struct {
|
||||
LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
|
||||
}
|
||||
|
||||
type UserPatch struct {
|
||||
Username *string `json:"username"`
|
||||
Nickname *string `json:"nickname"`
|
||||
FirstName *string `json:"first_name"`
|
||||
LastName *string `json:"last_name"`
|
||||
Position *string `json:"position"`
|
||||
Email *string `json:"email"`
|
||||
Props *StringMap `json:"props,omitempty"`
|
||||
NotifyProps *StringMap `json:"notify_props,omitempty"`
|
||||
Locale *string `json:"locale"`
|
||||
}
|
||||
|
||||
// IsValid validates the user and returns an error if it isn't configured
|
||||
// correctly.
|
||||
func (u *User) IsValid() *AppError {
|
||||
|
||||
if len(u.Id) != 26 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "")
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.CreateAt == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.UpdateAt == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !IsValidUsername(u.Username) {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(u.Email) > 128 || len(u.Email) == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id)
|
||||
if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.Nickname) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
|
||||
if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.Position) > 35 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id)
|
||||
if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.FirstName) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
|
||||
if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.LastName) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id)
|
||||
if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.AuthData != nil && len(*u.AuthData) > 128 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id)
|
||||
if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH {
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -115,7 +143,7 @@ func (u *User) PreSave() {
|
||||
}
|
||||
|
||||
if u.Username == "" {
|
||||
u.Username = NewId()
|
||||
u.Username = "n" + NewId()
|
||||
}
|
||||
|
||||
if u.AuthData != nil && *u.AuthData == "" {
|
||||
@@ -205,6 +233,44 @@ func (user *User) UpdateMentionKeysFromUsername(oldUsername string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Patch(patch *UserPatch) {
|
||||
if patch.Username != nil {
|
||||
u.Username = *patch.Username
|
||||
}
|
||||
|
||||
if patch.Nickname != nil {
|
||||
u.Nickname = *patch.Nickname
|
||||
}
|
||||
|
||||
if patch.FirstName != nil {
|
||||
u.FirstName = *patch.FirstName
|
||||
}
|
||||
|
||||
if patch.LastName != nil {
|
||||
u.LastName = *patch.LastName
|
||||
}
|
||||
|
||||
if patch.Position != nil {
|
||||
u.Position = *patch.Position
|
||||
}
|
||||
|
||||
if patch.Email != nil {
|
||||
u.Email = *patch.Email
|
||||
}
|
||||
|
||||
if patch.Props != nil {
|
||||
u.Props = *patch.Props
|
||||
}
|
||||
|
||||
if patch.NotifyProps != nil {
|
||||
u.NotifyProps = *patch.NotifyProps
|
||||
}
|
||||
|
||||
if patch.Locale != nil {
|
||||
u.Locale = *patch.Locale
|
||||
}
|
||||
}
|
||||
|
||||
// ToJson convert a User to a json string
|
||||
func (u *User) ToJson() string {
|
||||
b, err := json.Marshal(u)
|
||||
@@ -215,6 +281,15 @@ func (u *User) ToJson() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserPatch) ToJson() string {
|
||||
b, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a valid strong etag so the browser can cache the results
|
||||
func (u *User) Etag(showFullName, showEmail bool) string {
|
||||
return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
|
||||
@@ -376,6 +451,13 @@ func IsInRole(userRoles string, inRole string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *User) IsSSOUser() bool {
|
||||
if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *User) IsOAuthUser() bool {
|
||||
if u.AuthService == USER_AUTH_SERVICE_GITLAB {
|
||||
return true
|
||||
@@ -402,6 +484,17 @@ func UserFromJson(data io.Reader) *User {
|
||||
}
|
||||
}
|
||||
|
||||
func UserPatchFromJson(data io.Reader) *UserPatch {
|
||||
decoder := json.NewDecoder(data)
|
||||
var user UserPatch
|
||||
err := decoder.Decode(&user)
|
||||
if err == nil {
|
||||
return &user
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func UserMapToJson(u map[string]*User) string {
|
||||
b, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
@@ -472,7 +565,7 @@ var restrictedUsernames = []string{
|
||||
}
|
||||
|
||||
func IsValidUsername(s string) bool {
|
||||
if len(s) == 0 || len(s) > 64 {
|
||||
if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -480,6 +573,10 @@ func IsValidUsername(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if !unicode.IsLetter(rune(s[0])) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, restrictedUsername := range restrictedUsernames {
|
||||
if s == restrictedUsername {
|
||||
return false
|
||||
|
||||
32
vendor/github.com/mattermost/platform/model/utils.go
generated
vendored
32
vendor/github.com/mattermost/platform/model/utils.go
generated
vendored
@@ -34,14 +34,14 @@ type StringArray []string
|
||||
type EncryptStringMap map[string]string
|
||||
|
||||
type AppError struct {
|
||||
Id string `json:"id"`
|
||||
Message string `json:"message"` // Message to be display to the end user without debugging information
|
||||
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
||||
RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
|
||||
StatusCode int `json:"status_code,omitempty"` // The http status code
|
||||
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
||||
IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
|
||||
params map[string]interface{} `json:"-"`
|
||||
Id string `json:"id"`
|
||||
Message string `json:"message"` // Message to be display to the end user without debugging information
|
||||
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
|
||||
RequestId string `json:"request_id,omitempty"` // The RequestId that's also set in the header
|
||||
StatusCode int `json:"status_code,omitempty"` // The http status code
|
||||
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
||||
IsOAuth bool `json:"is_oauth,omitempty"` // Whether the error is OAuth specific
|
||||
params map[string]interface{}
|
||||
}
|
||||
|
||||
func (er *AppError) Error() string {
|
||||
@@ -93,6 +93,18 @@ func AppErrorFromJson(data io.Reader) *AppError {
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
|
||||
ap := &AppError{}
|
||||
ap.Id = id
|
||||
ap.params = params
|
||||
ap.Message = id
|
||||
ap.Where = where
|
||||
ap.DetailedError = details
|
||||
ap.StatusCode = status
|
||||
ap.IsOAuth = false
|
||||
return ap
|
||||
}
|
||||
|
||||
func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
|
||||
ap := &AppError{}
|
||||
ap.Id = id
|
||||
@@ -268,7 +280,7 @@ func IsValidChannelIdentifier(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(s) < 2 {
|
||||
if len(s) < CHANNEL_NAME_MIN_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -370,7 +382,7 @@ func ClearMentionTags(post string) string {
|
||||
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
||||
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
|
||||
|
||||
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
|
||||
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true, '^': true, '#': true, '$': true, '&': true}
|
||||
|
||||
func IsValidHttpUrl(rawUrl string) bool {
|
||||
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
|
||||
|
||||
1
vendor/github.com/mattermost/platform/model/version.go
generated
vendored
1
vendor/github.com/mattermost/platform/model/version.go
generated
vendored
@@ -13,6 +13,7 @@ import (
|
||||
// It should be maitained in chronological order with most current
|
||||
// release at the front of the list.
|
||||
var versions = []string{
|
||||
"3.7.0",
|
||||
"3.6.0",
|
||||
"3.5.0",
|
||||
"3.4.0",
|
||||
|
||||
8
vendor/github.com/mattermost/platform/model/websocket_client.go
generated
vendored
8
vendor/github.com/mattermost/platform/model/websocket_client.go
generated
vendored
@@ -8,6 +8,10 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB
|
||||
)
|
||||
|
||||
type WebSocketClient struct {
|
||||
Url string // The location of the server like "ws://localhost:8065"
|
||||
ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3"
|
||||
@@ -22,14 +26,14 @@ type WebSocketClient struct {
|
||||
// NewWebSocketClient constructs a new WebSocket client with convienence
|
||||
// methods for talking to the server.
|
||||
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil)
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil)
|
||||
if err != nil {
|
||||
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
|
||||
}
|
||||
|
||||
client := &WebSocketClient{
|
||||
url,
|
||||
url + API_URL_SUFFIX,
|
||||
url + API_URL_SUFFIX_V3,
|
||||
conn,
|
||||
authToken,
|
||||
1,
|
||||
|
||||
3
vendor/github.com/mattermost/platform/model/websocket_message.go
generated
vendored
3
vendor/github.com/mattermost/platform/model/websocket_message.go
generated
vendored
@@ -14,8 +14,9 @@ const (
|
||||
WEBSOCKET_EVENT_POST_EDITED = "post_edited"
|
||||
WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
|
||||
WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
|
||||
WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
|
||||
WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
|
||||
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
|
||||
WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
|
||||
WEBSOCKET_EVENT_NEW_USER = "new_user"
|
||||
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
|
||||
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
|
||||
|
||||
897
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
generated
vendored
Normal file
897
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,897 @@
|
||||
Mattermost Licensing
|
||||
|
||||
SOFTWARE LICENSING
|
||||
|
||||
You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE
|
||||
|
||||
- See MIT-COMPILED-LICENSE.md included in compiled versions for details
|
||||
|
||||
You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways:
|
||||
|
||||
1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or
|
||||
2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com
|
||||
|
||||
You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,
|
||||
webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0.
|
||||
|
||||
We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not
|
||||
link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and
|
||||
(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of
|
||||
a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license.
|
||||
|
||||
MATTERMOST TRADEMARK GUIDELINES
|
||||
|
||||
Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark
|
||||
Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions
|
||||
you have about using these trademarks, please email trademark@mattermost.com
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The software is released under the terms of the GNU Affero General Public
|
||||
License, version 3.
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
288
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
generated
vendored
Normal file
288
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/config.go
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type xmlProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type xmlFilter struct {
|
||||
Enabled string `xml:"enabled,attr"`
|
||||
Tag string `xml:"tag"`
|
||||
Level string `xml:"level"`
|
||||
Type string `xml:"type"`
|
||||
Property []xmlProperty `xml:"property"`
|
||||
}
|
||||
|
||||
type xmlLoggerConfig struct {
|
||||
Filter []xmlFilter `xml:"filter"`
|
||||
}
|
||||
|
||||
// Load XML configuration; see examples/example.xml for documentation
|
||||
func (log Logger) LoadConfiguration(filename string) {
|
||||
log.Close()
|
||||
|
||||
// Open the configuration file
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
xc := new(xmlLoggerConfig)
|
||||
if err := xml.Unmarshal(contents, xc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, xmlfilt := range xc.Filter {
|
||||
var filt LogWriter
|
||||
var lvl Level
|
||||
bad, good, enabled := false, true, false
|
||||
|
||||
// Check required children
|
||||
if len(xmlfilt.Enabled) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
|
||||
bad = true
|
||||
} else {
|
||||
enabled = xmlfilt.Enabled != "false"
|
||||
}
|
||||
if len(xmlfilt.Tag) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Type) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Level) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
|
||||
bad = true
|
||||
}
|
||||
|
||||
switch xmlfilt.Level {
|
||||
case "FINEST":
|
||||
lvl = FINEST
|
||||
case "FINE":
|
||||
lvl = FINE
|
||||
case "DEBUG":
|
||||
lvl = DEBUG
|
||||
case "TRACE":
|
||||
lvl = TRACE
|
||||
case "INFO":
|
||||
lvl = INFO
|
||||
case "WARNING":
|
||||
lvl = WARNING
|
||||
case "ERROR":
|
||||
lvl = ERROR
|
||||
case "CRITICAL":
|
||||
lvl = CRITICAL
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
|
||||
bad = true
|
||||
}
|
||||
|
||||
// Just so all of the required attributes are errored at the same time if missing
|
||||
if bad {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch xmlfilt.Type {
|
||||
case "console":
|
||||
filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "file":
|
||||
filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "xml":
|
||||
filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "socket":
|
||||
filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Just so all of the required params are errored at the same time if wrong
|
||||
if !good {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// If we're disabled (syntax and correctness checks only), don't add to logger
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
log[xmlfilt.Tag] = &Filter{lvl, filt}
|
||||
}
|
||||
}
|
||||
|
||||
func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewConsoleLogWriter(), true
|
||||
}
|
||||
|
||||
// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
|
||||
func strToNumSuffix(str string, mult int) int {
|
||||
num := 1
|
||||
if len(str) > 1 {
|
||||
switch str[len(str)-1] {
|
||||
case 'G', 'g':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'M', 'm':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'K', 'k':
|
||||
num *= mult
|
||||
str = str[0 : len(str)-1]
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.Atoi(str)
|
||||
return parsed * num
|
||||
}
|
||||
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
format := "[%D %T] [%L] (%S) %M"
|
||||
maxlines := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "format":
|
||||
format = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxlines":
|
||||
maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
flw := NewFileLogWriter(file, rotate)
|
||||
flw.SetFormat(format)
|
||||
flw.SetRotateLines(maxlines)
|
||||
flw.SetRotateSize(maxsize)
|
||||
flw.SetRotateDaily(daily)
|
||||
return flw, true
|
||||
}
|
||||
|
||||
func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
maxrecords := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxrecords":
|
||||
maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
xlw := NewXMLLogWriter(file, rotate)
|
||||
xlw.SetRotateLines(maxrecords)
|
||||
xlw.SetRotateSize(maxsize)
|
||||
xlw.SetRotateDaily(daily)
|
||||
return xlw, true
|
||||
}
|
||||
|
||||
func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
|
||||
endpoint := ""
|
||||
protocol := "udp"
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "endpoint":
|
||||
endpoint = strings.Trim(prop.Value, " \r\n")
|
||||
case "protocol":
|
||||
protocol = strings.Trim(prop.Value, " \r\n")
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(endpoint) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewSocketLogWriter(protocol, endpoint), true
|
||||
}
|
||||
14
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
generated
vendored
Normal file
14
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
log := l4g.NewLogger()
|
||||
defer log.Close()
|
||||
log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
}
|
||||
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
generated
vendored
Normal file
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
const (
|
||||
filename = "flw.log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get a new logger instance
|
||||
log := l4g.NewLogger()
|
||||
|
||||
// Create a default logger that is logging messages of FINE or higher
|
||||
log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false))
|
||||
log.Close()
|
||||
|
||||
/* Can also specify manually via the following: (these are the defaults) */
|
||||
flw := l4g.NewFileLogWriter(filename, false)
|
||||
flw.SetFormat("[%D %T] [%L] (%S) %M")
|
||||
flw.SetRotate(false)
|
||||
flw.SetRotateSize(0)
|
||||
flw.SetRotateLines(0)
|
||||
flw.SetRotateDaily(false)
|
||||
log.AddFilter("file", l4g.FINE, flw)
|
||||
|
||||
// Log some experimental messages
|
||||
log.Finest("Everything is created now (notice that I will not be printing to the file)")
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
log.Critical("Time to close out!")
|
||||
|
||||
// Close the log
|
||||
log.Close()
|
||||
|
||||
// Print what was logged to the file (yes, I know I'm skipping error checking)
|
||||
fd, _ := os.Open(filename)
|
||||
in := bufio.NewReader(fd)
|
||||
fmt.Print("Messages logged to file were: (line numbers not included)\n")
|
||||
for lineno := 1; ; lineno++ {
|
||||
line, err := in.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%3d:\t%s", lineno, line)
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
// Remove the file so it's not lying around
|
||||
os.Remove(filename)
|
||||
}
|
||||
42
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
generated
vendored
Normal file
42
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.String("p", "12124", "Port number to listen on")
|
||||
)
|
||||
|
||||
func e(err error) {
|
||||
if err != nil {
|
||||
fmt.Printf("Erroring out: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Bind to the port
|
||||
bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port)
|
||||
e(err)
|
||||
|
||||
// Create listener
|
||||
listener, err := net.ListenUDP("udp", bind)
|
||||
e(err)
|
||||
|
||||
fmt.Printf("Listening to port %s...\n", *port)
|
||||
for {
|
||||
// read into a new buffer
|
||||
buffer := make([]byte, 1024)
|
||||
_, _, err := listener.ReadFrom(buffer)
|
||||
e(err)
|
||||
|
||||
// log to standard output
|
||||
fmt.Println(string(buffer))
|
||||
}
|
||||
}
|
||||
18
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
generated
vendored
Normal file
18
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
log := l4g.NewLogger()
|
||||
log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124"))
|
||||
|
||||
// Run `nc -u -l -p 12124` or similar before you run this to see the following message
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
|
||||
// This makes sure the output stream buffer is written
|
||||
log.Close()
|
||||
}
|
||||
13
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
generated
vendored
Normal file
13
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
// Load the configuration (isn't this easy?)
|
||||
l4g.LoadConfiguration("example.xml")
|
||||
|
||||
// And now we're ready!
|
||||
l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.")
|
||||
l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2)
|
||||
l4g.Info("About that time, eh chaps?")
|
||||
}
|
||||
264
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
generated
vendored
Normal file
264
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/filelog.go
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This log writer sends output to a file
|
||||
type FileLogWriter struct {
|
||||
rec chan *LogRecord
|
||||
rot chan bool
|
||||
|
||||
// The opened file
|
||||
filename string
|
||||
file *os.File
|
||||
|
||||
// The logging format
|
||||
format string
|
||||
|
||||
// File header/trailer
|
||||
header, trailer string
|
||||
|
||||
// Rotate at linecount
|
||||
maxlines int
|
||||
maxlines_curlines int
|
||||
|
||||
// Rotate at size
|
||||
maxsize int
|
||||
maxsize_cursize int
|
||||
|
||||
// Rotate daily
|
||||
daily bool
|
||||
daily_opendate int
|
||||
|
||||
// Keep old logfiles (.001, .002, etc)
|
||||
rotate bool
|
||||
maxbackup int
|
||||
}
|
||||
|
||||
// This is the FileLogWriter's output method
|
||||
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
|
||||
w.rec <- rec
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) Close() {
|
||||
close(w.rec)
|
||||
w.file.Sync()
|
||||
}
|
||||
|
||||
// NewFileLogWriter creates a new LogWriter which writes to the given file and
|
||||
// has rotation enabled if rotate is true.
|
||||
//
|
||||
// If rotate is true, any time a new log file is opened, the old one is renamed
|
||||
// with a .### extension to preserve it. The various Set* methods can be used
|
||||
// to configure log rotation based on lines, size, and daily.
|
||||
//
|
||||
// The standard log-line format is:
|
||||
// [%D %T] [%L] (%S) %M
|
||||
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
w := &FileLogWriter{
|
||||
rec: make(chan *LogRecord, LogBufferLength),
|
||||
rot: make(chan bool),
|
||||
filename: fname,
|
||||
format: "[%D %T] [%L] (%S) %M",
|
||||
rotate: rotate,
|
||||
maxbackup: 999,
|
||||
}
|
||||
|
||||
// open the file for the first time
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.rot:
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
case rec, ok := <-w.rec:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
|
||||
(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
|
||||
(w.daily && now.Day() != w.daily_opendate) {
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the write
|
||||
n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the counts
|
||||
w.maxlines_curlines++
|
||||
w.maxsize_cursize += n
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
// Request that the logs rotate
|
||||
func (w *FileLogWriter) Rotate() {
|
||||
w.rot <- true
|
||||
}
|
||||
|
||||
// If this is called in a threaded context, it MUST be synchronized
|
||||
func (w *FileLogWriter) intRotate() error {
|
||||
// Close any log file that may be open
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
|
||||
// If we are keeping log files, move it to the next available number
|
||||
if w.rotate {
|
||||
_, err := os.Lstat(w.filename)
|
||||
if err == nil { // file exists
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fname := ""
|
||||
if w.daily && time.Now().Day() != w.daily_opendate {
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||
|
||||
for ; err == nil && num <= 999; num++ {
|
||||
fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num)
|
||||
_, err = os.Lstat(fname)
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
|
||||
}
|
||||
} else {
|
||||
num = w.maxbackup - 1
|
||||
for ; num >= 1; num-- {
|
||||
fname = w.filename + fmt.Sprintf(".%d", num)
|
||||
nfname := w.filename + fmt.Sprintf(".%d", num+1)
|
||||
_, err = os.Lstat(fname)
|
||||
if err == nil {
|
||||
os.Rename(fname, nfname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.file.Close()
|
||||
// Rename the file to its newfound home
|
||||
err = os.Rename(w.filename, fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the log file
|
||||
fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.file = fd
|
||||
|
||||
now := time.Now()
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
|
||||
|
||||
// Set the daily open date to the current date
|
||||
w.daily_opendate = now.Day()
|
||||
|
||||
// initialize rotation values
|
||||
w.maxlines_curlines = 0
|
||||
w.maxsize_cursize = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the logging format (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
|
||||
w.format = format
|
||||
return w
|
||||
}
|
||||
|
||||
// Set the logfile header and footer (chainable). Must be called before the first log
|
||||
// message is written. These are formatted similar to the FormatLogRecord (e.g.
|
||||
// you can use %D and %T in your header/footer for date and time).
|
||||
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
|
||||
w.header, w.trailer = head, foot
|
||||
if w.maxlines_curlines == 0 {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at linecount (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
|
||||
w.maxlines = maxlines
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at size (chainable). Must be called before the first log message
|
||||
// is written.
|
||||
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
|
||||
w.maxsize = maxsize
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate daily (chainable). Must be called before the first log message is
|
||||
// written.
|
||||
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
|
||||
w.daily = daily
|
||||
return w
|
||||
}
|
||||
|
||||
// Set max backup files. Must be called before the first log message
|
||||
// is written.
|
||||
func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter {
|
||||
w.maxbackup = maxbackup
|
||||
return w
|
||||
}
|
||||
|
||||
// SetRotate changes whether or not the old logs are kept. (chainable) Must be
|
||||
// called before the first log message is written. If rotate is false, the
|
||||
// files are overwritten; otherwise, they are rotated to another file before the
|
||||
// new log is opened.
|
||||
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
|
||||
w.rotate = rotate
|
||||
return w
|
||||
}
|
||||
|
||||
// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
|
||||
// output XML record log messages instead of line-based ones.
|
||||
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
return NewFileLogWriter(fname, rotate).SetFormat(
|
||||
` <record level="%L">
|
||||
<timestamp>%D %T</timestamp>
|
||||
<source>%S</source>
|
||||
<message>%M</message>
|
||||
</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
|
||||
}
|
||||
484
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
generated
vendored
Normal file
484
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/log4go.go
generated
vendored
Normal file
@@ -0,0 +1,484 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
// Package log4go provides level-based and highly configurable logging.
|
||||
//
|
||||
// Enhanced Logging
|
||||
//
|
||||
// This is inspired by the logging functionality in Java. Essentially, you create a Logger
|
||||
// object and create output filters for it. You can send whatever you want to the Logger,
|
||||
// and it will filter that based on your settings and send it to the outputs. This way, you
|
||||
// can put as much debug code in your program as you want, and when you're done you can filter
|
||||
// out the mundane messages so only the important ones show up.
|
||||
//
|
||||
// Utility functions are provided to make life easier. Here is some example code to get started:
|
||||
//
|
||||
// log := log4go.NewLogger()
|
||||
// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// The first two lines can be combined with the utility NewDefaultLogger:
|
||||
//
|
||||
// log := log4go.NewDefaultLogger(log4go.DEBUG)
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// Usage notes:
|
||||
// - The ConsoleLogWriter does not display the source of the message to standard
|
||||
// output, but the FileLogWriter does.
|
||||
// - The utility functions (Info, Debug, Warn, etc) derive their source from the
|
||||
// calling function, and this incurs extra overhead.
|
||||
//
|
||||
// Changes from 2.0:
|
||||
// - The external interface has remained mostly stable, but a lot of the
|
||||
// internals have been changed, so if you depended on any of this or created
|
||||
// your own LogWriter, then you will probably have to update your code. In
|
||||
// particular, Logger is now a map and ConsoleLogWriter is now a channel
|
||||
// behind-the-scenes, and the LogWrite method no longer has return values.
|
||||
//
|
||||
// Future work: (please let me know if you think I should work on any of these particularly)
|
||||
// - Log file rotation
|
||||
// - Logging configuration files ala log4j
|
||||
// - Have the ability to remove filters?
|
||||
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
|
||||
// for another method of logging
|
||||
// - Add an XML filter type
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version information
|
||||
const (
|
||||
L4G_VERSION = "log4go-v3.0.1"
|
||||
L4G_MAJOR = 3
|
||||
L4G_MINOR = 0
|
||||
L4G_BUILD = 1
|
||||
)
|
||||
|
||||
/****** Constants ******/
|
||||
|
||||
// These are the integer logging levels used by the logger
|
||||
type Level int
|
||||
|
||||
const (
|
||||
FINEST Level = iota
|
||||
FINE
|
||||
DEBUG
|
||||
TRACE
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
CRITICAL
|
||||
)
|
||||
|
||||
// Logging level strings
|
||||
var (
|
||||
levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
if l < 0 || int(l) > len(levelStrings) {
|
||||
return "UNKNOWN"
|
||||
}
|
||||
return levelStrings[int(l)]
|
||||
}
|
||||
|
||||
/****** Variables ******/
|
||||
var (
|
||||
// LogBufferLength specifies how many log messages a particular log4go
|
||||
// logger can buffer at a time before writing them.
|
||||
LogBufferLength = 32
|
||||
)
|
||||
|
||||
/****** LogRecord ******/
|
||||
|
||||
// A LogRecord contains all of the pertinent information for each message
|
||||
type LogRecord struct {
|
||||
Level Level // The log level
|
||||
Created time.Time // The time at which the log message was created (nanoseconds)
|
||||
Source string // The message source
|
||||
Message string // The log message
|
||||
}
|
||||
|
||||
/****** LogWriter ******/
|
||||
|
||||
// This is an interface for anything that should be able to write logs
|
||||
type LogWriter interface {
|
||||
// This will be called to log a LogRecord message.
|
||||
LogWrite(rec *LogRecord)
|
||||
|
||||
// This should clean up anything lingering about the LogWriter, as it is called before
|
||||
// the LogWriter is removed. LogWrite should not be called after Close.
|
||||
Close()
|
||||
}
|
||||
|
||||
/****** Logger ******/
|
||||
|
||||
// A Filter represents the log level below which no log records are written to
|
||||
// the associated LogWriter.
|
||||
type Filter struct {
|
||||
Level Level
|
||||
LogWriter
|
||||
}
|
||||
|
||||
// A Logger represents a collection of Filters through which log messages are
|
||||
// written.
|
||||
type Logger map[string]*Filter
|
||||
|
||||
// Create a new logger.
|
||||
//
|
||||
// DEPRECATED: Use make(Logger) instead.
|
||||
func NewLogger() Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
|
||||
return make(Logger)
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
//
|
||||
// DEPRECATED: use NewDefaultLogger instead.
|
||||
func NewConsoleLogger(lvl Level) Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
func NewDefaultLogger(lvl Level) Logger {
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Closes all log writers in preparation for exiting the program or a
|
||||
// reconfiguration of logging. Calling this is not really imperative, unless
|
||||
// you want to guarantee that all log messages are written. Close removes
|
||||
// all filters (and thus all LogWriters) from the logger.
|
||||
func (log Logger) Close() {
|
||||
// Close all open loggers
|
||||
for name, filt := range log {
|
||||
filt.Close()
|
||||
delete(log, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new LogWriter to the Logger which will only log messages at lvl or
|
||||
// higher. This function should not be called from multiple goroutines.
|
||||
// Returns the logger for chaining.
|
||||
func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
|
||||
log[name] = &Filter{lvl, writer}
|
||||
return log
|
||||
}
|
||||
|
||||
/******* Logging *******/
|
||||
// Send a formatted log message internally
|
||||
func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
msg := format
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: msg,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a closure log message internally
|
||||
func (log Logger) intLogc(lvl Level, closure func() string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: closure(),
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a log message with manual level, source, and message.
|
||||
func (log Logger) Log(lvl Level, source, message string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: source,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Logf logs a formatted log message at the given log level, using the caller as
|
||||
// its source.
|
||||
func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
|
||||
log.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Logc logs a string returned by the closure at the given log level, using the caller as
|
||||
// its source. If no log message would be written, the closure is never called.
|
||||
func (log Logger) Logc(lvl Level, closure func() string) {
|
||||
log.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Finest logs a message at the finest log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fine logs a message at the fine log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug is a utility method for debug log messages.
|
||||
// The behavior of Debug depends on the first argument:
|
||||
// - arg0 is a string
|
||||
// When given a string as the first argument, this behaves like Logf but with
|
||||
// the DEBUG log level: the first argument is interpreted as a format for the
|
||||
// latter arguments.
|
||||
// - arg0 is a func()string
|
||||
// When given a closure of type func()string, this logs the string returned by
|
||||
// the closure iff it will be logged. The closure runs at most one time.
|
||||
// - arg0 is interface{}
|
||||
// When given anything else, the log message will be each of the arguments
|
||||
// formatted with %v and separated by spaces (ala Sprint).
|
||||
func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace logs a message at the trace log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message at the info log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn logs a message at the warning log level and returns the formatted error.
|
||||
// At the warning level and higher, there is no performance benefit if the
|
||||
// message is not actually logged, because all formats are processed and all
|
||||
// closures are executed to format the error message.
|
||||
// See Debug for further explanation of the arguments.
|
||||
func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Error logs a message at the error log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Critical logs a message at the critical log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
126
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/pattlog.go
generated
vendored
Normal file
126
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/pattlog.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
|
||||
FORMAT_SHORT = "[%t %d] [%L] %M"
|
||||
FORMAT_ABBREV = "[%L] %M"
|
||||
)
|
||||
|
||||
type formatCacheType struct {
|
||||
LastUpdateSeconds int64
|
||||
shortTime, shortDate string
|
||||
longTime, longDate string
|
||||
}
|
||||
|
||||
var formatCache = &formatCacheType{}
|
||||
|
||||
// Known format codes:
|
||||
// %T - Time (15:04:05 MST)
|
||||
// %t - Time (15:04)
|
||||
// %D - Date (2006/01/02)
|
||||
// %d - Date (01/02/06)
|
||||
// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
|
||||
// %S - Source
|
||||
// %M - Message
|
||||
// Ignores unknown formats
|
||||
// Recommended: "[%D %T] [%L] (%S) %M"
|
||||
func FormatLogRecord(format string, rec *LogRecord) string {
|
||||
if rec == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if len(format) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(make([]byte, 0, 64))
|
||||
secs := rec.Created.UnixNano() / 1e9
|
||||
|
||||
cache := *formatCache
|
||||
if cache.LastUpdateSeconds != secs {
|
||||
month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
|
||||
hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
|
||||
zone, _ := rec.Created.Zone()
|
||||
updated := &formatCacheType{
|
||||
LastUpdateSeconds: secs,
|
||||
shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
|
||||
shortDate: fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
|
||||
longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
|
||||
longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
|
||||
}
|
||||
cache = *updated
|
||||
formatCache = updated
|
||||
}
|
||||
|
||||
// Split the string into pieces by % signs
|
||||
pieces := bytes.Split([]byte(format), []byte{'%'})
|
||||
|
||||
// Iterate over the pieces, replacing known formats
|
||||
for i, piece := range pieces {
|
||||
if i > 0 && len(piece) > 0 {
|
||||
switch piece[0] {
|
||||
case 'T':
|
||||
out.WriteString(cache.longTime)
|
||||
case 't':
|
||||
out.WriteString(cache.shortTime)
|
||||
case 'D':
|
||||
out.WriteString(cache.longDate)
|
||||
case 'd':
|
||||
out.WriteString(cache.shortDate)
|
||||
case 'L':
|
||||
out.WriteString(levelStrings[rec.Level])
|
||||
case 'S':
|
||||
out.WriteString(rec.Source)
|
||||
case 's':
|
||||
slice := strings.Split(rec.Source, "/")
|
||||
out.WriteString(slice[len(slice)-1])
|
||||
case 'M':
|
||||
out.WriteString(rec.Message)
|
||||
}
|
||||
if len(piece) > 1 {
|
||||
out.Write(piece[1:])
|
||||
}
|
||||
} else if len(piece) > 0 {
|
||||
out.Write(piece)
|
||||
}
|
||||
}
|
||||
out.WriteByte('\n')
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type FormatLogWriter chan *LogRecord
|
||||
|
||||
// This creates a new FormatLogWriter
|
||||
func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
|
||||
records := make(FormatLogWriter, LogBufferLength)
|
||||
go records.run(out, format)
|
||||
return records
|
||||
}
|
||||
|
||||
func (w FormatLogWriter) run(out io.Writer, format string) {
|
||||
for rec := range w {
|
||||
fmt.Fprint(out, FormatLogRecord(format, rec))
|
||||
}
|
||||
}
|
||||
|
||||
// This is the FormatLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (w FormatLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (w FormatLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/socklog.go
generated
vendored
Normal file
57
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/socklog.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// This log writer sends output to a socket
|
||||
type SocketLogWriter chan *LogRecord
|
||||
|
||||
// This is the SocketLogWriter's output method
|
||||
func (w SocketLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
func (w SocketLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
|
||||
func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
|
||||
sock, err := net.Dial(proto, hostport)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if sock != nil && proto == "tcp" {
|
||||
sock.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range w {
|
||||
// Marshall into JSON
|
||||
js, err := json.Marshal(rec)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sock.Write(js)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
||||
49
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/termlog.go
generated
vendored
Normal file
49
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/termlog.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var stdout io.Writer = os.Stdout
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type ConsoleLogWriter struct {
|
||||
format string
|
||||
w chan *LogRecord
|
||||
}
|
||||
|
||||
// This creates a new ConsoleLogWriter
|
||||
func NewConsoleLogWriter() *ConsoleLogWriter {
|
||||
consoleWriter := &ConsoleLogWriter{
|
||||
format: "[%T %D] [%L] (%S) %M",
|
||||
w: make(chan *LogRecord, LogBufferLength),
|
||||
}
|
||||
go consoleWriter.run(stdout)
|
||||
return consoleWriter
|
||||
}
|
||||
func (c *ConsoleLogWriter) SetFormat(format string) {
|
||||
c.format = format
|
||||
}
|
||||
func (c *ConsoleLogWriter) run(out io.Writer) {
|
||||
for rec := range c.w {
|
||||
fmt.Fprint(out, FormatLogRecord(c.format, rec))
|
||||
}
|
||||
}
|
||||
|
||||
// This is the ConsoleLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
|
||||
c.w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (c *ConsoleLogWriter) Close() {
|
||||
close(c.w)
|
||||
time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
|
||||
}
|
||||
278
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/wrapper.go
generated
vendored
Normal file
278
vendor/github.com/mattermost/platform/vendor/github.com/alecthomas/log4go/wrapper.go
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Global Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
Global = NewDefaultLogger(DEBUG)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).LoadConfiguration
|
||||
func LoadConfiguration(filename string) {
|
||||
Global.LoadConfiguration(filename)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).AddFilter
|
||||
func AddFilter(name string, lvl Level, writer LogWriter) {
|
||||
Global.AddFilter(name, lvl, writer)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).Close (closes and removes all logwriters)
|
||||
func Close() {
|
||||
Global.Close()
|
||||
}
|
||||
|
||||
func Crash(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
panic(args)
|
||||
}
|
||||
|
||||
// Logs the given message and crashes the program
|
||||
func Crashf(format string, args ...interface{}) {
|
||||
Global.intLogf(CRITICAL, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exit(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exitf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderr(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderrf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdout(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdoutf(format string, args ...interface{}) {
|
||||
Global.intLogf(INFO, format, args...)
|
||||
}
|
||||
|
||||
// Send a log message manually
|
||||
// Wrapper for (*Logger).Log
|
||||
func Log(lvl Level, source, message string) {
|
||||
Global.Log(lvl, source, message)
|
||||
}
|
||||
|
||||
// Send a formatted log message easily
|
||||
// Wrapper for (*Logger).Logf
|
||||
func Logf(lvl Level, format string, args ...interface{}) {
|
||||
Global.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Send a closure log message
|
||||
// Wrapper for (*Logger).Logc
|
||||
func Logc(lvl Level, closure func() string) {
|
||||
Global.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Utility for finest log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Finest
|
||||
func Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for fine log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Fine
|
||||
func Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for debug log messages
|
||||
// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
|
||||
// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
|
||||
// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
|
||||
// Wrapper for (*Logger).Debug
|
||||
func Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for trace log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Trace
|
||||
func Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for info log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Info
|
||||
func Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Warn
|
||||
func Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Error
|
||||
func Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Critical
|
||||
func Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
897
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/LICENSE.txt
generated
vendored
Normal file
897
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,897 @@
|
||||
Mattermost Licensing
|
||||
|
||||
SOFTWARE LICENSING
|
||||
|
||||
You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE
|
||||
|
||||
- See MIT-COMPILED-LICENSE.md included in compiled versions for details
|
||||
|
||||
You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways:
|
||||
|
||||
1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or
|
||||
2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com
|
||||
|
||||
You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,
|
||||
webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0.
|
||||
|
||||
We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not
|
||||
link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and
|
||||
(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of
|
||||
a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license.
|
||||
|
||||
MATTERMOST TRADEMARK GUIDELINES
|
||||
|
||||
Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark
|
||||
Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions
|
||||
you have about using these trademarks, please email trademark@mattermost.com
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The software is released under the terms of the GNU Affero General Public
|
||||
License, version 3.
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
420
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
420
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
// 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")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// 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
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
|
||||
// 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 cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// 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-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
default:
|
||||
req.Header[k] = vs
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||
}
|
||||
|
||||
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 := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
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 d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(req.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
// cloneTLSConfig clones all public fields except the fields
|
||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||
// config in active use.
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return &tls.Config{
|
||||
Rand: cfg.Rand,
|
||||
Time: cfg.Time,
|
||||
Certificates: cfg.Certificates,
|
||||
NameToCertificate: cfg.NameToCertificate,
|
||||
GetCertificate: cfg.GetCertificate,
|
||||
RootCAs: cfg.RootCAs,
|
||||
NextProtos: cfg.NextProtos,
|
||||
ServerName: cfg.ServerName,
|
||||
ClientAuth: cfg.ClientAuth,
|
||||
ClientCAs: cfg.ClientCAs,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CipherSuites: cfg.CipherSuites,
|
||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||
ClientSessionCache: cfg.ClientSessionCache,
|
||||
MinVersion: cfg.MinVersion,
|
||||
MaxVersion: cfg.MaxVersion,
|
||||
CurvePreferences: cfg.CurvePreferences,
|
||||
}
|
||||
}
|
||||
105
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
105
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
flateWriterPool = sync.Pool{}
|
||||
)
|
||||
|
||||
func decompressNoContextTakeover(r io.Reader) io.Reader {
|
||||
const tail =
|
||||
// Add four bytes as specified in RFC
|
||||
"\x00\x00\xff\xff" +
|
||||
// Add final block to squelch unexpected EOF error from flate reader.
|
||||
"\x01\x00\x00\xff\xff"
|
||||
return flate.NewReader(io.MultiReader(r, strings.NewReader(tail)))
|
||||
}
|
||||
|
||||
func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) {
|
||||
tw := &truncWriter{w: w}
|
||||
i := flateWriterPool.Get()
|
||||
var fw *flate.Writer
|
||||
var err error
|
||||
if i == nil {
|
||||
fw, err = flate.NewWriter(tw, 3)
|
||||
} else {
|
||||
fw = i.(*flate.Writer)
|
||||
fw.Reset(tw)
|
||||
}
|
||||
return &flateWrapper{fw: fw, tw: tw}, err
|
||||
}
|
||||
|
||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||
// stream to another io.Writer.
|
||||
type truncWriter struct {
|
||||
w io.WriteCloser
|
||||
n int
|
||||
p [4]byte
|
||||
}
|
||||
|
||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||
n := 0
|
||||
|
||||
// fill buffer first for simplicity.
|
||||
if w.n < len(w.p) {
|
||||
n = copy(w.p[w.n:], p)
|
||||
p = p[n:]
|
||||
w.n += n
|
||||
if len(p) == 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
m := len(p)
|
||||
if m > len(w.p) {
|
||||
m = len(w.p)
|
||||
}
|
||||
|
||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
copy(w.p[:], w.p[m:])
|
||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||
nn, err := w.w.Write(p[:len(p)-m])
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
type flateWrapper struct {
|
||||
fw *flate.Writer
|
||||
tw *truncWriter
|
||||
}
|
||||
|
||||
func (w *flateWrapper) Write(p []byte) (int, error) {
|
||||
if w.fw == nil {
|
||||
return 0, errWriteClosed
|
||||
}
|
||||
return w.fw.Write(p)
|
||||
}
|
||||
|
||||
func (w *flateWrapper) Close() error {
|
||||
if w.fw == nil {
|
||||
return errWriteClosed
|
||||
}
|
||||
err1 := w.fw.Flush()
|
||||
flateWriterPool.Put(w.fw)
|
||||
w.fw = nil
|
||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||
}
|
||||
err2 := w.tw.w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
1043
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
1043
vendor/github.com/mattermost/platform/vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user