diff options
author | Wim <wim@42.be> | 2023-08-05 20:43:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-05 20:43:19 +0200 |
commit | 56e7bd01ca09ad52b0c4f48f146a20a4f1b78696 (patch) | |
tree | b1355645342667209263cbd355dc0b4254f1e8fe /vendor/github.com | |
parent | 9459495484d6e06a3d46de64fccd8d06f7ccc72c (diff) | |
download | matterbridge-msglm-56e7bd01ca09ad52b0c4f48f146a20a4f1b78696.tar.gz matterbridge-msglm-56e7bd01ca09ad52b0c4f48f146a20a4f1b78696.tar.bz2 matterbridge-msglm-56e7bd01ca09ad52b0c4f48f146a20a4f1b78696.zip |
Diffstat (limited to 'vendor/github.com')
328 files changed, 3493 insertions, 52119 deletions
diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go index 0b571764..5c0fea0c 100644 --- a/vendor/github.com/bwmarrin/discordgo/discord.go +++ b/vendor/github.com/bwmarrin/discordgo/discord.go @@ -22,7 +22,7 @@ import ( ) // VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) -const VERSION = "0.27.0" +const VERSION = "0.27.1" // New creates a new Discord session with provided token. // If the token is for a bot, it must be prefixed with "Bot " diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go index b7d323e7..fe4d2fe8 100644 --- a/vendor/github.com/bwmarrin/discordgo/restapi.go +++ b/vendor/github.com/bwmarrin/discordgo/restapi.go @@ -223,6 +223,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b for _, opt := range options { opt(cfg) } + req = cfg.Request if s.Debug { for k, v := range req.Header { diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go index 1c29a7e9..2dad321c 100644 --- a/vendor/github.com/bwmarrin/discordgo/structs.go +++ b/vendor/github.com/bwmarrin/discordgo/structs.go @@ -527,7 +527,7 @@ type ThreadMember struct { // The time the current user last joined the thread JoinTimestamp time.Time `json:"join_timestamp"` // Any user-thread settings, currently only used for notifications - Flags int + Flags int `json:"flags"` } // ThreadsList represents a list of threads alongisde with thread member objects for the current user. @@ -622,6 +622,7 @@ const ( StickerFormatTypePNG StickerFormat = 1 StickerFormatTypeAPNG StickerFormat = 2 StickerFormatTypeLottie StickerFormat = 3 + StickerFormatTypeGIF StickerFormat = 4 ) // StickerType is the type of sticker. diff --git a/vendor/github.com/bwmarrin/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go index 87e84b12..79ce18e2 100644 --- a/vendor/github.com/bwmarrin/discordgo/voice.go +++ b/vendor/github.com/bwmarrin/discordgo/voice.go @@ -599,44 +599,46 @@ func (v *VoiceConnection) udpOpen() (err error) { return } - // Create a 70 byte array and put the SSRC code from the Op 2 VoiceConnection event - // into it. Then send that over the UDP connection to Discord - sb := make([]byte, 70) - binary.BigEndian.PutUint32(sb, v.op2.SSRC) + // Create a 74 byte array to store the packet data + sb := make([]byte, 74) + binary.BigEndian.PutUint16(sb, 1) // Packet type (0x1 is request, 0x2 is response) + binary.BigEndian.PutUint16(sb[2:], 70) // Packet length (excluding type and length fields) + binary.BigEndian.PutUint32(sb[4:], v.op2.SSRC) // The SSRC code from the Op 2 VoiceConnection event + + // And send that data over the UDP connection to Discord. _, err = v.udpConn.Write(sb) if err != nil { v.log(LogWarning, "udp write error to %s, %s", addr.String(), err) return } - // Create a 70 byte array and listen for the initial handshake response + // Create a 74 byte array and listen for the initial handshake response // from Discord. Once we get it parse the IP and PORT information out // of the response. This should be our public IP and PORT as Discord // saw us. - rb := make([]byte, 70) + rb := make([]byte, 74) rlen, _, err := v.udpConn.ReadFromUDP(rb) if err != nil { v.log(LogWarning, "udp read error, %s, %s", addr.String(), err) return } - if rlen < 70 { + if rlen < 74 { v.log(LogWarning, "received udp packet too small") return fmt.Errorf("received udp packet too small") } - // Loop over position 4 through 20 to grab the IP address - // Should never be beyond position 20. + // Loop over position 8 through 71 to grab the IP address. var ip string - for i := 4; i < 20; i++ { + for i := 8; i < len(rb)-2; i++ { if rb[i] == 0 { break } ip += string(rb[i]) } - // Grab port from position 68 and 69 - port := binary.BigEndian.Uint16(rb[68:70]) + // Grab port from position 72 and 73 + port := binary.BigEndian.Uint16(rb[len(rb)-2:]) // Take the data from above and send it back to Discord to finalize // the UDP connection handshake. diff --git a/vendor/github.com/d5/tengo/v2/parser/parser.go b/vendor/github.com/d5/tengo/v2/parser/parser.go index eaa9dc93..a12af614 100644 --- a/vendor/github.com/d5/tengo/v2/parser/parser.go +++ b/vendor/github.com/d5/tengo/v2/parser/parser.go @@ -143,6 +143,7 @@ func (p *Parser) ParseFile() (file *File, err error) { } stmts := p.parseStmtList() + p.expect(token.EOF) if p.errors.Len() > 0 { return nil, p.errors.Err() } diff --git a/vendor/github.com/d5/tengo/v2/script.go b/vendor/github.com/d5/tengo/v2/script.go index 4cca4d04..16f2944b 100644 --- a/vendor/github.com/d5/tengo/v2/script.go +++ b/vendor/github.com/d5/tengo/v2/script.go @@ -247,8 +247,8 @@ func (c *Compiled) RunContext(ctx context.Context) (err error) { // Clone creates a new copy of Compiled. Cloned copies are safe for concurrent // use by multiple goroutines. func (c *Compiled) Clone() *Compiled { - c.lock.Lock() - defer c.lock.Unlock() + c.lock.RLock() + defer c.lock.RUnlock() clone := &Compiled{ globalIndexes: c.globalIndexes, diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go index 6d468ef0..557fb9ea 100644 --- a/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go @@ -62,9 +62,12 @@ func (d *decodeState) scanNext() { // scanWhile processes bytes in d.data[d.off:] until it // receives a scan code not equal to op. -func (d *decodeState) scanWhile(op int) { +func (d *decodeState) scanWhile(op int) (isFloat bool) { s, data, i := &d.scan, d.data, d.off for i < len(data) { + if data[i] == '.' || data[i] == 'e' || data[i] == 'E' { + isFloat = true + } newOp := s.step(s, data[i]) i++ if newOp != op { @@ -76,6 +79,7 @@ func (d *decodeState) scanWhile(op int) { d.off = len(data) + 1 // mark processed EOF with len+1 d.opcode = d.scan.eof() + return } func (d *decodeState) value() (tengo.Object, error) { @@ -185,7 +189,7 @@ func (d *decodeState) object() (tengo.Object, error) { func (d *decodeState) literal() (tengo.Object, error) { // All bytes inside literal return scanContinue op code. start := d.readIndex() - d.scanWhile(scanContinue) + isFloat := d.scanWhile(scanContinue) item := d.data[start:d.readIndex()] @@ -210,8 +214,12 @@ func (d *decodeState) literal() (tengo.Object, error) { if c != '-' && (c < '0' || c > '9') { panic(phasePanicMsg) } - n, _ := strconv.ParseFloat(string(item), 10) - return &tengo.Float{Value: n}, nil + if isFloat { + n, _ := strconv.ParseFloat(string(item), 10) + return &tengo.Float{Value: n}, nil + } + n, _ := strconv.ParseInt(string(item), 10, 64) + return &tengo.Int{Value: n}, nil } } diff --git a/vendor/github.com/d5/tengo/v2/stdlib/math.go b/vendor/github.com/d5/tengo/v2/stdlib/math.go index 633ea09f..1bd9ba69 100644 --- a/vendor/github.com/d5/tengo/v2/stdlib/math.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/math.go @@ -7,17 +7,31 @@ import ( ) var mathModule = map[string]tengo.Object{ - "e": &tengo.Float{Value: math.E}, - "pi": &tengo.Float{Value: math.Pi}, - "phi": &tengo.Float{Value: math.Phi}, - "sqrt2": &tengo.Float{Value: math.Sqrt2}, - "sqrtE": &tengo.Float{Value: math.SqrtE}, - "sqrtPi": &tengo.Float{Value: math.SqrtPi}, - "sqrtPhi": &tengo.Float{Value: math.SqrtPhi}, - "ln2": &tengo.Float{Value: math.Ln2}, - "log2E": &tengo.Float{Value: math.Log2E}, - "ln10": &tengo.Float{Value: math.Ln10}, - "log10E": &tengo.Float{Value: math.Log10E}, + "e": &tengo.Float{Value: math.E}, + "pi": &tengo.Float{Value: math.Pi}, + "phi": &tengo.Float{Value: math.Phi}, + "sqrt2": &tengo.Float{Value: math.Sqrt2}, + "sqrtE": &tengo.Float{Value: math.SqrtE}, + "sqrtPi": &tengo.Float{Value: math.SqrtPi}, + "sqrtPhi": &tengo.Float{Value: math.SqrtPhi}, + "ln2": &tengo.Float{Value: math.Ln2}, + "log2E": &tengo.Float{Value: math.Log2E}, + "ln10": &tengo.Float{Value: math.Ln10}, + "log10E": &tengo.Float{Value: math.Log10E}, + "maxFloat32": &tengo.Float{Value: math.MaxFloat32}, + "smallestNonzeroFloat32": &tengo.Float{Value: math.SmallestNonzeroFloat32}, + "maxFloat64": &tengo.Float{Value: math.MaxFloat64}, + "smallestNonzeroFloat64": &tengo.Float{Value: math.SmallestNonzeroFloat64}, + "maxInt": &tengo.Int{Value: math.MaxInt}, + "minInt": &tengo.Int{Value: math.MinInt}, + "maxInt8": &tengo.Int{Value: math.MaxInt8}, + "minInt8": &tengo.Int{Value: math.MinInt8}, + "maxInt16": &tengo.Int{Value: math.MaxInt16}, + "minInt16": &tengo.Int{Value: math.MinInt16}, + "maxInt32": &tengo.Int{Value: math.MaxInt32}, + "minInt32": &tengo.Int{Value: math.MinInt32}, + "maxInt64": &tengo.Int{Value: math.MaxInt64}, + "minInt64": &tengo.Int{Value: math.MinInt64}, "abs": &tengo.UserFunction{ Name: "abs", Value: FuncAFRF(math.Abs), diff --git a/vendor/github.com/d5/tengo/v2/stdlib/times.go b/vendor/github.com/d5/tengo/v2/stdlib/times.go index 0b6f7bd4..dc9a1a56 100644 --- a/vendor/github.com/d5/tengo/v2/stdlib/times.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/times.go @@ -180,6 +180,10 @@ var timesModule = map[string]tengo.Object{ Name: "to_utc", Value: timesToUTC, }, // to_utc(time) => time + "in_location": &tengo.UserFunction{ + Name: "in_location", + Value: timesInLocation, + }, // in_location(time, location) => time } func timesSleep(args ...tengo.Object) (ret tengo.Object, err error) { @@ -430,7 +434,7 @@ func timesDate(args ...tengo.Object) ( ret tengo.Object, err error, ) { - if len(args) != 7 { + if len(args) < 7 || len(args) > 8 { err = tengo.ErrWrongNumArguments return } @@ -499,9 +503,29 @@ func timesDate(args ...tengo.Object) ( return } + var loc *time.Location + if len(args) == 8 { + i8, ok := tengo.ToString(args[7]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "eighth", + Expected: "string(compatible)", + Found: args[7].TypeName(), + } + return + } + loc, err = time.LoadLocation(i8) + if err != nil { + ret = wrapError(err) + return + } + } else { + loc = time.Now().Location() + } + ret = &tengo.Time{ Value: time.Date(i1, - time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location()), + time.Month(i2), i3, i4, i5, i6, i7, loc), } return @@ -1113,6 +1137,46 @@ func timesTimeLocation(args ...tengo.Object) ( return } +func timesInLocation(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + location, err := time.LoadLocation(s2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Time{Value: t1.In(location)} + + return +} + func timesTimeString(args ...tengo.Object) (ret tengo.Object, err error) { if len(args) != 1 { err = tengo.ErrWrongNumArguments diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml index ba95cdd1..ac12e485 100644 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: go +go_import_path: github.com/dustin/go-humanize go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x + - stable - master matrix: allow_failures: @@ -15,7 +15,7 @@ matrix: install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . + - go vet . + - go install -v -race ./... - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown index 91b4ae56..7d0b16b3 100644 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -5,7 +5,7 @@ Just a few functions for helping humanize times and sizes. `go get` it as `github.com/dustin/go-humanize`, import it as `"github.com/dustin/go-humanize"`, use it as `humanize`. -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for complete documentation. ## Sizes diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go index 1a2bf617..3b015fd5 100644 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -28,6 +28,10 @@ var ( BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) // BigYiByte is 1,024 z bytes in bit.Ints BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) + // BigRiByte is 1,024 y bytes in bit.Ints + BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) + // BigQiByte is 1,024 r bytes in bit.Ints + BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) ) var ( @@ -51,6 +55,10 @@ var ( BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) // BigYByte is 1,000 SI z bytes in big.Ints BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) + // BigRByte is 1,000 SI y bytes in big.Ints + BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) + // BigQByte is 1,000 SI r bytes in big.Ints + BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) ) var bigBytesSizeTable = map[string]*big.Int{ @@ -71,6 +79,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zb": BigZByte, "yib": BigYiByte, "yb": BigYByte, + "rib": BigRiByte, + "rb": BigRByte, + "qib": BigQiByte, + "qb": BigQByte, // Without suffix "": BigByte, "ki": BigKiByte, @@ -89,6 +101,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zi": BigZiByte, "y": BigYByte, "yi": BigYiByte, + "r": BigRByte, + "ri": BigRiByte, + "q": BigQByte, + "qi": BigQiByte, } var ten = big.NewInt(10) @@ -115,7 +131,7 @@ func humanateBigBytes(s, base *big.Int, sizes []string) string { // // BigBytes(82854982) -> 83 MB func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} return humanateBigBytes(s, bigSIExp, sizes) } @@ -125,7 +141,7 @@ func BigBytes(s *big.Int) string { // // BigIBytes(82854982) -> 79 MiB func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} return humanateBigBytes(s, bigIECExp, sizes) } diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go index 620690de..2bc83a03 100644 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -1,3 +1,4 @@ +//go:build go1.6 // +build go1.6 package humanize diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go index 1c62b640..bce923f3 100644 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -6,6 +6,9 @@ import ( ) func stripTrailingZeros(s string) string { + if !strings.ContainsRune(s, '.') { + return s + } offset := len(s) - 1 for offset > 0 { if s[offset] == '.' { diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go index dec61865..6470d0d4 100644 --- a/vendor/github.com/dustin/go-humanize/number.go +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -73,7 +73,7 @@ func FormatFloat(format string, n float64) string { if n > math.MaxFloat64 { return "Infinity" } - if n < -math.MaxFloat64 { + if n < (0.0 - math.MaxFloat64) { return "-Infinity" } diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go index ae659e0e..8b850198 100644 --- a/vendor/github.com/dustin/go-humanize/si.go +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -8,6 +8,8 @@ import ( ) var siPrefixTable = map[float64]string{ + -30: "q", // quecto + -27: "r", // ronto -24: "y", // yocto -21: "z", // zepto -18: "a", // atto @@ -25,6 +27,8 @@ var siPrefixTable = map[float64]string{ 18: "E", // exa 21: "Z", // zetta 24: "Y", // yotta + 27: "R", // ronna + 30: "Q", // quetta } var revSIPrefixTable = revfmap(siPrefixTable) diff --git a/vendor/github.com/gomarkdown/markdown/.gitpod.yml b/vendor/github.com/gomarkdown/markdown/.gitpod.yml deleted file mode 100644 index dde1f1d9..00000000 --- a/vendor/github.com/gomarkdown/markdown/.gitpod.yml +++ /dev/null @@ -1,9 +0,0 @@ -# This configuration file was automatically generated by Gitpod. -# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) -# and commit this file to your remote git repository to share the goodness with others. - -tasks: - - init: go get && go build ./... && go test ./... - command: go run - - diff --git a/vendor/github.com/gomarkdown/markdown/README.md b/vendor/github.com/gomarkdown/markdown/README.md index a85ce69c..a60b8ba5 100644 --- a/vendor/github.com/gomarkdown/markdown/README.md +++ b/vendor/github.com/gomarkdown/markdown/README.md @@ -6,7 +6,16 @@ Package `github.com/gomarkdown/markdown` is a Go library for parsing Markdown te It's very fast and supports common extensions. -Try code examples online: https://replit.com/@kjk1?path=folder/gomarkdown +Tutorial: https://blog.kowalczyk.info/article/cxn3/advanced-markdown-processing-in-go.html + +Code examples: +* https://onlinetool.io/goplayground/#txO7hJ-ibeU : basic markdown => HTML +* https://onlinetool.io/goplayground/#yFRIWRiu-KL : customize HTML renderer +* https://onlinetool.io/goplayground/#2yV5-HDKBUV : modify AST +* https://onlinetool.io/goplayground/#9fqKwRbuJ04 : customize parser +* https://onlinetool.io/goplayground/#Bk0zTvrzUDR : syntax highlight + +Those examples are also in [examples](./examples) directory. ## API Docs: @@ -15,101 +24,58 @@ Try code examples online: https://replit.com/@kjk1?path=folder/gomarkdown - https://pkg.go.dev/github.com/gomarkdown/markdown/parser : parser - https://pkg.go.dev/github.com/gomarkdown/markdown/html : html renderer -## Users - -Some tools using this package: https://pkg.go.dev/github.com/gomarkdown/markdown?tab=importedby - ## Usage To convert markdown text to HTML using reasonable defaults: ```go -md := []byte("## markdown document") -output := markdown.ToHTML(md, nil, nil) -``` - -Try it online: https://replit.com/@kjk1/gomarkdown-basic - -## Customizing markdown parser +package main -Markdown format is loosely specified and there are multiple extensions invented after original specification was created. - -The parser supports several [extensions](https://pkg.go.dev/github.com/gomarkdown/markdown/parser#Extensions). +import ( + "os" -Default parser uses most common `parser.CommonExtensions` but you can easily use parser with custom extension: + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/ast" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" -```go -import ( - "github.com/gomarkdown/markdown" - "github.com/gomarkdown/markdown/parser" + "fmt" ) -extensions := parser.CommonExtensions | parser.AutoHeadingIDs -parser := parser.NewWithExtensions(extensions) +var mds = `# header -md := []byte("markdown text") -html := markdown.ToHTML(md, parser, nil) -``` - -Try it online: https://replit.com/@kjk1/gomarkdown-customized-html-renderer +Sample text. -## Customizing HTML renderer +[link](http://example.com) +` -Similarly, HTML renderer can be configured with different [options](https://pkg.go.dev/github.com/gomarkdown/markdown/html#RendererOptions) +func mdToHTML(md []byte) []byte { + // create markdown parser with extensions + extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock + p := parser.NewWithExtensions(extensions) + doc := p.Parse(md) -Here's how to use a custom renderer: + // create HTML renderer with extensions + htmlFlags := html.CommonFlags | html.HrefTargetBlank + opts := html.RendererOptions{Flags: htmlFlags} + renderer := html.NewRenderer(opts) -```go -import ( - "github.com/gomarkdown/markdown" - "github.com/gomarkdown/markdown/html" -) + return markdown.Render(doc, renderer) +} -htmlFlags := html.CommonFlags | html.HrefTargetBlank -opts := html.RendererOptions{Flags: htmlFlags} -renderer := html.NewRenderer(opts) +func main() { + md := []byte(mds) + html := mdToHTML(md) -md := []byte("markdown text") -html := markdown.ToHTML(md, nil, renderer) + fmt.Printf("--- Markdown:\n%s\n\n--- HTML:\n%s\n", md, html) +} ``` -Try it online: https://replit.com/@kjk1/gomarkdown-customized-html-renderer - -HTML renderer also supports reusing most of the logic and overriding rendering of only specific nodes. - -You can provide [RenderNodeFunc](https://pkg.go.dev/github.com/gomarkdown/markdown/html#RenderNodeFunc) in [RendererOptions](https://pkg.go.dev/github.com/gomarkdown/markdown/html#RendererOptions). - -The function is called for each node in AST, you can implement custom rendering logic and tell HTML renderer to skip rendering this node. - -Here's the simplest example that drops all code blocks from the output: - -````go -import ( - "github.com/gomarkdown/markdown" - "github.com/gomarkdown/markdown/ast" - "github.com/gomarkdown/markdown/html" -) +Try it online: https://onlinetool.io/goplayground/#txO7hJ-ibeU -// return (ast.GoToNext, true) to tell html renderer to skip rendering this node -// (because you've rendered it) -func renderHookDropCodeBlock(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) { - // skip all nodes that are not CodeBlock nodes - if _, ok := node.(*ast.CodeBlock); !ok { - return ast.GoToNext, false - } - // custom rendering logic for ast.CodeBlock. By doing nothing it won't be - // present in the output - return ast.GoToNext, true -} +For more documentation read [this guide](https://blog.kowalczyk.info/article/cxn3/advanced-markdown-processing-in-go.html) -opts := html.RendererOptions{ - Flags: html.CommonFlags, - RenderNodeHook: renderHookDropCodeBlock, -} -renderer := html.NewRenderer(opts) -md := "test\n```\nthis code block will be dropped from output\n```\ntext" -html := markdown.ToHTML([]byte(md), nil, renderer) -```` +Comparing to other markdown parsers: https://babelmark.github.io/ ## Sanitize untrusted content @@ -129,12 +95,6 @@ maybeUnsafeHTML := markdown.ToHTML(md, nil, nil) html := bluemonday.UGCPolicy().SanitizeBytes(maybeUnsafeHTML) ``` -## Windows / Mac newlines - -The library only supports Unix newlines. If you have markdown text with possibly -Windows / Mac newlines, normalize newlines before calling this library using -`d = markdown.NormalizeNewlines(d)` - ## mdtohtml command-line tool https://github.com/gomarkdown/mdtohtml is a command-line markdown to html @@ -323,26 +283,15 @@ implements the following extensions: - **Mmark support**, see <https://mmark.miek.nl/post/syntax/> for all new syntax elements this adds. -## Todo +## Users -- port https://github.com/russross/blackfriday/issues/348 -- port [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX): - renders output as LaTeX. -- port https://github.com/shurcooL/github_flavored_markdown to markdown -- port [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt, - but for markdown. -- More unit testing -- Improve unicode support. It does not understand all unicode - rules (about what constitutes a letter, a punctuation symbol, - etc.), so it may fail to detect word boundaries correctly in - some instances. It is safe on all utf-8 input. +Some tools using this package: https://pkg.go.dev/github.com/gomarkdown/markdown?tab=importedby ## History -markdown is a fork of v2 of https://github.com/russross/blackfriday that is: +markdown is a fork of v2 of https://github.com/russross/blackfriday. -- actively maintained (sadly in Feb 2018 blackfriday was inactive for 5 months with many bugs and pull requests accumulated) -- refactored API (split into ast/parser/html sub-packages) +I refactored the API (split into ast/parser/html sub-packages). Blackfriday itself was based on C implementation [sundown](https://github.com/vmg/sundown) which in turn was based on [libsoldout](http://fossil.instinctive.eu/libsoldout/home). diff --git a/vendor/github.com/gomarkdown/markdown/ast/node.go b/vendor/github.com/gomarkdown/markdown/ast/node.go index 1d558dd3..8f802db8 100644 --- a/vendor/github.com/gomarkdown/markdown/ast/node.go +++ b/vendor/github.com/gomarkdown/markdown/ast/node.go @@ -92,6 +92,12 @@ type Container struct { *Attribute // Block level attribute } +// return true if can contain children of a given node type +// used by custom nodes to over-ride logic in canNodeContain +type CanContain interface { + CanContain(Node) bool +} + // AsContainer returns itself as *Container func (c *Container) AsContainer() *Container { return c diff --git a/vendor/github.com/gomarkdown/markdown/ast/print.go b/vendor/github.com/gomarkdown/markdown/ast/print.go index b186ec07..a4e3d624 100644 --- a/vendor/github.com/gomarkdown/markdown/ast/print.go +++ b/vendor/github.com/gomarkdown/markdown/ast/print.go @@ -157,6 +157,8 @@ func printRecur(w io.Writer, node Node, prefix string, depth int) { content += "flags=" + flags + " " } printDefault(w, indent, typeName, content) + case *CodeBlock: + printDefault(w, indent, typeName + ":" + string(v.Info), content) default: printDefault(w, indent, typeName, content) } diff --git a/vendor/github.com/gomarkdown/markdown/codecov.yml b/vendor/github.com/gomarkdown/markdown/codecov.yml deleted file mode 100644 index f681ff11..00000000 --- a/vendor/github.com/gomarkdown/markdown/codecov.yml +++ /dev/null @@ -1,8 +0,0 @@ -coverage: - status: - project: - default: - # basic - target: 60% - threshold: 2% - base: auto diff --git a/vendor/github.com/gomarkdown/markdown/html/renderer.go b/vendor/github.com/gomarkdown/markdown/html/renderer.go index fc63c5fe..494e7540 100644 --- a/vendor/github.com/gomarkdown/markdown/html/renderer.go +++ b/vendor/github.com/gomarkdown/markdown/html/renderer.go @@ -88,13 +88,15 @@ type RendererOptions struct { // FootnoteReturnLinks flag is enabled. If blank, the string // <sup>[return]</sup> is used. FootnoteReturnLinkContents string - // CitationFormatString defines how a citation is rendered. If blnck, the string + // CitationFormatString defines how a citation is rendered. If blank, the string // <sup>[%s]</sup> is used. Where %s will be substituted with the citation target. CitationFormatString string // If set, add this text to the front of each Heading ID, to ensure uniqueness. HeadingIDPrefix string // If set, add this text to the back of each Heading ID, to ensure uniqueness. HeadingIDSuffix string + // can over-write <p> for paragraph tag + ParagraphTag string Title string // Document title (used if CompletePage is set) CSS string // Optional CSS file URL (used if CompletePage is set) @@ -120,7 +122,7 @@ type RendererOptions struct { // // Do not create this directly, instead use the NewRenderer function. type Renderer struct { - opts RendererOptions + Opts RendererOptions closeTag string // how to end singleton tags: either " />" or ">" @@ -168,7 +170,7 @@ func EscapeHTML(w io.Writer, d []byte) { } } -func escLink(w io.Writer, text []byte) { +func EscLink(w io.Writer, text []byte) { unesc := html.UnescapeString(string(text)) EscapeHTML(w, []byte(unesc)) } @@ -207,7 +209,7 @@ func NewRenderer(opts RendererOptions) *Renderer { } return &Renderer{ - opts: opts, + Opts: opts, closeTag: closeTag, headingIDs: make(map[string]int), @@ -250,12 +252,12 @@ func isRelativeLink(link []byte) (yes bool) { return false } -func (r *Renderer) addAbsPrefix(link []byte) []byte { - if len(link) == 0 { +func AddAbsPrefix(link []byte, prefix string) []byte { + if len(link) == 0 || len(prefix) == 0 { return link } - if r.opts.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' { - newDest := r.opts.AbsolutePrefix + if isRelativeLink(link) && link[0] != '.' { + newDest := prefix if link[0] != '/' { newDest += "/" } @@ -294,7 +296,7 @@ func isMailto(link []byte) bool { } func needSkipLink(r *Renderer, dest []byte) bool { - flags := r.opts.Flags + flags := r.Opts.Flags if flags&SkipLinks != 0 { return true } @@ -317,7 +319,7 @@ func appendLanguageAttr(attrs []string, info []byte) []string { return append(attrs, s) } -func (r *Renderer) outTag(w io.Writer, name string, attrs []string) { +func (r *Renderer) OutTag(w io.Writer, name string, attrs []string) { s := name if len(attrs) > 0 { s += " " + strings.Join(attrs, " ") @@ -326,22 +328,22 @@ func (r *Renderer) outTag(w io.Writer, name string, attrs []string) { r.lastOutputLen = 1 } -func footnoteRef(prefix string, node *ast.Link) string { - urlFrag := prefix + string(slugify(node.Destination)) +func FootnoteRef(prefix string, node *ast.Link) string { + urlFrag := prefix + string(Slugify(node.Destination)) nStr := strconv.Itoa(node.NoteID) anchor := `<a href="#fn:` + urlFrag + `">` + nStr + `</a>` return `<sup class="footnote-ref" id="fnref:` + urlFrag + `">` + anchor + `</sup>` } -func footnoteItem(prefix string, slug []byte) string { +func FootnoteItem(prefix string, slug []byte) string { return `<li id="fn:` + prefix + string(slug) + `">` } -func footnoteReturnLink(prefix, returnLink string, slug []byte) string { +func FootnoteReturnLink(prefix, returnLink string, slug []byte) string { return ` <a class="footnote-return" href="#fnref:` + prefix + string(slug) + `">` + returnLink + `</a>` } -func listItemOpenCR(listItem *ast.ListItem) bool { +func ListItemOpenCR(listItem *ast.ListItem) bool { if ast.GetPrevNode(listItem) == nil { return false } @@ -349,13 +351,13 @@ func listItemOpenCR(listItem *ast.ListItem) bool { return !ld.Tight && ld.ListFlags&ast.ListTypeDefinition == 0 } -func skipParagraphTags(para *ast.Paragraph) bool { +func SkipParagraphTags(para *ast.Paragraph) bool { parent := para.Parent grandparent := parent.GetParent() - if grandparent == nil || !isList(grandparent) { + if grandparent == nil || !IsList(grandparent) { return false } - isParentTerm := isListItemTerm(parent) + isParentTerm := IsListItemTerm(parent) grandparentListData := grandparent.(*ast.List) tightOrTerm := grandparentListData.Tight || isParentTerm return tightOrTerm @@ -391,35 +393,35 @@ var ( closeHTags = []string{"</h1>", "</h2>", "</h3>", "</h4>", "</h5>"} ) -func headingOpenTagFromLevel(level int) string { +func HeadingOpenTagFromLevel(level int) string { if level < 1 || level > 5 { return "<h6" } return openHTags[level-1] } -func headingCloseTagFromLevel(level int) string { +func HeadingCloseTagFromLevel(level int) string { if level < 1 || level > 5 { return "</h6>" } return closeHTags[level-1] } -func (r *Renderer) outHRTag(w io.Writer, attrs []string) { +func (r *Renderer) OutHRTag(w io.Writer, attrs []string) { hr := TagWithAttributes("<hr", attrs) - r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, hr, "<hr />") + r.OutOneOf(w, r.Opts.Flags&UseXHTML == 0, hr, "<hr />") } // Text writes ast.Text node func (r *Renderer) Text(w io.Writer, text *ast.Text) { - if r.opts.Flags&Smartypants != 0 { + if r.Opts.Flags&Smartypants != 0 { var tmp bytes.Buffer EscapeHTML(&tmp, text.Literal) r.sr.Process(w, tmp.Bytes()) } else { _, parentIsLink := text.Parent.(*ast.Link) if parentIsLink { - escLink(w, text.Literal) + EscLink(w, text.Literal) } else { EscapeHTML(w, text.Literal) } @@ -428,7 +430,7 @@ func (r *Renderer) Text(w io.Writer, text *ast.Text) { // HardBreak writes ast.Hardbreak node func (r *Renderer) HardBreak(w io.Writer, node *ast.Hardbreak) { - r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, "<br>", "<br />") + r.OutOneOf(w, r.Opts.Flags&UseXHTML == 0, "<br>", "<br />") r.CR(w) } @@ -459,7 +461,7 @@ func (r *Renderer) OutOneOfCr(w io.Writer, outFirst bool, first string, second s // HTMLSpan writes ast.HTMLSpan node func (r *Renderer) HTMLSpan(w io.Writer, span *ast.HTMLSpan) { - if r.opts.Flags&SkipHTML == 0 { + if r.Opts.Flags&SkipHTML == 0 { r.Out(w, span.Literal) } } @@ -467,18 +469,18 @@ func (r *Renderer) HTMLSpan(w io.Writer, span *ast.HTMLSpan) { func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) { attrs := link.AdditionalAttributes dest := link.Destination - dest = r.addAbsPrefix(dest) + dest = AddAbsPrefix(dest, r.Opts.AbsolutePrefix) var hrefBuf bytes.Buffer hrefBuf.WriteString("href=\"") - escLink(&hrefBuf, dest) + EscLink(&hrefBuf, dest) hrefBuf.WriteByte('"') attrs = append(attrs, hrefBuf.String()) if link.NoteID != 0 { - r.Outs(w, footnoteRef(r.opts.FootnoteAnchorPrefix, link)) + r.Outs(w, FootnoteRef(r.Opts.FootnoteAnchorPrefix, link)) return } - attrs = appendLinkAttrs(attrs, r.opts.Flags, dest) + attrs = appendLinkAttrs(attrs, r.Opts.Flags, dest) if len(link.Title) > 0 { var titleBuff bytes.Buffer titleBuff.WriteString("title=\"") @@ -486,7 +488,7 @@ func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) { titleBuff.WriteByte('"') attrs = append(attrs, titleBuff.String()) } - r.outTag(w, "<a", attrs) + r.OutTag(w, "<a", attrs) } func (r *Renderer) linkExit(w io.Writer, link *ast.Link) { @@ -511,33 +513,34 @@ func (r *Renderer) Link(w io.Writer, link *ast.Link, entering bool) { } func (r *Renderer) imageEnter(w io.Writer, image *ast.Image) { - dest := image.Destination - dest = r.addAbsPrefix(dest) - if r.DisableTags == 0 { - //if options.safe && potentiallyUnsafe(dest) { - //out(w, `<img src="" alt="`) - //} else { - if r.opts.Flags&LazyLoadImages != 0 { - r.Outs(w, `<img loading="lazy" src="`) - } else { - r.Outs(w, `<img src="`) - } - escLink(w, dest) - r.Outs(w, `" alt="`) - //} - } r.DisableTags++ + if r.DisableTags > 1 { + return + } + src := image.Destination + src = AddAbsPrefix(src, r.Opts.AbsolutePrefix) + attrs := BlockAttrs(image) + if r.Opts.Flags&LazyLoadImages != 0 { + attrs = append(attrs, `loading="lazy"`) + } + + s := TagWithAttributes("<img", attrs) + s = s[:len(s)-1] // hackish: strip off ">" from end + r.Outs(w, s+` src="`) + EscLink(w, src) + r.Outs(w, `" alt="`) } func (r *Renderer) imageExit(w io.Writer, image *ast.Image) { r.DisableTags-- - if r.DisableTags == 0 { - if image.Title != nil { - r.Outs(w, `" title="`) - EscapeHTML(w, image.Title) - } - r.Outs(w, `" />`) + if r.DisableTags > 0 { + return + } + if image.Title != nil { + r.Outs(w, `" title="`) + EscapeHTML(w, image.Title) } + r.Outs(w, `" />`) } // Image writes ast.Image node @@ -571,20 +574,28 @@ func (r *Renderer) paragraphEnter(w io.Writer, para *ast.Paragraph) { } } - tag := TagWithAttributes("<p", BlockAttrs(para)) + ptag := "<p" + if r.Opts.ParagraphTag != "" { + ptag = "<" + r.Opts.ParagraphTag + } + tag := TagWithAttributes(ptag, BlockAttrs(para)) r.Outs(w, tag) } func (r *Renderer) paragraphExit(w io.Writer, para *ast.Paragraph) { - r.Outs(w, "</p>") - if !(isListItem(para.Parent) && ast.GetNextNode(para) == nil) { + ptag := "</p>" + if r.Opts.ParagraphTag != "" { + ptag = "</" + r.Opts.ParagraphTag + ">" + } + r.Outs(w, ptag) + if !(IsListItem(para.Parent) && ast.GetNextNode(para) == nil) { r.CR(w) } } // Paragraph writes ast.Paragraph node func (r *Renderer) Paragraph(w io.Writer, para *ast.Paragraph, entering bool) { - if skipParagraphTags(para) { + if SkipParagraphTags(para) { return } if entering { @@ -603,7 +614,7 @@ func (r *Renderer) Code(w io.Writer, node *ast.Code) { // HTMLBlock write ast.HTMLBlock node func (r *Renderer) HTMLBlock(w io.Writer, node *ast.HTMLBlock) { - if r.opts.Flags&SkipHTML != 0 { + if r.Opts.Flags&SkipHTML != 0 { return } r.CR(w) @@ -611,6 +622,25 @@ func (r *Renderer) HTMLBlock(w io.Writer, node *ast.HTMLBlock) { r.CR(w) } +func (r *Renderer) EnsureUniqueHeadingID(id string) string { + for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] { + tmp := fmt.Sprintf("%s-%d", id, count+1) + + if _, tmpFound := r.headingIDs[tmp]; !tmpFound { + r.headingIDs[id] = count + 1 + id = tmp + } else { + id = id + "-1" + } + } + + if _, found := r.headingIDs[id]; !found { + r.headingIDs[id] = 0 + } + + return id +} + func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) { var attrs []string var class string @@ -629,44 +659,25 @@ func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) { attrs = []string{`class="` + class + `"`} } - ensureUniqueHeadingID := func(id string) string { - for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] { - tmp := fmt.Sprintf("%s-%d", id, count+1) - - if _, tmpFound := r.headingIDs[tmp]; !tmpFound { - r.headingIDs[id] = count + 1 - id = tmp - } else { - id = id + "-1" - } - } - - if _, found := r.headingIDs[id]; !found { - r.headingIDs[id] = 0 - } - - return id - } - if nodeData.HeadingID != "" { - id := ensureUniqueHeadingID(nodeData.HeadingID) - if r.opts.HeadingIDPrefix != "" { - id = r.opts.HeadingIDPrefix + id + id := r.EnsureUniqueHeadingID(nodeData.HeadingID) + if r.Opts.HeadingIDPrefix != "" { + id = r.Opts.HeadingIDPrefix + id } - if r.opts.HeadingIDSuffix != "" { - id = id + r.opts.HeadingIDSuffix + if r.Opts.HeadingIDSuffix != "" { + id = id + r.Opts.HeadingIDSuffix } attrID := `id="` + id + `"` attrs = append(attrs, attrID) } attrs = append(attrs, BlockAttrs(nodeData)...) r.CR(w) - r.outTag(w, headingOpenTagFromLevel(nodeData.Level), attrs) + r.OutTag(w, HeadingOpenTagFromLevel(nodeData.Level), attrs) } func (r *Renderer) headingExit(w io.Writer, heading *ast.Heading) { - r.Outs(w, headingCloseTagFromLevel(heading.Level)) - if !(isListItem(heading.Parent) && ast.GetNextNode(heading) == nil) { + r.Outs(w, HeadingCloseTagFromLevel(heading.Level)) + if !(IsListItem(heading.Parent) && ast.GetNextNode(heading) == nil) { r.CR(w) } } @@ -683,7 +694,7 @@ func (r *Renderer) Heading(w io.Writer, node *ast.Heading, entering bool) { // HorizontalRule writes ast.HorizontalRule node func (r *Renderer) HorizontalRule(w io.Writer, node *ast.HorizontalRule) { r.CR(w) - r.outHRTag(w, BlockAttrs(node)) + r.OutHRTag(w, BlockAttrs(node)) r.CR(w) } @@ -693,15 +704,15 @@ func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) { if nodeData.IsFootnotesList { r.Outs(w, "\n<div class=\"footnotes\">\n\n") - if r.opts.Flags&FootnoteNoHRTag == 0 { - r.outHRTag(w, nil) + if r.Opts.Flags&FootnoteNoHRTag == 0 { + r.OutHRTag(w, nil) r.CR(w) } } r.CR(w) - if isListItem(nodeData.Parent) { + if IsListItem(nodeData.Parent) { grand := nodeData.Parent.GetParent() - if isListTight(grand) { + if IsListTight(grand) { r.CR(w) } } @@ -717,7 +728,7 @@ func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) { openTag = "<dl" } attrs = append(attrs, BlockAttrs(nodeData)...) - r.outTag(w, openTag, attrs) + r.OutTag(w, openTag, attrs) r.CR(w) } @@ -760,12 +771,12 @@ func (r *Renderer) List(w io.Writer, list *ast.List, entering bool) { } func (r *Renderer) listItemEnter(w io.Writer, listItem *ast.ListItem) { - if listItemOpenCR(listItem) { + if ListItemOpenCR(listItem) { r.CR(w) } if listItem.RefLink != nil { - slug := slugify(listItem.RefLink) - r.Outs(w, footnoteItem(r.opts.FootnoteAnchorPrefix, slug)) + slug := Slugify(listItem.RefLink) + r.Outs(w, FootnoteItem(r.Opts.FootnoteAnchorPrefix, slug)) return } @@ -780,11 +791,11 @@ func (r *Renderer) listItemEnter(w io.Writer, listItem *ast.ListItem) { } func (r *Renderer) listItemExit(w io.Writer, listItem *ast.ListItem) { - if listItem.RefLink != nil && r.opts.Flags&FootnoteReturnLinks != 0 { - slug := slugify(listItem.RefLink) - prefix := r.opts.FootnoteAnchorPrefix - link := r.opts.FootnoteReturnLinkContents - s := footnoteReturnLink(prefix, link, slug) + if listItem.RefLink != nil && r.Opts.Flags&FootnoteReturnLinks != 0 { + slug := Slugify(listItem.RefLink) + prefix := r.Opts.FootnoteAnchorPrefix + link := r.Opts.FootnoteReturnLinkContents + s := FootnoteReturnLink(prefix, link, slug) r.Outs(w, s) } @@ -815,7 +826,7 @@ func (r *Renderer) EscapeHTMLCallouts(w io.Writer, d []byte) { ld := len(d) Parse: for i := 0; i < ld; i++ { - for _, comment := range r.opts.Comments { + for _, comment := range r.Opts.Comments { if !bytes.HasPrefix(d[i:], comment) { break } @@ -853,14 +864,14 @@ func (r *Renderer) CodeBlock(w io.Writer, codeBlock *ast.CodeBlock) { r.Outs(w, "<pre>") code := TagWithAttributes("<code", attrs) r.Outs(w, code) - if r.opts.Comments != nil { + if r.Opts.Comments != nil { r.EscapeHTMLCallouts(w, codeBlock.Literal) } else { EscapeHTML(w, codeBlock.Literal) } r.Outs(w, "</code>") r.Outs(w, "</pre>") - if !isListItem(codeBlock.Parent) { + if !IsListItem(codeBlock.Parent) { r.CR(w) } } @@ -910,7 +921,7 @@ func (r *Renderer) TableCell(w io.Writer, tableCell *ast.TableCell, entering boo if ast.GetPrevNode(tableCell) == nil { r.CR(w) } - r.outTag(w, openTag, attrs) + r.OutTag(w, openTag, attrs) } // TableBody writes ast.TableBody node @@ -959,8 +970,8 @@ func (r *Renderer) Citation(w io.Writer, node *ast.Citation) { case ast.CitationTypeSuppressed: attr[0] = `class="suppressed"` } - r.outTag(w, "<cite", attr) - r.Outs(w, fmt.Sprintf(`<a href="#%s">`+r.opts.CitationFormatString+`</a>`, c, c)) + r.OutTag(w, "<cite", attr) + r.Outs(w, fmt.Sprintf(`<a href="#%s">`+r.Opts.CitationFormatString+`</a>`, c, c)) r.Outs(w, "</cite>") } } @@ -968,7 +979,7 @@ func (r *Renderer) Citation(w io.Writer, node *ast.Citation) { // Callout writes ast.Callout node func (r *Renderer) Callout(w io.Writer, node *ast.Callout) { attr := []string{`class="callout"`} - r.outTag(w, "<span", attr) + r.OutTag(w, "<span", attr) r.Out(w, node.ID) r.Outs(w, "</span>") } @@ -977,14 +988,14 @@ func (r *Renderer) Callout(w io.Writer, node *ast.Callout) { func (r *Renderer) Index(w io.Writer, node *ast.Index) { // there is no in-text representation. attr := []string{`class="index"`, fmt.Sprintf(`id="%s"`, node.ID)} - r.outTag(w, "<span", attr) + r.OutTag(w, "<span", attr) r.Outs(w, "</span>") } // RenderNode renders a markdown node to HTML func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.WalkStatus { - if r.opts.RenderNodeHook != nil { - status, didHandle := r.opts.RenderNodeHook(w, node, entering) + if r.Opts.RenderNodeHook != nil { + status, didHandle := r.Opts.RenderNodeHook(w, node, entering) if didHandle { return status } @@ -1019,7 +1030,7 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal case *ast.Citation: r.Citation(w, node) case *ast.Image: - if r.opts.Flags&SkipImages != 0 { + if r.Opts.Flags&SkipImages != 0 { return ast.SkipChildren } r.Image(w, node, entering) @@ -1098,7 +1109,7 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal // RenderHeader writes HTML document preamble and TOC if requested. func (r *Renderer) RenderHeader(w io.Writer, ast ast.Node) { r.writeDocumentHeader(w) - if r.opts.Flags&TOC != 0 { + if r.Opts.Flags&TOC != 0 { r.writeTOC(w, ast) } } @@ -1109,18 +1120,18 @@ func (r *Renderer) RenderFooter(w io.Writer, _ ast.Node) { r.Outs(w, "</section>\n") } - if r.opts.Flags&CompletePage == 0 { + if r.Opts.Flags&CompletePage == 0 { return } io.WriteString(w, "\n</body>\n</html>\n") } func (r *Renderer) writeDocumentHeader(w io.Writer) { - if r.opts.Flags&CompletePage == 0 { + if r.Opts.Flags&CompletePage == 0 { return } ending := "" - if r.opts.Flags&UseXHTML != 0 { + if r.Opts.Flags&UseXHTML != 0 { io.WriteString(w, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ") io.WriteString(w, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n") io.WriteString(w, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n") @@ -1131,35 +1142,35 @@ func (r *Renderer) writeDocumentHeader(w io.Writer) { } io.WriteString(w, "<head>\n") io.WriteString(w, " <title>") - if r.opts.Flags&Smartypants != 0 { - r.sr.Process(w, []byte(r.opts.Title)) + if r.Opts.Flags&Smartypants != 0 { + r.sr.Process(w, []byte(r.Opts.Title)) } else { - EscapeHTML(w, []byte(r.opts.Title)) + EscapeHTML(w, []byte(r.Opts.Title)) } io.WriteString(w, "</title>\n") - io.WriteString(w, r.opts.Generator) + io.WriteString(w, r.Opts.Generator) io.WriteString(w, "\"") io.WriteString(w, ending) io.WriteString(w, ">\n") io.WriteString(w, " <meta charset=\"utf-8\"") io.WriteString(w, ending) io.WriteString(w, ">\n") - if r.opts.CSS != "" { + if r.Opts.CSS != "" { io.WriteString(w, " <link rel=\"stylesheet\" type=\"text/css\" href=\"") - EscapeHTML(w, []byte(r.opts.CSS)) + EscapeHTML(w, []byte(r.Opts.CSS)) io.WriteString(w, "\"") io.WriteString(w, ending) io.WriteString(w, ">\n") } - if r.opts.Icon != "" { + if r.Opts.Icon != "" { io.WriteString(w, " <link rel=\"icon\" type=\"image/x-icon\" href=\"") - EscapeHTML(w, []byte(r.opts.Icon)) + EscapeHTML(w, []byte(r.Opts.Icon)) io.WriteString(w, "\"") io.WriteString(w, ending) io.WriteString(w, ">\n") } - if r.opts.Head != nil { - w.Write(r.opts.Head) + if r.Opts.Head != nil { + w.Write(r.Opts.Head) } io.WriteString(w, "</head>\n") io.WriteString(w, "<body>\n\n") @@ -1221,31 +1232,31 @@ func (r *Renderer) writeTOC(w io.Writer, doc ast.Node) { r.lastOutputLen = buf.Len() } -func isList(node ast.Node) bool { +func IsList(node ast.Node) bool { _, ok := node.(*ast.List) return ok } -func isListTight(node ast.Node) bool { +func IsListTight(node ast.Node) bool { if list, ok := node.(*ast.List); ok { return list.Tight } return false } -func isListItem(node ast.Node) bool { +func IsListItem(node ast.Node) bool { _, ok := node.(*ast.ListItem) return ok } -func isListItemTerm(node ast.Node) bool { +func IsListItemTerm(node ast.Node) bool { data, ok := node.(*ast.ListItem) return ok && data.ListFlags&ast.ListTypeTerm != 0 } // TODO: move to internal package // Create a url-safe slug for fragments -func slugify(in []byte) []byte { +func Slugify(in []byte) []byte { if len(in) == 0 { return in } diff --git a/vendor/github.com/gomarkdown/markdown/markdown.go b/vendor/github.com/gomarkdown/markdown/markdown.go index 537eb27b..2fb73c1e 100644 --- a/vendor/github.com/gomarkdown/markdown/markdown.go +++ b/vendor/github.com/gomarkdown/markdown/markdown.go @@ -84,28 +84,7 @@ func ToHTML(markdown []byte, p *parser.Parser, renderer Renderer) []byte { return Render(doc, renderer) } -// NormalizeNewlines converts Windows and Mac newlines to Unix newlines -// The parser only supports Unix newlines. If your mardown content +// NormalizeNewlines converts Windows and Mac newlines to Unix newlines. +// The parser only supports Unix newlines. If your markdown content // might contain Windows or Mac newlines, use this function to convert to Unix newlines -func NormalizeNewlines(d []byte) []byte { - wi := 0 - n := len(d) - for i := 0; i < n; i++ { - c := d[i] - // 13 is CR - if c != 13 { - d[wi] = c - wi++ - continue - } - // replace CR (mac / win) with LF (unix) - d[wi] = 10 - wi++ - if i < n-1 && d[i+1] == 10 { - // this was CRLF, so skip the LF - i++ - } - - } - return d[:wi] -} +var NormalizeNewlines = parser.NormalizeNewlines diff --git a/vendor/github.com/gomarkdown/markdown/parser/aside.go b/vendor/github.com/gomarkdown/markdown/parser/aside.go index 96e25fe0..9d02ed04 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/aside.go +++ b/vendor/github.com/gomarkdown/markdown/parser/aside.go @@ -25,13 +25,13 @@ func (p *Parser) asidePrefix(data []byte) int { // aside ends with at least one blank line // followed by something without a aside prefix func (p *Parser) terminateAside(data []byte, beg, end int) bool { - if p.isEmpty(data[beg:]) <= 0 { + if IsEmpty(data[beg:]) <= 0 { return false } if end >= len(data) { return true } - return p.asidePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0 + return p.asidePrefix(data[end:]) == 0 && IsEmpty(data[end:]) == 0 } // parse a aside fragment @@ -66,8 +66,8 @@ func (p *Parser) aside(data []byte) int { beg = end } - block := p.addBlock(&ast.Aside{}) - p.block(raw.Bytes()) - p.finalize(block) + block := p.AddBlock(&ast.Aside{}) + p.Block(raw.Bytes()) + p.Finalize(block) return end } diff --git a/vendor/github.com/gomarkdown/markdown/parser/block.go b/vendor/github.com/gomarkdown/markdown/parser/block.go index 490871c7..028ae758 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/block.go +++ b/vendor/github.com/gomarkdown/markdown/parser/block.go @@ -103,10 +103,10 @@ func sanitizeHeadingID(text string) string { return string(anchorName) } -// Parse block-level data. +// Parse Block-level data. // Note: this function and many that it calls assume that // the input buffer ends with a newline. -func (p *Parser) block(data []byte) { +func (p *Parser) Block(data []byte) { // this is called recursively: enforce a maximum depth if p.nesting >= p.maxNesting { return @@ -142,7 +142,7 @@ func (p *Parser) block(data []byte) { } } p.includeStack.Push(path) - p.block(included) + p.Block(included) p.includeStack.Pop() data = data[consumed:] continue @@ -156,10 +156,10 @@ func (p *Parser) block(data []byte) { data = data[consumed:] if node != nil { - p.addBlock(node) + p.AddBlock(node) if blockdata != nil { - p.block(blockdata) - p.finalize(node) + p.Block(blockdata) + p.Finalize(node) } } continue @@ -213,7 +213,7 @@ func (p *Parser) block(data []byte) { } // blank lines. note: returns the # of bytes to skip - if i := p.isEmpty(data); i > 0 { + if i := IsEmpty(data); i > 0 { data = data[i:] continue } @@ -255,11 +255,11 @@ func (p *Parser) block(data []byte) { // ****** // or // ______ - if p.isHRule(data) { + if isHRule(data) { i := skipUntilChar(data, 0, '\n') hr := ast.HorizontalRule{} hr.Literal = bytes.Trim(data[:i], " \n") - p.addBlock(&hr) + p.AddBlock(&hr) data = data[i:] continue } @@ -377,7 +377,7 @@ func (p *Parser) block(data []byte) { p.nesting-- } -func (p *Parser) addBlock(n ast.Node) ast.Node { +func (p *Parser) AddBlock(n ast.Node) ast.Node { p.closeUnmatchedBlocks() if p.attr != nil { @@ -448,7 +448,7 @@ func (p *Parser) prefixHeading(data []byte) int { p.allHeadingsWithAutoID = append(p.allHeadingsWithAutoID, block) } block.Content = data[i:end] - p.addBlock(block) + p.AddBlock(block) } return skip } @@ -521,7 +521,7 @@ func (p *Parser) prefixSpecialHeading(data []byte) int { } block.Literal = data[i:end] block.Content = data[i:end] - p.addBlock(block) + p.AddBlock(block) } return skip } @@ -572,7 +572,7 @@ func (p *Parser) titleBlock(data []byte, doRender bool) int { IsTitleblock: true, } block.Content = data - p.addBlock(block) + p.AddBlock(block) return consumed } @@ -617,14 +617,14 @@ func (p *Parser) html(data []byte, doRender bool) int { } // see if it is the only thing on the line - if skip := p.isEmpty(data[j:]); skip > 0 { + if skip := IsEmpty(data[j:]); skip > 0 { // see if it is followed by a blank line/eof j += skip if j >= len(data) { found = true i = j } else { - if skip := p.isEmpty(data[j:]); skip > 0 { + if skip := IsEmpty(data[j:]); skip > 0 { j += skip found = true i = j @@ -667,7 +667,7 @@ func (p *Parser) html(data []byte, doRender bool) int { // trim newlines end := backChar(data, i, '\n') htmlBLock := &ast.HTMLBlock{Leaf: ast.Leaf{Content: data[:end]}} - p.addBlock(htmlBLock) + p.AddBlock(htmlBLock) finalizeHTMLBlock(htmlBLock) } @@ -683,13 +683,13 @@ func finalizeHTMLBlock(block *ast.HTMLBlock) { func (p *Parser) htmlComment(data []byte, doRender bool) int { i := p.inlineHTMLComment(data) // needs to end with a blank line - if j := p.isEmpty(data[i:]); j > 0 { + if j := IsEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim trailing newlines end := backChar(data, size, '\n') htmlBLock := &ast.HTMLBlock{Leaf: ast.Leaf{Content: data[:end]}} - p.addBlock(htmlBLock) + p.AddBlock(htmlBLock) finalizeHTMLBlock(htmlBLock) } return size @@ -715,13 +715,13 @@ func (p *Parser) htmlHr(data []byte, doRender bool) int { } if i < len(data) && data[i] == '>' { i++ - if j := p.isEmpty(data[i:]); j > 0 { + if j := IsEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim newlines end := backChar(data, size, '\n') htmlBlock := &ast.HTMLBlock{Leaf: ast.Leaf{Content: data[:end]}} - p.addBlock(htmlBlock) + p.AddBlock(htmlBlock) finalizeHTMLBlock(htmlBlock) } return size @@ -753,7 +753,7 @@ func (p *Parser) htmlFindEnd(tag string, data []byte) int { // check that the rest of the line is blank skip := 0 - if skip = p.isEmpty(data[i:]); skip == 0 { + if skip = IsEmpty(data[i:]); skip == 0 { return 0 } i += skip @@ -766,7 +766,7 @@ func (p *Parser) htmlFindEnd(tag string, data []byte) int { if p.extensions&LaxHTMLBlocks != 0 { return i } - if skip = p.isEmpty(data[i:]); skip == 0 { + if skip = IsEmpty(data[i:]); skip == 0 { // following line must be blank return 0 } @@ -774,7 +774,7 @@ func (p *Parser) htmlFindEnd(tag string, data []byte) int { return i + skip } -func (*Parser) isEmpty(data []byte) int { +func IsEmpty(data []byte) int { // it is okay to call isEmpty on an empty buffer if len(data) == 0 { return 0 @@ -790,7 +790,7 @@ func (*Parser) isEmpty(data []byte) int { return i } -func (*Parser) isHRule(data []byte) bool { +func isHRule(data []byte) bool { i := 0 // skip up to three spaces @@ -976,7 +976,7 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int { codeBlock.Content = work.Bytes() // TODO: get rid of temp buffer if p.extensions&Mmark == 0 { - p.addBlock(codeBlock) + p.AddBlock(codeBlock) finalizeCodeBlock(codeBlock) return beg } @@ -988,12 +988,12 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int { figure.HeadingID = id p.Inline(caption, captionContent) - p.addBlock(figure) + p.AddBlock(figure) codeBlock.AsLeaf().Attribute = figure.AsContainer().Attribute p.addChild(codeBlock) finalizeCodeBlock(codeBlock) p.addChild(caption) - p.finalize(figure) + p.Finalize(figure) beg += consumed @@ -1001,7 +1001,7 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int { } // Still here, normal block - p.addBlock(codeBlock) + p.AddBlock(codeBlock) finalizeCodeBlock(codeBlock) } @@ -1055,13 +1055,13 @@ func (p *Parser) quotePrefix(data []byte) int { // blockquote ends with at least one blank line // followed by something without a blockquote prefix func (p *Parser) terminateBlockquote(data []byte, beg, end int) bool { - if p.isEmpty(data[beg:]) <= 0 { + if IsEmpty(data[beg:]) <= 0 { return false } if end >= len(data) { return true } - return p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0 + return p.quotePrefix(data[end:]) == 0 && IsEmpty(data[end:]) == 0 } // parse a blockquote fragment @@ -1096,9 +1096,9 @@ func (p *Parser) quote(data []byte) int { } if p.extensions&Mmark == 0 { - block := p.addBlock(&ast.BlockQuote{}) - p.block(raw.Bytes()) - p.finalize(block) + block := p.AddBlock(&ast.BlockQuote{}) + p.Block(raw.Bytes()) + p.Finalize(block) return end } @@ -1108,24 +1108,24 @@ func (p *Parser) quote(data []byte) int { figure.HeadingID = id p.Inline(caption, captionContent) - p.addBlock(figure) // this discard any attributes + p.AddBlock(figure) // this discard any attributes block := &ast.BlockQuote{} block.AsContainer().Attribute = figure.AsContainer().Attribute p.addChild(block) - p.block(raw.Bytes()) - p.finalize(block) + p.Block(raw.Bytes()) + p.Finalize(block) p.addChild(caption) - p.finalize(figure) + p.Finalize(figure) end += consumed return end } - block := p.addBlock(&ast.BlockQuote{}) - p.block(raw.Bytes()) - p.finalize(block) + block := p.AddBlock(&ast.BlockQuote{}) + p.Block(raw.Bytes()) + p.Finalize(block) return end } @@ -1152,7 +1152,7 @@ func (p *Parser) code(data []byte) int { i = skipUntilChar(data, i, '\n') i = skipCharN(data, i, '\n', 1) - blankline := p.isEmpty(data[beg:i]) > 0 + blankline := IsEmpty(data[beg:i]) > 0 if pre := p.codePrefix(data[beg:i]); pre > 0 { beg += pre } else if !blankline { @@ -1185,7 +1185,7 @@ func (p *Parser) code(data []byte) int { } // TODO: get rid of temp buffer codeBlock.Content = work.Bytes() - p.addBlock(codeBlock) + p.AddBlock(codeBlock) finalizeCodeBlock(codeBlock) return i @@ -1237,10 +1237,29 @@ func (p *Parser) dliPrefix(data []byte) int { if data[0] != ':' || !(data[1] == ' ' || data[1] == '\t') { return 0 } + // TODO: this is a no-op (data[0] is ':' so not ' '). + // Maybe the intent was to eat spaces before ':' ? + // either way, no change in tests i := skipChar(data, 0, ' ') return i + 2 } +// TODO: maybe it was meant to be like below +// either way, no change in tests +/* +func (p *Parser) dliPrefix(data []byte) int { + i := skipChar(data, 0, ' ') + if i+len(data) < 2 { + return 0 + } + // need a ':' followed by a space or a tab + if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') { + return 0 + } + return i + 2 +} +*/ + // parse ordered or unordered list block func (p *Parser) list(data []byte, flags ast.ListType, start int, delim byte) int { i := 0 @@ -1251,7 +1270,7 @@ func (p *Parser) list(data []byte, flags ast.ListType, start int, delim byte) in Start: start, Delimiter: delim, } - block := p.addBlock(list) + block := p.AddBlock(list) for i < len(data) { skip := p.listItem(data[i:], &flags) @@ -1398,7 +1417,7 @@ gatherlines: // if it is an empty line, guess that it is part of this item // and move on to the next line - if p.isEmpty(data[line:i]) > 0 { + if IsEmpty(data[line:i]) > 0 { containsBlankLine = true line = i continue @@ -1432,7 +1451,7 @@ gatherlines: // evaluate how this line fits in switch { // is this a nested list item? - case (p.uliPrefix(chunk) > 0 && !p.isHRule(chunk)) || p.oliPrefix(chunk) > 0 || p.dliPrefix(chunk) > 0: + case (p.uliPrefix(chunk) > 0 && !isHRule(chunk)) || p.oliPrefix(chunk) > 0 || p.dliPrefix(chunk) > 0: // if indent is 4 or more spaces on unordered or ordered lists // we need to add leadingWhiteSpaces + 1 spaces in the beginning of the chunk @@ -1484,10 +1503,7 @@ gatherlines: case containsBlankLine && indent < 4: if *flags&ast.ListTypeDefinition != 0 && i < len(data)-1 { // is the next item still a part of this list? - next := i - for next < len(data) && data[next] != '\n' { - next++ - } + next := skipUntilChar(data, i, '\n') for next < len(data)-1 && data[next] == '\n' { next++ } @@ -1526,16 +1542,16 @@ gatherlines: BulletChar: bulletChar, Delimiter: delimiter, } - p.addBlock(listItem) + p.AddBlock(listItem) // render the contents of the list item if *flags&ast.ListItemContainsBlock != 0 && *flags&ast.ListTypeTerm == 0 { // intermediate render of block item, except for definition term if sublist > 0 { - p.block(rawBytes[:sublist]) - p.block(rawBytes[sublist:]) + p.Block(rawBytes[:sublist]) + p.Block(rawBytes[sublist:]) } else { - p.block(rawBytes) + p.Block(rawBytes) } } else { // intermediate render of inline item @@ -1547,7 +1563,7 @@ gatherlines: } p.addChild(para) if sublist > 0 { - p.block(rawBytes[sublist:]) + p.Block(rawBytes[sublist:]) } } return line @@ -1574,7 +1590,7 @@ func (p *Parser) renderParagraph(data []byte) { } para := &ast.Paragraph{} para.Content = data[beg:end] - p.addBlock(para) + p.AddBlock(para) } // blockMath handle block surround with $$ @@ -1596,7 +1612,7 @@ func (p *Parser) blockMath(data []byte) int { // render the display math mathBlock := &ast.MathBlock{} mathBlock.Literal = data[2:end] - p.addBlock(mathBlock) + p.AddBlock(mathBlock) return end + 2 } @@ -1626,7 +1642,7 @@ func (p *Parser) paragraph(data []byte) int { } // did we find a blank line marking the end of the paragraph? - if n := p.isEmpty(current); n > 0 { + if n := IsEmpty(current); n > 0 { // did this blank line followed by a definition list item? if p.extensions&DefinitionLists != 0 { if i < len(data)-1 && data[i+1] == ':' { @@ -1663,7 +1679,7 @@ func (p *Parser) paragraph(data []byte) int { } block.Content = data[prev:eol] - p.addBlock(block) + p.AddBlock(block) // find the end of the underline return skipUntilChar(data, i, '\n') @@ -1680,7 +1696,7 @@ func (p *Parser) paragraph(data []byte) int { } // if there's a prefixed heading or a horizontal rule after this, paragraph is over - if p.isPrefixHeading(current) || p.isPrefixSpecialHeading(current) || p.isHRule(current) { + if p.isPrefixHeading(current) || p.isPrefixSpecialHeading(current) || isHRule(current) { p.renderParagraph(data[:i]) return i } diff --git a/vendor/github.com/gomarkdown/markdown/parser/block_table.go b/vendor/github.com/gomarkdown/markdown/parser/block_table.go index 0bf4f4ad..fa8efdf2 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/block_table.go +++ b/vendor/github.com/gomarkdown/markdown/parser/block_table.go @@ -12,7 +12,7 @@ func isBackslashEscaped(data []byte, i int) bool { } func (p *Parser) tableRow(data []byte, columns []ast.CellAlignFlags, header bool) { - p.addBlock(&ast.TableRow{}) + p.AddBlock(&ast.TableRow{}) col := 0 i := skipChar(data, 0, '|') @@ -61,7 +61,7 @@ func (p *Parser) tableRow(data []byte, columns []ast.CellAlignFlags, header bool // an empty cell that we should ignore, it exists because of colspan colspans-- } else { - p.addBlock(block) + p.AddBlock(block) } if colspan > 0 { @@ -75,7 +75,7 @@ func (p *Parser) tableRow(data []byte, columns []ast.CellAlignFlags, header bool IsHeader: header, Align: columns[col], } - p.addBlock(block) + p.AddBlock(block) } // silently ignore rows with too many cells @@ -109,7 +109,7 @@ func (p *Parser) tableFooter(data []byte) bool { return false } - p.addBlock(&ast.TableFooter{}) + p.AddBlock(&ast.TableFooter{}) return true } @@ -217,7 +217,7 @@ func (p *Parser) tableHeader(data []byte, doRender bool) (size int, columns []as } // end of column test is messy switch { - case dashes < 3: + case dashes < 1: // not a valid column return @@ -253,9 +253,9 @@ func (p *Parser) tableHeader(data []byte, doRender bool) (size int, columns []as if doRender { table = &ast.Table{} - p.addBlock(table) + p.AddBlock(table) if header != nil { - p.addBlock(&ast.TableHeader{}) + p.AddBlock(&ast.TableHeader{}) p.tableRow(header, columns, true) } } @@ -277,7 +277,7 @@ func (p *Parser) table(data []byte) int { return 0 } - p.addBlock(&ast.TableBody{}) + p.AddBlock(&ast.TableBody{}) for i < len(data) { pipes, rowStart := 0, i @@ -319,7 +319,7 @@ func (p *Parser) table(data []byte) int { ast.AppendChild(figure, caption) p.addChild(figure) - p.finalize(figure) + p.Finalize(figure) i += consumed } diff --git a/vendor/github.com/gomarkdown/markdown/parser/caption.go b/vendor/github.com/gomarkdown/markdown/parser/caption.go index fe31711a..08794504 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/caption.go +++ b/vendor/github.com/gomarkdown/markdown/parser/caption.go @@ -11,7 +11,7 @@ func (p *Parser) caption(data, caption []byte) ([]byte, string, int) { } j := len(caption) data = data[j:] - end := p.linesUntilEmpty(data) + end := LinesUntilEmpty(data) data = data[:end] @@ -23,8 +23,8 @@ func (p *Parser) caption(data, caption []byte) ([]byte, string, int) { return data, "", end + j } -// linesUntilEmpty scans lines up to the first empty line. -func (p *Parser) linesUntilEmpty(data []byte) int { +// LinesUntilEmpty scans lines up to the first empty line. +func LinesUntilEmpty(data []byte) int { line, i := 0, 0 for line < len(data) { @@ -35,7 +35,7 @@ func (p *Parser) linesUntilEmpty(data []byte) int { i++ } - if p.isEmpty(data[line:i]) == 0 { + if IsEmpty(data[line:i]) == 0 { line = i continue } diff --git a/vendor/github.com/gomarkdown/markdown/parser/figures.go b/vendor/github.com/gomarkdown/markdown/parser/figures.go index 6615449c..0566c16e 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/figures.go +++ b/vendor/github.com/gomarkdown/markdown/parser/figures.go @@ -98,10 +98,10 @@ func (p *Parser) figureBlock(data []byte, doRender bool) int { } figure := &ast.CaptionFigure{} - p.addBlock(figure) - p.block(raw.Bytes()) + p.AddBlock(figure) + p.Block(raw.Bytes()) - defer p.finalize(figure) + defer p.Finalize(figure) if captionContent, id, consumed := p.caption(data[beg:], []byte("Figure: ")); consumed > 0 { caption := &ast.Caption{} @@ -113,7 +113,5 @@ func (p *Parser) figureBlock(data []byte, doRender bool) int { beg += consumed } - - p.finalize(figure) return beg } diff --git a/vendor/github.com/gomarkdown/markdown/parser/matter.go b/vendor/github.com/gomarkdown/markdown/parser/matter.go index 92686357..df284237 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/matter.go +++ b/vendor/github.com/gomarkdown/markdown/parser/matter.go @@ -29,8 +29,8 @@ func (p *Parser) documentMatter(data []byte) int { return 0 } node := &ast.DocumentMatter{Matter: matter} - p.addBlock(node) - p.finalize(node) + p.AddBlock(node) + p.Finalize(node) return consumed } diff --git a/vendor/github.com/gomarkdown/markdown/parser/parser.go b/vendor/github.com/gomarkdown/markdown/parser/parser.go index 07444cd8..91123e1b 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/parser.go +++ b/vendor/github.com/gomarkdown/markdown/parser/parser.go @@ -42,7 +42,7 @@ const ( SuperSubscript // Super- and subscript support: 2^10^, H~2~O. EmptyLinesBreakList // 2 empty lines break out of list Includes // Support including other files. - Mmark // Support Mmark syntax, see https://mmark.nl/syntax + Mmark // Support Mmark syntax, see https://mmark.miek.nl/post/syntax/ CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | @@ -206,13 +206,13 @@ func (p *Parser) isFootnote(ref *reference) bool { return ok } -func (p *Parser) finalize(block ast.Node) { +func (p *Parser) Finalize(block ast.Node) { p.tip = block.GetParent() } func (p *Parser) addChild(node ast.Node) ast.Node { for !canNodeContain(p.tip, node) { - p.finalize(p.tip) + p.Finalize(p.tip) } ast.AppendChild(p.tip, node) p.tip = node @@ -239,6 +239,18 @@ func canNodeContain(n ast.Node, v ast.Node) bool { _, ok := v.(*ast.TableCell) return ok } + // for nodes implemented outside of ast package, allow them + // to implement this logic via CanContain interface + if o, ok := n.(ast.CanContain); ok { + return o.CanContain(v) + } + // for container nodes outside of ast package default to true + // because false is a bad default + typ := fmt.Sprintf("%T", n) + customNode := !strings.HasPrefix(typ, "*ast.") + if customNode { + return n.AsLeaf() == nil + } return false } @@ -248,7 +260,7 @@ func (p *Parser) closeUnmatchedBlocks() { } for p.oldTip != p.lastMatchedContainer { parent := p.oldTip.GetParent() - p.finalize(p.oldTip) + p.Finalize(p.oldTip) p.oldTip = parent } p.allClosed = true @@ -273,10 +285,14 @@ type Reference struct { // You can then convert AST to html using html.Renderer, to some other format // using a custom renderer or transform the tree. func (p *Parser) Parse(input []byte) ast.Node { - p.block(input) + // the code only works with Unix CR newlines so to make life easy for + // callers normalize newlines + input = NormalizeNewlines(input) + + p.Block(input) // Walk the tree and finish up some of unfinished blocks for p.tip != nil { - p.finalize(p.tip) + p.Finalize(p.tip) } // Walk the tree again and process inline markdown in each block ast.WalkFunc(p.Doc, func(node ast.Node, entering bool) ast.WalkStatus { @@ -322,8 +338,8 @@ func (p *Parser) parseRefsToAST() { IsFootnotesList: true, ListFlags: ast.ListTypeOrdered, } - p.addBlock(&ast.Footnotes{}) - block := p.addBlock(list) + p.AddBlock(&ast.Footnotes{}) + block := p.AddBlock(list) flags := ast.ListItemBeginningOfList // Note: this loop is intentionally explicit, not range-form. This is // because the body of the loop will append nested footnotes to p.notes and @@ -338,7 +354,7 @@ func (p *Parser) parseRefsToAST() { listItem.RefLink = ref.link if ref.hasBlock { flags |= ast.ListItemContainsBlock - p.block(ref.title) + p.Block(ref.title) } else { p.Inline(block, ref.title) } @@ -660,7 +676,7 @@ gatherLines: // if it is an empty line, guess that it is part of this item // and move on to the next line - if p.isEmpty(data[blockEnd:i]) > 0 { + if IsEmpty(data[blockEnd:i]) > 0 { containsBlankLine = true blockEnd = i continue @@ -883,3 +899,26 @@ func isListItem(d ast.Node) bool { _, ok := d.(*ast.ListItem) return ok } + +func NormalizeNewlines(d []byte) []byte { + wi := 0 + n := len(d) + for i := 0; i < n; i++ { + c := d[i] + // 13 is CR + if c != 13 { + d[wi] = c + wi++ + continue + } + // replace CR (mac / win) with LF (unix) + d[wi] = 10 + wi++ + if i < n-1 && d[i+1] == 10 { + // this was CRLF, so skip the LF + i++ + } + + } + return d[:wi] +} diff --git a/vendor/github.com/gomarkdown/markdown/todo.md b/vendor/github.com/gomarkdown/markdown/todo.md deleted file mode 100644 index be8bb55c..00000000 --- a/vendor/github.com/gomarkdown/markdown/todo.md +++ /dev/null @@ -1,7 +0,0 @@ -# Things to do - -[ ] docs: add examples like https://godoc.org/github.com/dgrijalva/jwt-go (put in foo_example_test.go). Or see https://github.com/garyburd/redigo/blob/master/redis/zpop_example_test.go#L5 / https://godoc.org/github.com/garyburd/redigo/redis or https://godoc.org/github.com/go-redis/redis - -[ ] figure out expandTabs and parser.TabSizeEight. Are those used? - -[ ] SoftbreakData is not used diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE deleted file mode 100644 index c33dcc7c..00000000 --- a/vendor/github.com/hashicorp/errwrap/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md deleted file mode 100644 index 444df08f..00000000 --- a/vendor/github.com/hashicorp/errwrap/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# errwrap - -`errwrap` is a package for Go that formalizes the pattern of wrapping errors -and checking if an error contains another error. - -There is a common pattern in Go of taking a returned `error` value and -then wrapping it (such as with `fmt.Errorf`) before returning it. The problem -with this pattern is that you completely lose the original `error` structure. - -Arguably the _correct_ approach is that you should make a custom structure -implementing the `error` interface, and have the original error as a field -on that structure, such [as this example](http://golang.org/pkg/os/#PathError). -This is a good approach, but you have to know the entire chain of possible -rewrapping that happens, when you might just care about one. - -`errwrap` formalizes this pattern (it doesn't matter what approach you use -above) by giving a single interface for wrapping errors, checking if a specific -error is wrapped, and extracting that error. - -## Installation and Docs - -Install using `go get github.com/hashicorp/errwrap`. - -Full documentation is available at -http://godoc.org/github.com/hashicorp/errwrap - -## Usage - -#### Basic Usage - -Below is a very basic example of its usage: - -```go -// A function that always returns an error, but wraps it, like a real -// function might. -func tryOpen() error { - _, err := os.Open("/i/dont/exist") - if err != nil { - return errwrap.Wrapf("Doesn't exist: {{err}}", err) - } - - return nil -} - -func main() { - err := tryOpen() - - // We can use the Contains helpers to check if an error contains - // another error. It is safe to do this with a nil error, or with - // an error that doesn't even use the errwrap package. - if errwrap.Contains(err, "does not exist") { - // Do something - } - if errwrap.ContainsType(err, new(os.PathError)) { - // Do something - } - - // Or we can use the associated `Get` functions to just extract - // a specific error. This would return nil if that specific error doesn't - // exist. - perr := errwrap.GetType(err, new(os.PathError)) -} -``` - -#### Custom Types - -If you're already making custom types that properly wrap errors, then -you can get all the functionality of `errwraps.Contains` and such by -implementing the `Wrapper` interface with just one function. Example: - -```go -type AppError { - Code ErrorCode - Err error -} - -func (e *AppError) WrappedErrors() []error { - return []error{e.Err} -} -``` - -Now this works: - -```go -err := &AppError{Err: fmt.Errorf("an error")} -if errwrap.ContainsType(err, fmt.Errorf("")) { - // This will work! -} -``` diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go deleted file mode 100644 index 44e368e5..00000000 --- a/vendor/github.com/hashicorp/errwrap/errwrap.go +++ /dev/null @@ -1,178 +0,0 @@ -// Package errwrap implements methods to formalize error wrapping in Go. -// -// All of the top-level functions that take an `error` are built to be able -// to take any error, not just wrapped errors. This allows you to use errwrap -// without having to type-check and type-cast everywhere. -package errwrap - -import ( - "errors" - "reflect" - "strings" -) - -// WalkFunc is the callback called for Walk. -type WalkFunc func(error) - -// Wrapper is an interface that can be implemented by custom types to -// have all the Contains, Get, etc. functions in errwrap work. -// -// When Walk reaches a Wrapper, it will call the callback for every -// wrapped error in addition to the wrapper itself. Since all the top-level -// functions in errwrap use Walk, this means that all those functions work -// with your custom type. -type Wrapper interface { - WrappedErrors() []error -} - -// Wrap defines that outer wraps inner, returning an error type that -// can be cleanly used with the other methods in this package, such as -// Contains, GetAll, etc. -// -// This function won't modify the error message at all (the outer message -// will be used). -func Wrap(outer, inner error) error { - return &wrappedError{ - Outer: outer, - Inner: inner, - } -} - -// Wrapf wraps an error with a formatting message. This is similar to using -// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap -// errors, you should replace it with this. -// -// format is the format of the error message. The string '{{err}}' will -// be replaced with the original error message. -// -// Deprecated: Use fmt.Errorf() -func Wrapf(format string, err error) error { - outerMsg := "<nil>" - if err != nil { - outerMsg = err.Error() - } - - outer := errors.New(strings.Replace( - format, "{{err}}", outerMsg, -1)) - - return Wrap(outer, err) -} - -// Contains checks if the given error contains an error with the -// message msg. If err is not a wrapped error, this will always return -// false unless the error itself happens to match this msg. -func Contains(err error, msg string) bool { - return len(GetAll(err, msg)) > 0 -} - -// ContainsType checks if the given error contains an error with -// the same concrete type as v. If err is not a wrapped error, this will -// check the err itself. -func ContainsType(err error, v interface{}) bool { - return len(GetAllType(err, v)) > 0 -} - -// Get is the same as GetAll but returns the deepest matching error. -func Get(err error, msg string) error { - es := GetAll(err, msg) - if len(es) > 0 { - return es[len(es)-1] - } - - return nil -} - -// GetType is the same as GetAllType but returns the deepest matching error. -func GetType(err error, v interface{}) error { - es := GetAllType(err, v) - if len(es) > 0 { - return es[len(es)-1] - } - - return nil -} - -// GetAll gets all the errors that might be wrapped in err with the -// given message. The order of the errors is such that the outermost -// matching error (the most recent wrap) is index zero, and so on. -func GetAll(err error, msg string) []error { - var result []error - - Walk(err, func(err error) { - if err.Error() == msg { - result = append(result, err) - } - }) - - return result -} - -// GetAllType gets all the errors that are the same type as v. -// -// The order of the return value is the same as described in GetAll. -func GetAllType(err error, v interface{}) []error { - var result []error - - var search string - if v != nil { - search = reflect.TypeOf(v).String() - } - Walk(err, func(err error) { - var needle string - if err != nil { - needle = reflect.TypeOf(err).String() - } - - if needle == search { - result = append(result, err) - } - }) - - return result -} - -// Walk walks all the wrapped errors in err and calls the callback. If -// err isn't a wrapped error, this will be called once for err. If err -// is a wrapped error, the callback will be called for both the wrapper -// that implements error as well as the wrapped error itself. -func Walk(err error, cb WalkFunc) { - if err == nil { - return - } - - switch e := err.(type) { - case *wrappedError: - cb(e.Outer) - Walk(e.Inner, cb) - case Wrapper: - cb(err) - - for _, err := range e.WrappedErrors() { - Walk(err, cb) - } - case interface{ Unwrap() error }: - cb(err) - Walk(e.Unwrap(), cb) - default: - cb(err) - } -} - -// wrappedError is an implementation of error that has both the -// outer and inner errors. -type wrappedError struct { - Outer error - Inner error -} - -func (w *wrappedError) Error() string { - return w.Outer.Error() -} - -func (w *wrappedError) WrappedErrors() []error { - return []error{w.Outer, w.Inner} -} - -func (w *wrappedError) Unwrap() error { - return w.Inner -} diff --git a/vendor/github.com/hashicorp/go-multierror/LICENSE b/vendor/github.com/hashicorp/go-multierror/LICENSE deleted file mode 100644 index 82b4de97..00000000 --- a/vendor/github.com/hashicorp/go-multierror/LICENSE +++ /dev/null @@ -1,353 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-multierror/Makefile b/vendor/github.com/hashicorp/go-multierror/Makefile deleted file mode 100644 index b97cd6ed..00000000 --- a/vendor/github.com/hashicorp/go-multierror/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -TEST?=./... - -default: test - -# test runs the test suite and vets the code. -test: generate - @echo "==> Running tests..." - @go list $(TEST) \ - | grep -v "/vendor/" \ - | xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS} - -# testrace runs the race checker -testrace: generate - @echo "==> Running tests (race)..." - @go list $(TEST) \ - | grep -v "/vendor/" \ - | xargs -n1 go test -timeout=60s -race ${TESTARGS} - -# updatedeps installs all the dependencies needed to run and build. -updatedeps: - @sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'" - -# generate runs `go generate` to build the dynamically generated source files. -generate: - @echo "==> Generating..." - @find . -type f -name '.DS_Store' -delete - @go list ./... \ - | grep -v "/vendor/" \ - | xargs -n1 go generate - -.PHONY: default test testrace updatedeps generate diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md deleted file mode 100644 index 71dd308e..00000000 --- a/vendor/github.com/hashicorp/go-multierror/README.md +++ /dev/null @@ -1,150 +0,0 @@ -# go-multierror - -[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror) -[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror) -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror) - -[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror -[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror - -`go-multierror` is a package for Go that provides a mechanism for -representing a list of `error` values as a single `error`. - -This allows a function in Go to return an `error` that might actually -be a list of errors. If the caller knows this, they can unwrap the -list and access the errors. If the caller doesn't know, the error -formats to a nice human-readable format. - -`go-multierror` is fully compatible with the Go standard library -[errors](https://golang.org/pkg/errors/) package, including the -functions `As`, `Is`, and `Unwrap`. This provides a standardized approach -for introspecting on error values. - -## Installation and Docs - -Install using `go get github.com/hashicorp/go-multierror`. - -Full documentation is available at -https://pkg.go.dev/github.com/hashicorp/go-multierror - -### Requires go version 1.13 or newer - -`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced -[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which -this library takes advantage of. - -If you need to use an earlier version of go, you can use the -[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0) -tag, which doesn't rely on features in go 1.13. - -If you see compile errors that look like the below, it's likely that -you're on an older version of go: - -``` -/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As -/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is -``` - -## Usage - -go-multierror is easy to use and purposely built to be unobtrusive in -existing Go applications/libraries that may not be aware of it. - -**Building a list of errors** - -The `Append` function is used to create a list of errors. This function -behaves a lot like the Go built-in `append` function: it doesn't matter -if the first argument is nil, a `multierror.Error`, or any other `error`, -the function behaves as you would expect. - -```go -var result error - -if err := step1(); err != nil { - result = multierror.Append(result, err) -} -if err := step2(); err != nil { - result = multierror.Append(result, err) -} - -return result -``` - -**Customizing the formatting of the errors** - -By specifying a custom `ErrorFormat`, you can customize the format -of the `Error() string` function: - -```go -var result *multierror.Error - -// ... accumulate errors here, maybe using Append - -if result != nil { - result.ErrorFormat = func([]error) string { - return "errors!" - } -} -``` - -**Accessing the list of errors** - -`multierror.Error` implements `error` so if the caller doesn't know about -multierror, it will work just fine. But if you're aware a multierror might -be returned, you can use type switches to access the list of errors: - -```go -if err := something(); err != nil { - if merr, ok := err.(*multierror.Error); ok { - // Use merr.Errors - } -} -``` - -You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap) -function. This will continue to unwrap into subsequent errors until none exist. - -**Extracting an error** - -The standard library [`errors.As`](https://golang.org/pkg/errors/#As) -function can be used directly with a multierror to extract a specific error: - -```go -// Assume err is a multierror value -err := somefunc() - -// We want to know if "err" has a "RichErrorType" in it and extract it. -var errRich RichErrorType -if errors.As(err, &errRich) { - // It has it, and now errRich is populated. -} -``` - -**Checking for an exact error value** - -Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables) -error in the `os` package. You can check if this error is present by using -the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function. - -```go -// Assume err is a multierror value -err := somefunc() -if errors.Is(err, os.ErrNotExist) { - // err contains os.ErrNotExist -} -``` - -**Returning a multierror only if there are errors** - -If you build a `multierror.Error`, you can use the `ErrorOrNil` function -to return an `error` implementation only if there are errors to return: - -```go -var result *multierror.Error - -// ... accumulate errors here - -// Return the `error` only if errors were added to the multierror, otherwise -// return nil since there are no errors. -return result.ErrorOrNil() -``` diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go deleted file mode 100644 index 3e2589bf..00000000 --- a/vendor/github.com/hashicorp/go-multierror/append.go +++ /dev/null @@ -1,43 +0,0 @@ -package multierror - -// Append is a helper function that will append more errors -// onto an Error in order to create a larger multi-error. -// -// If err is not a multierror.Error, then it will be turned into -// one. If any of the errs are multierr.Error, they will be flattened -// one level into err. -// Any nil errors within errs will be ignored. If err is nil, a new -// *Error will be returned. -func Append(err error, errs ...error) *Error { - switch err := err.(type) { - case *Error: - // Typed nils can reach here, so initialize if we are nil - if err == nil { - err = new(Error) - } - - // Go through each error and flatten - for _, e := range errs { - switch e := e.(type) { - case *Error: - if e != nil { - err.Errors = append(err.Errors, e.Errors...) - } - default: - if e != nil { - err.Errors = append(err.Errors, e) - } - } - } - - return err - default: - newErrs := make([]error, 0, len(errs)+1) - if err != nil { - newErrs = append(newErrs, err) - } - newErrs = append(newErrs, errs...) - - return Append(&Error{}, newErrs...) - } -} diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go deleted file mode 100644 index aab8e9ab..00000000 --- a/vendor/github.com/hashicorp/go-multierror/flatten.go +++ /dev/null @@ -1,26 +0,0 @@ -package multierror - -// Flatten flattens the given error, merging any *Errors together into -// a single *Error. -func Flatten(err error) error { - // If it isn't an *Error, just return the error as-is - if _, ok := err.(*Error); !ok { - return err - } - - // Otherwise, make the result and flatten away! - flatErr := new(Error) - flatten(err, flatErr) - return flatErr -} - -func flatten(err error, flatErr *Error) { - switch err := err.(type) { - case *Error: - for _, e := range err.Errors { - flatten(e, flatErr) - } - default: - flatErr.Errors = append(flatErr.Errors, err) - } -} diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go deleted file mode 100644 index 47f13c49..00000000 --- a/vendor/github.com/hashicorp/go-multierror/format.go +++ /dev/null @@ -1,27 +0,0 @@ -package multierror - -import ( - "fmt" - "strings" -) - -// ErrorFormatFunc is a function callback that is called by Error to -// turn the list of errors into a string. -type ErrorFormatFunc func([]error) string - -// ListFormatFunc is a basic formatter that outputs the number of errors -// that occurred along with a bullet point list of the errors. -func ListFormatFunc(es []error) string { - if len(es) == 1 { - return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) - } - - points := make([]string, len(es)) - for i, err := range es { - points[i] = fmt.Sprintf("* %s", err) - } - - return fmt.Sprintf( - "%d errors occurred:\n\t%s\n\n", - len(es), strings.Join(points, "\n\t")) -} diff --git a/vendor/github.com/hashicorp/go-multierror/group.go b/vendor/github.com/hashicorp/go-multierror/group.go deleted file mode 100644 index 9c29efb7..00000000 --- a/vendor/github.com/hashicorp/go-multierror/group.go +++ /dev/null @@ -1,38 +0,0 @@ -package multierror - -import "sync" - -// Group is a collection of goroutines which return errors that need to be -// coalesced. -type Group struct { - mutex sync.Mutex - err *Error - wg sync.WaitGroup -} - -// Go calls the given function in a new goroutine. -// -// If the function returns an error it is added to the group multierror which -// is returned by Wait. -func (g *Group) Go(f func() error) { - g.wg.Add(1) - - go func() { - defer g.wg.Done() - - if err := f(); err != nil { - g.mutex.Lock() - g.err = Append(g.err, err) - g.mutex.Unlock() - } - }() -} - -// Wait blocks until all function calls from the Go method have returned, then -// returns the multierror. -func (g *Group) Wait() *Error { - g.wg.Wait() - g.mutex.Lock() - defer g.mutex.Unlock() - return g.err -} diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go deleted file mode 100644 index f5457432..00000000 --- a/vendor/github.com/hashicorp/go-multierror/multierror.go +++ /dev/null @@ -1,121 +0,0 @@ -package multierror - -import ( - "errors" - "fmt" -) - -// Error is an error type to track multiple errors. This is used to -// accumulate errors in cases and return them as a single "error". -type Error struct { - Errors []error - ErrorFormat ErrorFormatFunc -} - -func (e *Error) Error() string { - fn := e.ErrorFormat - if fn == nil { - fn = ListFormatFunc - } - - return fn(e.Errors) -} - -// ErrorOrNil returns an error interface if this Error represents -// a list of errors, or returns nil if the list of errors is empty. This -// function is useful at the end of accumulation to make sure that the value -// returned represents the existence of errors. -func (e *Error) ErrorOrNil() error { - if e == nil { - return nil - } - if len(e.Errors) == 0 { - return nil - } - - return e -} - -func (e *Error) GoString() string { - return fmt.Sprintf("*%#v", *e) -} - -// WrappedErrors returns the list of errors that this Error is wrapping. It is -// an implementation of the errwrap.Wrapper interface so that multierror.Error -// can be used with that library. -// -// This method is not safe to be called concurrently. Unlike accessing the -// Errors field directly, this function also checks if the multierror is nil to -// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface. -func (e *Error) WrappedErrors() []error { - if e == nil { - return nil - } - return e.Errors -} - -// Unwrap returns an error from Error (or nil if there are no errors). -// This error returned will further support Unwrap to get the next error, -// etc. The order will match the order of Errors in the multierror.Error -// at the time of calling. -// -// The resulting error supports errors.As/Is/Unwrap so you can continue -// to use the stdlib errors package to introspect further. -// -// This will perform a shallow copy of the errors slice. Any errors appended -// to this error after calling Unwrap will not be available until a new -// Unwrap is called on the multierror.Error. -func (e *Error) Unwrap() error { - // If we have no errors then we do nothing - if e == nil || len(e.Errors) == 0 { - return nil - } - - // If we have exactly one error, we can just return that directly. - if len(e.Errors) == 1 { - return e.Errors[0] - } - - // Shallow copy the slice - errs := make([]error, len(e.Errors)) - copy(errs, e.Errors) - return chain(errs) -} - -// chain implements the interfaces necessary for errors.Is/As/Unwrap to -// work in a deterministic way with multierror. A chain tracks a list of -// errors while accounting for the current represented error. This lets -// Is/As be meaningful. -// -// Unwrap returns the next error. In the cleanest form, Unwrap would return -// the wrapped error here but we can't do that if we want to properly -// get access to all the errors. Instead, users are recommended to use -// Is/As to get the correct error type out. -// -// Precondition: []error is non-empty (len > 0) -type chain []error - -// Error implements the error interface -func (e chain) Error() string { - return e[0].Error() -} - -// Unwrap implements errors.Unwrap by returning the next error in the -// chain or nil if there are no more errors. -func (e chain) Unwrap() error { - if len(e) == 1 { - return nil - } - - return e[1:] -} - -// As implements errors.As by attempting to map to the current value. -func (e chain) As(target interface{}) bool { - return errors.As(e[0], target) -} - -// Is implements errors.Is by comparing the current value directly. -func (e chain) Is(target error) bool { - return errors.Is(e[0], target) -} diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go deleted file mode 100644 index 5c477abe..00000000 --- a/vendor/github.com/hashicorp/go-multierror/prefix.go +++ /dev/null @@ -1,37 +0,0 @@ -package multierror - -import ( - "fmt" - - "github.com/hashicorp/errwrap" -) - -// Prefix is a helper function that will prefix some text -// to the given error. If the error is a multierror.Error, then -// it will be prefixed to each wrapped error. -// -// This is useful to use when appending multiple multierrors -// together in order to give better scoping. -func Prefix(err error, prefix string) error { - if err == nil { - return nil - } - - format := fmt.Sprintf("%s {{err}}", prefix) - switch err := err.(type) { - case *Error: - // Typed nils can reach here, so initialize if we are nil - if err == nil { - err = new(Error) - } - - // Wrap each of the errors - for i, e := range err.Errors { - err.Errors[i] = errwrap.Wrapf(format, e) - } - - return err - default: - return errwrap.Wrapf(format, err) - } -} diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go deleted file mode 100644 index fecb14e8..00000000 --- a/vendor/github.com/hashicorp/go-multierror/sort.go +++ /dev/null @@ -1,16 +0,0 @@ -package multierror - -// Len implements sort.Interface function for length -func (err Error) Len() int { - return len(err.Errors) -} - -// Swap implements sort.Interface function for swapping elements -func (err Error) Swap(i, j int) { - err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] -} - -// Less implements sort.Interface function for determining order -func (err Error) Less(i, j int) bool { - return err.Errors[i].Error() < err.Errors[j].Error() -} diff --git a/vendor/github.com/labstack/echo/v4/CHANGELOG.md b/vendor/github.com/labstack/echo/v4/CHANGELOG.md index 83184249..fef7bb98 100644 --- a/vendor/github.com/labstack/echo/v4/CHANGELOG.md +++ b/vendor/github.com/labstack/echo/v4/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## v4.11.1 - 2023-07-16 + +**Fixes** + +* Fix `Gzip` middleware not sending response code for no content responses (404, 301/302 redirects etc) [#2481](https://github.com/labstack/echo/pull/2481) + + +## v4.11.0 - 2023-07-14 + + +**Fixes** + +* Fixes the proxy middleware concurrency issue of calling the Next() proxy target on Round Robin Balancer [#2409](https://github.com/labstack/echo/pull/2409) +* Fix `group.RouteNotFound` not working when group has attached middlewares [#2411](https://github.com/labstack/echo/pull/2411) +* Fix global error handler return error message when message is an error [#2456](https://github.com/labstack/echo/pull/2456) +* Do not use global timeNow variables [#2477](https://github.com/labstack/echo/pull/2477) + + +**Enhancements** + +* Added a optional config variable to disable centralized error handler in recovery middleware [#2410](https://github.com/labstack/echo/pull/2410) +* refactor: use `strings.ReplaceAll` directly [#2424](https://github.com/labstack/echo/pull/2424) +* Add support for Go1.20 `http.rwUnwrapper` to Response struct [#2425](https://github.com/labstack/echo/pull/2425) +* Check whether is nil before invoking centralized error handling [#2429](https://github.com/labstack/echo/pull/2429) +* Proper colon support in `echo.Reverse` method [#2416](https://github.com/labstack/echo/pull/2416) +* Fix misuses of a vs an in documentation comments [#2436](https://github.com/labstack/echo/pull/2436) +* Add link to slog.Handler library for Echo logging into README.md [#2444](https://github.com/labstack/echo/pull/2444) +* In proxy middleware Support retries of failed proxy requests [#2414](https://github.com/labstack/echo/pull/2414) +* gofmt fixes to comments [#2452](https://github.com/labstack/echo/pull/2452) +* gzip response only if it exceeds a minimal length [#2267](https://github.com/labstack/echo/pull/2267) +* Upgrade packages [#2475](https://github.com/labstack/echo/pull/2475) + + ## v4.10.2 - 2023-02-22 **Security** diff --git a/vendor/github.com/labstack/echo/v4/README.md b/vendor/github.com/labstack/echo/v4/README.md index fe78b6ed..ea8f30f6 100644 --- a/vendor/github.com/labstack/echo/v4/README.md +++ b/vendor/github.com/labstack/echo/v4/README.md @@ -110,6 +110,7 @@ of middlewares in this list. | [github.com/swaggo/echo-swagger](https://github.com/swaggo/echo-swagger) | Automatically generate RESTful API documentation with [Swagger](https://swagger.io/) 2.0. | | [github.com/ziflex/lecho](https://github.com/ziflex/lecho) | [Zerolog](https://github.com/rs/zerolog) logging library wrapper for Echo logger interface. | | [github.com/brpaz/echozap](https://github.com/brpaz/echozap) | Uber´s [Zap](https://github.com/uber-go/zap) logging library wrapper for Echo logger interface. | +| [github.com/samber/slog-echo](https://github.com/samber/slog-echo) | Go [slog](https://pkg.go.dev/golang.org/x/exp/slog) logging library wrapper for Echo logger interface. | | [github.com/darkweak/souin/plugins/echo](https://github.com/darkweak/souin/tree/master/plugins/echo) | HTTP cache system based on [Souin](https://github.com/darkweak/souin) to automatically get your endpoints cached. It supports some distributed and non-distributed storage systems depending your needs. | | [github.com/mikestefanello/pagoda](https://github.com/mikestefanello/pagoda) | Rapid, easy full-stack web development starter kit built with Echo. | | [github.com/go-woo/protoc-gen-echo](https://github.com/go-woo/protoc-gen-echo) | ProtoBuf generate Echo server side code | diff --git a/vendor/github.com/labstack/echo/v4/bind.go b/vendor/github.com/labstack/echo/v4/bind.go index c841ca01..374a2aec 100644 --- a/vendor/github.com/labstack/echo/v4/bind.go +++ b/vendor/github.com/labstack/echo/v4/bind.go @@ -114,7 +114,7 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { // Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body. // For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues. // The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670) - method := c.Request().Method + method := c.Request().Method if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead { if err = b.BindQueryParams(c, i); err != nil { return err diff --git a/vendor/github.com/labstack/echo/v4/binder.go b/vendor/github.com/labstack/echo/v4/binder.go index 5a6cf9d9..29cceca0 100644 --- a/vendor/github.com/labstack/echo/v4/binder.go +++ b/vendor/github.com/labstack/echo/v4/binder.go @@ -1236,7 +1236,7 @@ func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]tim // Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, false, time.Second) } @@ -1247,7 +1247,7 @@ func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder // Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, true, time.Second) } @@ -1257,7 +1257,7 @@ func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBi // Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, false, time.Millisecond) } @@ -1268,7 +1268,7 @@ func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueB // Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, true, time.Millisecond) } @@ -1280,8 +1280,8 @@ func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *Va // Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal -// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example. +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example. func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, false, time.Nanosecond) } @@ -1294,8 +1294,8 @@ func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBi // Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00 // // Note: -// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal -// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example. +// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal +// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example. func (b *ValueBinder) MustUnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder { return b.unixTime(sourceParam, dest, true, time.Nanosecond) } diff --git a/vendor/github.com/labstack/echo/v4/context.go b/vendor/github.com/labstack/echo/v4/context.go index b3a7ce8d..27da28a9 100644 --- a/vendor/github.com/labstack/echo/v4/context.go +++ b/vendor/github.com/labstack/echo/v4/context.go @@ -100,8 +100,8 @@ type ( // Set saves data in the context. Set(key string, val interface{}) - // Bind binds the request body into provided type `i`. The default binder - // does it based on Content-Type header. + // Bind binds path params, query params and the request body into provided type `i`. The default binder + // binds body based on Content-Type header. Bind(i interface{}) error // Validate validates provided `i`. It is usually called after `Context#Bind()`. diff --git a/vendor/github.com/labstack/echo/v4/echo.go b/vendor/github.com/labstack/echo/v4/echo.go index 085a3a7f..22a5b7af 100644 --- a/vendor/github.com/labstack/echo/v4/echo.go +++ b/vendor/github.com/labstack/echo/v4/echo.go @@ -39,6 +39,7 @@ package echo import ( stdContext "context" "crypto/tls" + "encoding/json" "errors" "fmt" "io" @@ -258,7 +259,7 @@ const ( const ( // Version of Echo - Version = "4.10.2" + Version = "4.11.1" website = "https://echo.labstack.com" // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo banner = ` @@ -438,12 +439,18 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { // Issue #1426 code := he.Code message := he.Message - if m, ok := he.Message.(string); ok { + + switch m := he.Message.(type) { + case string: if e.Debug { message = Map{"message": m, "error": err.Error()} } else { message = Map{"message": m} } + case json.Marshaler: + // do nothing - this type knows how to format itself to JSON + case error: + message = Map{"message": m.Error()} } // Send response @@ -614,7 +621,7 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { return e.URI(h, params...) } -// Reverse generates an URL from route name and provided parameters. +// Reverse generates a URL from route name and provided parameters. func (e *Echo) Reverse(name string, params ...interface{}) string { return e.router.Reverse(name, params...) } diff --git a/vendor/github.com/labstack/echo/v4/group.go b/vendor/github.com/labstack/echo/v4/group.go index 28ce0dd9..749a5caa 100644 --- a/vendor/github.com/labstack/echo/v4/group.go +++ b/vendor/github.com/labstack/echo/v4/group.go @@ -23,10 +23,12 @@ func (g *Group) Use(middleware ...MiddlewareFunc) { if len(g.middleware) == 0 { return } - // Allow all requests to reach the group as they might get dropped if router - // doesn't find a match, making none of the group middleware process. - g.Any("", NotFoundHandler) - g.Any("/*", NotFoundHandler) + // group level middlewares are different from Echo `Pre` and `Use` middlewares (those are global). Group level middlewares + // are only executed if they are added to the Router with route. + // So we register catch all route (404 is a safe way to emulate route match) for this group and now during routing the + // Router would find route to match our request path and therefore guarantee the middleware(s) will get executed. + g.RouteNotFound("", NotFoundHandler) + g.RouteNotFound("/*", NotFoundHandler) } // CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. diff --git a/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go b/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go index 52ef1042..f9e8caaf 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go +++ b/vendor/github.com/labstack/echo/v4/middleware/basic_auth.go @@ -2,9 +2,9 @@ package middleware import ( "encoding/base64" + "net/http" "strconv" "strings" - "net/http" "github.com/labstack/echo/v4" ) diff --git a/vendor/github.com/labstack/echo/v4/middleware/compress.go b/vendor/github.com/labstack/echo/v4/middleware/compress.go index 9e5f6106..3e9bd320 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/compress.go +++ b/vendor/github.com/labstack/echo/v4/middleware/compress.go @@ -2,6 +2,7 @@ package middleware import ( "bufio" + "bytes" "compress/gzip" "io" "net" @@ -21,12 +22,30 @@ type ( // Gzip compression level. // Optional. Default value -1. Level int `yaml:"level"` + + // Length threshold before gzip compression is applied. + // Optional. Default value 0. + // + // Most of the time you will not need to change the default. Compressing + // a short response might increase the transmitted data because of the + // gzip format overhead. Compressing the response will also consume CPU + // and time on the server and the client (for decompressing). Depending on + // your use case such a threshold might be useful. + // + // See also: + // https://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits + MinLength int } gzipResponseWriter struct { io.Writer http.ResponseWriter - wroteBody bool + wroteHeader bool + wroteBody bool + minLength int + minLengthExceeded bool + buffer *bytes.Buffer + code int } ) @@ -37,8 +56,9 @@ const ( var ( // DefaultGzipConfig is the default Gzip middleware config. DefaultGzipConfig = GzipConfig{ - Skipper: DefaultSkipper, - Level: -1, + Skipper: DefaultSkipper, + Level: -1, + MinLength: 0, } ) @@ -58,8 +78,12 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { if config.Level == 0 { config.Level = DefaultGzipConfig.Level } + if config.MinLength < 0 { + config.MinLength = DefaultGzipConfig.MinLength + } pool := gzipCompressPool(config) + bpool := bufferPool() return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { @@ -70,7 +94,6 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { res := c.Response() res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding) if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) { - res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806 i := pool.Get() w, ok := i.(*gzip.Writer) if !ok { @@ -78,19 +101,38 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { } rw := res.Writer w.Reset(rw) - grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw} + + buf := bpool.Get().(*bytes.Buffer) + buf.Reset() + + grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw, minLength: config.MinLength, buffer: buf} defer func() { + // There are different reasons for cases when we have not yet written response to the client and now need to do so. + // a) handler response had only response code and no response body (ala 404 or redirects etc). Response code need to be written now. + // b) body is shorter than our minimum length threshold and being buffered currently and needs to be written if !grw.wroteBody { if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme { res.Header().Del(echo.HeaderContentEncoding) } + if grw.wroteHeader { + rw.WriteHeader(grw.code) + } // We have to reset response to it's pristine state when // nothing is written to body or error is returned. // See issue #424, #407. res.Writer = rw w.Reset(io.Discard) + } else if !grw.minLengthExceeded { + // Write uncompressed response + res.Writer = rw + if grw.wroteHeader { + grw.ResponseWriter.WriteHeader(grw.code) + } + grw.buffer.WriteTo(rw) + w.Reset(io.Discard) } w.Close() + bpool.Put(buf) pool.Put(w) }() res.Writer = grw @@ -102,7 +144,11 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { func (w *gzipResponseWriter) WriteHeader(code int) { w.Header().Del(echo.HeaderContentLength) // Issue #444 - w.ResponseWriter.WriteHeader(code) + + w.wroteHeader = true + + // Delay writing of the header until we know if we'll actually compress the response + w.code = code } func (w *gzipResponseWriter) Write(b []byte) (int, error) { @@ -110,10 +156,40 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) { w.Header().Set(echo.HeaderContentType, http.DetectContentType(b)) } w.wroteBody = true + + if !w.minLengthExceeded { + n, err := w.buffer.Write(b) + + if w.buffer.Len() >= w.minLength { + w.minLengthExceeded = true + + // The minimum length is exceeded, add Content-Encoding header and write the header + w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806 + if w.wroteHeader { + w.ResponseWriter.WriteHeader(w.code) + } + + return w.Writer.Write(w.buffer.Bytes()) + } + + return n, err + } + return w.Writer.Write(b) } func (w *gzipResponseWriter) Flush() { + if !w.minLengthExceeded { + // Enforce compression because we will not know how much more data will come + w.minLengthExceeded = true + w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806 + if w.wroteHeader { + w.ResponseWriter.WriteHeader(w.code) + } + + w.Writer.Write(w.buffer.Bytes()) + } + w.Writer.(*gzip.Writer).Flush() if flusher, ok := w.ResponseWriter.(http.Flusher); ok { flusher.Flush() @@ -142,3 +218,12 @@ func gzipCompressPool(config GzipConfig) sync.Pool { }, } } + +func bufferPool() sync.Pool { + return sync.Pool{ + New: func() interface{} { + b := &bytes.Buffer{} + return b + }, + } +} diff --git a/vendor/github.com/labstack/echo/v4/middleware/cors.go b/vendor/github.com/labstack/echo/v4/middleware/cors.go index 149de347..6ddb540a 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/cors.go +++ b/vendor/github.com/labstack/echo/v4/middleware/cors.go @@ -150,8 +150,8 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc { allowOriginPatterns := []string{} for _, origin := range config.AllowOrigins { pattern := regexp.QuoteMeta(origin) - pattern = strings.Replace(pattern, "\\*", ".*", -1) - pattern = strings.Replace(pattern, "\\?", ".", -1) + pattern = strings.ReplaceAll(pattern, "\\*", ".*") + pattern = strings.ReplaceAll(pattern, "\\?", ".") pattern = "^" + pattern + "$" allowOriginPatterns = append(allowOriginPatterns, pattern) } diff --git a/vendor/github.com/labstack/echo/v4/middleware/decompress.go b/vendor/github.com/labstack/echo/v4/middleware/decompress.go index 88ec7098..a73c9738 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/decompress.go +++ b/vendor/github.com/labstack/echo/v4/middleware/decompress.go @@ -20,7 +20,7 @@ type ( } ) -//GZIPEncoding content-encoding header if set to "gzip", decompress body contents. +// GZIPEncoding content-encoding header if set to "gzip", decompress body contents. const GZIPEncoding string = "gzip" // Decompressor is used to get the sync.Pool used by the middleware to get Gzip readers @@ -44,12 +44,12 @@ func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool { return sync.Pool{New: func() interface{} { return new(gzip.Reader) }} } -//Decompress decompresses request body based if content encoding type is set to "gzip" with default config +// Decompress decompresses request body based if content encoding type is set to "gzip" with default config func Decompress() echo.MiddlewareFunc { return DecompressWithConfig(DefaultDecompressConfig) } -//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config +// DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { diff --git a/vendor/github.com/labstack/echo/v4/middleware/middleware.go b/vendor/github.com/labstack/echo/v4/middleware/middleware.go index f250ca49..664f71f4 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/middleware.go +++ b/vendor/github.com/labstack/echo/v4/middleware/middleware.go @@ -38,9 +38,9 @@ func rewriteRulesRegex(rewrite map[string]string) map[*regexp.Regexp]string { rulesRegex := map[*regexp.Regexp]string{} for k, v := range rewrite { k = regexp.QuoteMeta(k) - k = strings.Replace(k, `\*`, "(.*?)", -1) + k = strings.ReplaceAll(k, `\*`, "(.*?)") if strings.HasPrefix(k, `\^`) { - k = strings.Replace(k, `\^`, "^", -1) + k = strings.ReplaceAll(k, `\^`, "^") } k = k + "$" rulesRegex[regexp.MustCompile(k)] = v diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy.go b/vendor/github.com/labstack/echo/v4/middleware/proxy.go index d2cd2aa6..e4f98d9e 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy.go +++ b/vendor/github.com/labstack/echo/v4/middleware/proxy.go @@ -12,7 +12,6 @@ import ( "regexp" "strings" "sync" - "sync/atomic" "time" "github.com/labstack/echo/v4" @@ -30,6 +29,33 @@ type ( // Required. Balancer ProxyBalancer + // RetryCount defines the number of times a failed proxied request should be retried + // using the next available ProxyTarget. Defaults to 0, meaning requests are never retried. + RetryCount int + + // RetryFilter defines a function used to determine if a failed request to a + // ProxyTarget should be retried. The RetryFilter will only be called when the number + // of previous retries is less than RetryCount. If the function returns true, the + // request will be retried. The provided error indicates the reason for the request + // failure. When the ProxyTarget is unavailable, the error will be an instance of + // echo.HTTPError with a Code of http.StatusBadGateway. In all other cases, the error + // will indicate an internal error in the Proxy middleware. When a RetryFilter is not + // specified, all requests that fail with http.StatusBadGateway will be retried. A custom + // RetryFilter can be provided to only retry specific requests. Note that RetryFilter is + // only called when the request to the target fails, or an internal error in the Proxy + // middleware has occurred. Successful requests that return a non-200 response code cannot + // be retried. + RetryFilter func(c echo.Context, e error) bool + + // ErrorHandler defines a function which can be used to return custom errors from + // the Proxy middleware. ErrorHandler is only invoked when there has been + // either an internal error in the Proxy middleware or the ProxyTarget is + // unavailable. Due to the way requests are proxied, ErrorHandler is not invoked + // when a ProxyTarget returns a non-200 response. In these cases, the response + // is already written so errors cannot be modified. ErrorHandler is only + // invoked after all retry attempts have been exhausted. + ErrorHandler func(c echo.Context, err error) error + // Rewrite defines URL path rewrite rules. The values captured in asterisk can be // retrieved by index e.g. $1, $2 and so on. // Examples: @@ -72,26 +98,28 @@ type ( Next(echo.Context) *ProxyTarget } - // TargetProvider defines an interface that gives the opportunity for balancer to return custom errors when selecting target. + // TargetProvider defines an interface that gives the opportunity for balancer + // to return custom errors when selecting target. TargetProvider interface { NextTarget(echo.Context) (*ProxyTarget, error) } commonBalancer struct { targets []*ProxyTarget - mutex sync.RWMutex + mutex sync.Mutex } // RandomBalancer implements a random load balancing technique. randomBalancer struct { - *commonBalancer + commonBalancer random *rand.Rand } // RoundRobinBalancer implements a round-robin load balancing technique. roundRobinBalancer struct { - *commonBalancer - i uint32 + commonBalancer + // tracking the index on `targets` slice for the next `*ProxyTarget` to be used + i int } ) @@ -107,14 +135,14 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { in, _, err := c.Response().Hijack() if err != nil { - c.Set("_error", fmt.Sprintf("proxy raw, hijack error=%v, url=%s", t.URL, err)) + c.Set("_error", fmt.Errorf("proxy raw, hijack error=%w, url=%s", err, t.URL)) return } defer in.Close() out, err := net.Dial("tcp", t.URL.Host) if err != nil { - c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))) + c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", err, t.URL))) return } defer out.Close() @@ -122,7 +150,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { // Write header err = r.Write(out) if err != nil { - c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))) + c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", err, t.URL))) return } @@ -136,39 +164,44 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler { go cp(in, out) err = <-errCh if err != nil && err != io.EOF { - c.Set("_error", fmt.Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)) + c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err, t.URL)) } }) } // NewRandomBalancer returns a random proxy balancer. func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer { - b := &randomBalancer{commonBalancer: new(commonBalancer)} + b := randomBalancer{} b.targets = targets - return b + b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) + return &b } // NewRoundRobinBalancer returns a round-robin proxy balancer. func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer { - b := &roundRobinBalancer{commonBalancer: new(commonBalancer)} + b := roundRobinBalancer{} b.targets = targets - return b + return &b } -// AddTarget adds an upstream target to the list. +// AddTarget adds an upstream target to the list and returns `true`. +// +// However, if a target with the same name already exists then the operation is aborted returning `false`. func (b *commonBalancer) AddTarget(target *ProxyTarget) bool { + b.mutex.Lock() + defer b.mutex.Unlock() for _, t := range b.targets { if t.Name == target.Name { return false } } - b.mutex.Lock() - defer b.mutex.Unlock() b.targets = append(b.targets, target) return true } -// RemoveTarget removes an upstream target from the list. +// RemoveTarget removes an upstream target from the list by name. +// +// Returns `true` on success, `false` if no target with the name is found. func (b *commonBalancer) RemoveTarget(name string) bool { b.mutex.Lock() defer b.mutex.Unlock() @@ -182,21 +215,58 @@ func (b *commonBalancer) RemoveTarget(name string) bool { } // Next randomly returns an upstream target. +// +// Note: `nil` is returned in case upstream target list is empty. func (b *randomBalancer) Next(c echo.Context) *ProxyTarget { - if b.random == nil { - b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) + b.mutex.Lock() + defer b.mutex.Unlock() + if len(b.targets) == 0 { + return nil + } else if len(b.targets) == 1 { + return b.targets[0] } - b.mutex.RLock() - defer b.mutex.RUnlock() return b.targets[b.random.Intn(len(b.targets))] } -// Next returns an upstream target using round-robin technique. +// Next returns an upstream target using round-robin technique. In the case +// where a previously failed request is being retried, the round-robin +// balancer will attempt to use the next target relative to the original +// request. If the list of targets held by the balancer is modified while a +// failed request is being retried, it is possible that the balancer will +// return the original failed target. +// +// Note: `nil` is returned in case upstream target list is empty. func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget { - b.i = b.i % uint32(len(b.targets)) - t := b.targets[b.i] - atomic.AddUint32(&b.i, 1) - return t + b.mutex.Lock() + defer b.mutex.Unlock() + if len(b.targets) == 0 { + return nil + } else if len(b.targets) == 1 { + return b.targets[0] + } + + var i int + const lastIdxKey = "_round_robin_last_index" + // This request is a retry, start from the index of the previous + // target to ensure we don't attempt to retry the request with + // the same failed target + if c.Get(lastIdxKey) != nil { + i = c.Get(lastIdxKey).(int) + i++ + if i >= len(b.targets) { + i = 0 + } + } else { + // This is a first time request, use the global index + if b.i >= len(b.targets) { + b.i = 0 + } + i = b.i + b.i++ + } + + c.Set(lastIdxKey, i) + return b.targets[i] } // Proxy returns a Proxy middleware. @@ -211,14 +281,26 @@ func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc { // ProxyWithConfig returns a Proxy middleware with config. // See: `Proxy()` func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { + if config.Balancer == nil { + panic("echo: proxy middleware requires balancer") + } // Defaults if config.Skipper == nil { config.Skipper = DefaultProxyConfig.Skipper } - if config.Balancer == nil { - panic("echo: proxy middleware requires balancer") + if config.RetryFilter == nil { + config.RetryFilter = func(c echo.Context, e error) bool { + if httpErr, ok := e.(*echo.HTTPError); ok { + return httpErr.Code == http.StatusBadGateway + } + return false + } + } + if config.ErrorHandler == nil { + config.ErrorHandler = func(c echo.Context, err error) error { + return err + } } - if config.Rewrite != nil { if config.RegexRewrite == nil { config.RegexRewrite = make(map[*regexp.Regexp]string) @@ -229,28 +311,17 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { } provider, isTargetProvider := config.Balancer.(TargetProvider) + return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) (err error) { + return func(c echo.Context) error { if config.Skipper(c) { return next(c) } req := c.Request() res := c.Response() - - var tgt *ProxyTarget - if isTargetProvider { - tgt, err = provider.NextTarget(c) - if err != nil { - return err - } - } else { - tgt = config.Balancer.Next(c) - } - c.Set(config.ContextKey, tgt) - if err := rewriteURL(config.RegexRewrite, req); err != nil { - return err + return config.ErrorHandler(c, err) } // Fix header @@ -266,19 +337,49 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { req.Header.Set(echo.HeaderXForwardedFor, c.RealIP()) } - // Proxy - switch { - case c.IsWebSocket(): - proxyRaw(tgt, c).ServeHTTP(res, req) - case req.Header.Get(echo.HeaderAccept) == "text/event-stream": - default: - proxyHTTP(tgt, c, config).ServeHTTP(res, req) - } - if e, ok := c.Get("_error").(error); ok { - err = e - } + retries := config.RetryCount + for { + var tgt *ProxyTarget + var err error + if isTargetProvider { + tgt, err = provider.NextTarget(c) + if err != nil { + return config.ErrorHandler(c, err) + } + } else { + tgt = config.Balancer.Next(c) + } - return + c.Set(config.ContextKey, tgt) + + //If retrying a failed request, clear any previous errors from + //context here so that balancers have the option to check for + //errors that occurred using previous target + if retries < config.RetryCount { + c.Set("_error", nil) + } + + // Proxy + switch { + case c.IsWebSocket(): + proxyRaw(tgt, c).ServeHTTP(res, req) + case req.Header.Get(echo.HeaderAccept) == "text/event-stream": + default: + proxyHTTP(tgt, c, config).ServeHTTP(res, req) + } + + err, hasError := c.Get("_error").(error) + if !hasError { + return nil + } + + retry := retries > 0 && config.RetryFilter(c, err) + if !retry { + return config.ErrorHandler(c, err) + } + + retries-- + } } } } diff --git a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go index f7fae83c..1d24df52 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go +++ b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go @@ -160,6 +160,8 @@ type ( burst int expiresIn time.Duration lastCleanup time.Time + + timeNow func() time.Time } // Visitor signifies a unique user's limiter details Visitor struct { @@ -219,7 +221,8 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s store.burst = int(config.Rate) } store.visitors = make(map[string]*Visitor) - store.lastCleanup = now() + store.timeNow = time.Now + store.lastCleanup = store.timeNow() return } @@ -244,12 +247,13 @@ func (store *RateLimiterMemoryStore) Allow(identifier string) (bool, error) { limiter.Limiter = rate.NewLimiter(store.rate, store.burst) store.visitors[identifier] = limiter } - limiter.lastSeen = now() - if now().Sub(store.lastCleanup) > store.expiresIn { + now := store.timeNow() + limiter.lastSeen = now + if now.Sub(store.lastCleanup) > store.expiresIn { store.cleanupStaleVisitors() } store.mutex.Unlock() - return limiter.AllowN(now(), 1), nil + return limiter.AllowN(store.timeNow(), 1), nil } /* @@ -258,14 +262,9 @@ of users who haven't visited again after the configured expiry time has elapsed */ func (store *RateLimiterMemoryStore) cleanupStaleVisitors() { for id, visitor := range store.visitors { - if now().Sub(visitor.lastSeen) > store.expiresIn { + if store.timeNow().Sub(visitor.lastSeen) > store.expiresIn { delete(store.visitors, id) } } - store.lastCleanup = now() + store.lastCleanup = store.timeNow() } - -/* -actual time method which is mocked in test file -*/ -var now = time.Now diff --git a/vendor/github.com/labstack/echo/v4/middleware/recover.go b/vendor/github.com/labstack/echo/v4/middleware/recover.go index 7b612853..0466cfe5 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/recover.go +++ b/vendor/github.com/labstack/echo/v4/middleware/recover.go @@ -37,19 +37,26 @@ type ( // LogErrorFunc defines a function for custom logging in the middleware. // If it's set you don't need to provide LogLevel for config. + // If this function returns nil, the centralized HTTPErrorHandler will not be called. LogErrorFunc LogErrorFunc + + // DisableErrorHandler disables the call to centralized HTTPErrorHandler. + // The recovered error is then passed back to upstream middleware, instead of swallowing the error. + // Optional. Default value false. + DisableErrorHandler bool `yaml:"disable_error_handler"` } ) var ( // DefaultRecoverConfig is the default Recover middleware config. DefaultRecoverConfig = RecoverConfig{ - Skipper: DefaultSkipper, - StackSize: 4 << 10, // 4 KB - DisableStackAll: false, - DisablePrintStack: false, - LogLevel: 0, - LogErrorFunc: nil, + Skipper: DefaultSkipper, + StackSize: 4 << 10, // 4 KB + DisableStackAll: false, + DisablePrintStack: false, + LogLevel: 0, + LogErrorFunc: nil, + DisableErrorHandler: false, } ) @@ -71,7 +78,7 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc { } return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { + return func(c echo.Context) (returnErr error) { if config.Skipper(c) { return next(c) } @@ -113,7 +120,12 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc { c.Logger().Print(msg) } } - c.Error(err) + + if err != nil && !config.DisableErrorHandler { + c.Error(err) + } else { + returnErr = err + } } }() return next(c) diff --git a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go index b9e36925..ce76230c 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go +++ b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go @@ -225,7 +225,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.Skipper == nil { config.Skipper = DefaultSkipper } - now = time.Now + now := time.Now if config.timeNow != nil { now = config.timeNow } @@ -257,7 +257,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { config.BeforeNextFunc(c) } err := next(c) - if config.HandleError { + if err != nil && config.HandleError { c.Error(err) } diff --git a/vendor/github.com/labstack/echo/v4/response.go b/vendor/github.com/labstack/echo/v4/response.go index 84f7c9e7..d9c9aa6e 100644 --- a/vendor/github.com/labstack/echo/v4/response.go +++ b/vendor/github.com/labstack/echo/v4/response.go @@ -94,6 +94,13 @@ func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { return r.Writer.(http.Hijacker).Hijack() } +// Unwrap returns the original http.ResponseWriter. +// ResponseController can be used to access the original http.ResponseWriter. +// See [https://go.dev/blog/go1.20] +func (r *Response) Unwrap() http.ResponseWriter { + return r.Writer +} + func (r *Response) reset(w http.ResponseWriter) { r.beforeFuncs = nil r.afterFuncs = nil diff --git a/vendor/github.com/labstack/echo/v4/router.go b/vendor/github.com/labstack/echo/v4/router.go index 597660d3..ee6f3fa4 100644 --- a/vendor/github.com/labstack/echo/v4/router.go +++ b/vendor/github.com/labstack/echo/v4/router.go @@ -151,7 +151,7 @@ func (r *Router) Routes() []*Route { return routes } -// Reverse generates an URL from route name and provided parameters. +// Reverse generates a URL from route name and provided parameters. func (r *Router) Reverse(name string, params ...interface{}) string { uri := new(bytes.Buffer) ln := len(params) @@ -159,7 +159,12 @@ func (r *Router) Reverse(name string, params ...interface{}) string { for _, route := range r.routes { if route.Name == name { for i, l := 0, len(route.Path); i < l; i++ { - if (route.Path[i] == ':' || route.Path[i] == '*') && n < ln { + hasBackslash := route.Path[i] == '\\' + if hasBackslash && i+1 < l && route.Path[i+1] == ':' { + i++ // backslash before colon escapes that colon. in that case skip backslash + } + if n < ln && (route.Path[i] == '*' || (!hasBackslash && route.Path[i] == ':')) { + // in case of `*` wildcard or `:` (unescaped colon) param we replace everything till next slash or end of path for ; i < l && route.Path[i] != '/'; i++ { } uri.WriteString(fmt.Sprintf("%v", params[n])) diff --git a/vendor/github.com/lrstanley/girc/.editorconfig b/vendor/github.com/lrstanley/girc/.editorconfig index 32ecf3ee..4ae34fde 100644 --- a/vendor/github.com/lrstanley/girc/.editorconfig +++ b/vendor/github.com/lrstanley/girc/.editorconfig @@ -1,6 +1,9 @@ # THIS FILE IS GENERATED! DO NOT EDIT! Maintained by Terraform. # -# editorconfig.org +# editorconfig: https://editorconfig.org/ +# actual source: https://github.com/lrstanley/.github/blob/master/terraform/github-common-files/templates/.editorconfig +# + root = true [*] diff --git a/vendor/github.com/lrstanley/girc/.golangci.yml b/vendor/github.com/lrstanley/girc/.golangci.yml index 1a8320c9..1b4e7221 100644 --- a/vendor/github.com/lrstanley/girc/.golangci.yml +++ b/vendor/github.com/lrstanley/girc/.golangci.yml @@ -1,11 +1,33 @@ # THIS FILE IS GENERATED! DO NOT EDIT! Maintained by Terraform. +# +# golangci-lint: https://golangci-lint.run/ +# false-positives: https://golangci-lint.run/usage/false-positives/ +# actual source: https://github.com/lrstanley/.github/blob/master/terraform/github-common-files/templates/.golangci.yml +# modified variant of: https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 +# + run: - tests: False timeout: 3m issues: max-per-linter: 0 - max-same-issues: 0 + # max-same-issues: 0 + max-same-issues: 50 + + exclude-rules: + - source: "(noinspection|TODO)" + linters: [godot] + - source: "//noinspection" + linters: [gocritic] + - path: "_test\\.go" + linters: + - bodyclose + - dupl + - funlen + - goconst + - gosec + - noctx + - wrapcheck severity: default-severity: error @@ -16,17 +38,102 @@ severity: severity: warning linters: + disable-all: true enable: - - asciicheck - - exportloopref - - gci - - gocritic - - gofmt - - misspell + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - cyclop # checks function and package cyclomatic complexity + - dupl # tool for code clone detection + - durationcheck # checks for two durations multiplied together + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - execinquery # checks query string in Query function which reads your Go src files and warning it finds + - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # forbids identifiers + - funlen # tool for detection of long functions + - gci # controls golang package import order and makes it always deterministic + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + - gochecknoinits # checks that no init functions are present in Go code + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gocyclo # computes and checks the cyclomatic complexity of functions + - godot # checks if comments end in a period + - godox # detects FIXME, TODO and other comment keywords + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + - gomnd # detects magic numbers + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - gosimple # specializes in simplifying a code + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # detects when assignments to existing variables are not used + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - misspell # finds commonly misspelled words + - musttag # enforces field tags in (un)marshaled structs + - nakedret # finds naked returns in functions greater than a specified function length + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nilnil # checks that there is no simultaneous return of nil error and an invalid value + - noctx # finds sending http request without context.Context + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - reassign # checks that package variables are not reassigned + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - stylecheck # is a replacement for golint + - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 + - testableexamples # checks if examples are testable (have an expected output) + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - typecheck # like the front-end of a Go compiler, parses and type-checks Go code + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - unused # checks for unused constants, variables, functions and types + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - wastedassign # finds wasted assignment statements + - whitespace # detects leading and trailing whitespace + + # disabled for now: + # - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + # - gochecknoglobals # checks that no global variables exist + # - gocognit # computes and checks the cognitive complexity of functions + # - nestif # reports deeply nested if statements + # - nonamedreturns # reports all named returns + # - testpackage # makes you use a separate _test package linters-settings: + cyclop: + # The maximal code complexity to report. + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + check-type-assertions: true + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + lines: 150 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + statements: 75 + + # gocognit: + # # Minimal code complexity to report. + # min-complexity: 25 + gocritic: disabled-checks: + - whyNoLint - hugeParam - ifElseChain enabled-tags: @@ -34,5 +141,71 @@ linters-settings: - opinionated - performance - style + # https://go-critic.github.io/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + skipRecvDeref: false + + gomnd: + # Values always ignored: `time.Date`, + # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, + # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. + ignored-functions: + - os.Chmod + - os.Mkdir + - os.MkdirAll + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets + - prometheus.ExponentialBucketsRange + - prometheus.LinearBuckets + + gomodguard: + blocked: + # List of blocked modules. + modules: + - github.com/golang/protobuf: + recommendations: + - google.golang.org/protobuf + reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules" + - github.com/satori/go.uuid: + recommendations: + - github.com/google/uuid + reason: "satori's package is not maintained" + - github.com/gofrs/uuid: + recommendations: + - github.com/google/uuid + reason: "gofrs' package is not go module" + govet: - check-shadowing: true + enable-all: true + # Run `go tool vet help` to see all analyzers. + disable: + - fieldalignment # too strict + settings: + shadow: + # Whether to be strict about shadowing; can be noisy. + strict: true + + nakedret: + # Make an issue if func has more lines of code than this setting, and it has naked returns. + max-func-lines: 0 + + rowserrcheck: + # database/sql is always checked + packages: + - github.com/jmoiron/sqlx + + stylecheck: + checks: + - all + - -ST1008 # handled by revive already. + + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + all: true diff --git a/vendor/github.com/lrstanley/girc/builtin.go b/vendor/github.com/lrstanley/girc/builtin.go index e60c577a..345452e5 100644 --- a/vendor/github.com/lrstanley/girc/builtin.go +++ b/vendor/github.com/lrstanley/girc/builtin.go @@ -408,6 +408,48 @@ func handleISUPPORT(c *Client, e Event) { c.state.serverOptions[name] = val } c.state.Unlock() + + // Check for max line/nick/user/host lengths here. + c.state.RLock() + maxLineLength := c.state.maxLineLength + c.state.RUnlock() + maxNickLength := defaultNickLength + maxUserLength := defaultUserLength + maxHostLength := defaultHostLength + + var ok bool + var tmp int + + if tmp, ok = c.GetServerOptionInt("LINELEN"); ok { + maxLineLength = tmp + c.state.Lock() + c.state.maxLineLength = maxTagLength - 2 // -2 for CR-LF. + c.state.Unlock() + } + + if tmp, ok = c.GetServerOptionInt("NICKLEN"); ok { + maxNickLength = tmp + } + if tmp, ok = c.GetServerOptionInt("MAXNICKLEN"); ok && tmp > maxNickLength { + maxNickLength = tmp + } + if tmp, ok = c.GetServerOptionInt("USERLEN"); ok && tmp > maxUserLength { + maxUserLength = tmp + } + if tmp, ok = c.GetServerOptionInt("HOSTLEN"); ok && tmp > maxHostLength { + maxHostLength = tmp + } + + prefixLen := defaultPrefixPadding + maxNickLength + maxUserLength + maxHostLength + if prefixLen >= maxLineLength { + // Give up and go with defaults. + c.state.notify(c, UPDATE_GENERAL) + return + } + c.state.Lock() + c.state.maxPrefixLength = prefixLen + c.state.Unlock() + c.state.notify(c, UPDATE_GENERAL) } diff --git a/vendor/github.com/lrstanley/girc/cap.go b/vendor/github.com/lrstanley/girc/cap.go index 631b925b..f35f2ec4 100644 --- a/vendor/github.com/lrstanley/girc/cap.go +++ b/vendor/github.com/lrstanley/girc/cap.go @@ -267,9 +267,9 @@ func handleCAP(c *Client, e Event) { } if isError { - c.rx <- &Event{Command: ERROR, Params: []string{ + c.receive(&Event{Command: ERROR, Params: []string{ fmt.Sprintf("closing connection: strict transport policy provided by server is invalid; possible MITM? config: %#v", sts), - }} + }}) return } diff --git a/vendor/github.com/lrstanley/girc/cap_sasl.go b/vendor/github.com/lrstanley/girc/cap_sasl.go index d880316b..2a1e8417 100644 --- a/vendor/github.com/lrstanley/girc/cap_sasl.go +++ b/vendor/github.com/lrstanley/girc/cap_sasl.go @@ -95,9 +95,9 @@ func handleSASL(c *Client, e Event) { // some reason. The SASL spec and IRCv3 spec do not define a clear // way to abort a SASL exchange, other than to disconnect, or proceed // with CAP END. - c.rx <- &Event{Command: ERROR, Params: []string{ + c.receive(&Event{Command: ERROR, Params: []string{ fmt.Sprintf("closing connection: SASL %s failed: %s", c.Config.SASL.Method(), e.Last()), - }} + }}) return } @@ -131,5 +131,5 @@ func handleSASLError(c *Client, e Event) { // Authentication failed. The SASL spec and IRCv3 spec do not define a // clear way to abort a SASL exchange, other than to disconnect, or // proceed with CAP END. - c.rx <- &Event{Command: ERROR, Params: []string{"closing connection: " + e.Last()}} + c.receive(&Event{Command: ERROR, Params: []string{"closing connection: " + e.Last()}}) } diff --git a/vendor/github.com/lrstanley/girc/cap_tags.go b/vendor/github.com/lrstanley/girc/cap_tags.go index 42599f3a..3cc8887f 100644 --- a/vendor/github.com/lrstanley/girc/cap_tags.go +++ b/vendor/github.com/lrstanley/girc/cap_tags.go @@ -52,9 +52,12 @@ type Tags map[string]string // ParseTags parses out the key-value map of tags. raw should only be the tag // data, not a full message. For example: -// @aaa=bbb;ccc;example.com/ddd=eee +// +// @aaa=bbb;ccc;example.com/ddd=eee +// // NOT: -// @aaa=bbb;ccc;example.com/ddd=eee :nick!ident@host.com PRIVMSG me :Hello +// +// @aaa=bbb;ccc;example.com/ddd=eee :nick!ident@host.com PRIVMSG me :Hello // // Technically, there is a length limit of 4096, but the server should reject // tag messages longer than this. diff --git a/vendor/github.com/lrstanley/girc/client.go b/vendor/github.com/lrstanley/girc/client.go index db6ec080..fa2d7f69 100644 --- a/vendor/github.com/lrstanley/girc/client.go +++ b/vendor/github.com/lrstanley/girc/client.go @@ -155,6 +155,10 @@ type Config struct { // and the client. If this is set to -1, the client will not attempt to // send client -> server PING requests. PingDelay time.Duration + // PingTimeout specifies the duration at which girc will assume + // that the connection to the server has been lost if no PONG + // message has been received in reply to an outstanding PING. + PingTimeout time.Duration // disableTracking disables all channel and user-level tracking. Useful // for highly embedded scripts with single purposes. This has an exported @@ -179,13 +183,13 @@ type Config struct { // server. // // Client expectations: -// - Perform any proxy resolution. -// - Check the reverse DNS and forward DNS match. -// - Check the IP against suitable access controls (ipaccess, dnsbl, etc). +// - Perform any proxy resolution. +// - Check the reverse DNS and forward DNS match. +// - Check the IP against suitable access controls (ipaccess, dnsbl, etc). // // More information: -// - https://ircv3.net/specs/extensions/webirc.html -// - https://kiwiirc.com/docs/webirc +// - https://ircv3.net/specs/extensions/webirc.html +// - https://kiwiirc.com/docs/webirc type WebIRC struct { // Password that authenticates the WEBIRC command from this client. Password string @@ -262,6 +266,10 @@ func New(config Config) *Client { c.Config.PingDelay = 600 * time.Second } + if c.Config.PingTimeout == 0 { + c.Config.PingTimeout = 60 * time.Second + } + envDebug, _ := strconv.ParseBool(os.Getenv("GIRC_DEBUG")) if c.Config.Debug == nil { if envDebug { @@ -300,6 +308,23 @@ func New(config Config) *Client { return c } +// receive is a wrapper for sending to the Client.rx channel. It will timeout if +// the event can't be sent within 30s. +func (c *Client) receive(e *Event) { + t := time.NewTimer(30 * time.Second) + defer func() { + if !t.Stop() { + <-t.C + } + }() + + select { + case c.rx <- e: + case <-t.C: + c.debugLogEvent(e, true) + } +} + // String returns a brief description of the current client state. func (c *Client) String() string { connected := c.IsConnected() @@ -380,7 +405,7 @@ func (e *ErrEvent) Error() string { return e.Event.Last() } -func (c *Client) execLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { +func (c *Client) execLoop(ctx context.Context) error { c.debug.Print("starting execLoop") defer c.debug.Print("closing execLoop") @@ -403,9 +428,10 @@ func (c *Client) execLoop(ctx context.Context, errs chan error, wg *sync.WaitGro } done: - wg.Done() - return + return nil case event = <-c.rx: + c.RunHandlers(event) + if event != nil && event.Command == ERROR { // Handles incoming ERROR responses. These are only ever sent // by the server (with the exception that this library may use @@ -415,13 +441,9 @@ func (c *Client) execLoop(ctx context.Context, errs chan error, wg *sync.WaitGro // some reason the server doesn't disconnect the client, or // if this library is the source of the error, this should // signal back up to the main connect loop, to disconnect. - errs <- &ErrEvent{Event: event} - // Make sure to not actually exit, so we can let any handlers - // actually handle the ERROR event. + return &ErrEvent{Event: event} } - - c.RunHandlers(event) } } } @@ -669,8 +691,7 @@ func (c *Client) IsInChannel(channel string) (in bool) { // during client connection. This is also known as ISUPPORT (or RPL_PROTOCTL). // Will panic if used when tracking has been disabled. Examples of usage: // -// nickLen, success := GetServerOption("MAXNICKLEN") -// +// nickLen, success := GetServerOption("MAXNICKLEN") func (c *Client) GetServerOption(key string) (result string, ok bool) { c.panicIfNotTracking() @@ -680,6 +701,42 @@ func (c *Client) GetServerOption(key string) (result string, ok bool) { return result, ok } +// GetServerOptionInt retrieves a server capability setting (as an integer) that was +// retrieved during client connection. This is also known as ISUPPORT (or RPL_PROTOCTL). +// Will panic if used when tracking has been disabled. Examples of usage: +// +// nickLen, success := GetServerOption("MAXNICKLEN") +func (c *Client) GetServerOptionInt(key string) (result int, ok bool) { + var data string + var err error + + data, ok = c.GetServerOption(key) + if !ok { + return result, ok + } + result, err = strconv.Atoi(data) + if err != nil { + ok = false + } + + return result, ok +} + +// MaxEventLength returns the maximum supported server length of an event. This is the +// maximum length of the command and arguments, excluding the source/prefix supported +// by the protocol. If state tracking is enabled, this will utilize ISUPPORT/IRCv3 +// information to more accurately calculate the maximum supported length (i.e. extended +// length events). +func (c *Client) MaxEventLength() (max int) { + if !c.Config.disableTracking { + c.state.RLock() + max = c.state.maxLineLength - c.state.maxPrefixLength + c.state.RUnlock() + return max + } + return DefaultMaxLineLength - DefaultMaxPrefixLength +} + // NetworkName returns the network identifier. E.g. "EsperNet", "ByteIRC". // May be empty if the server does not support RPL_ISUPPORT (or RPL_PROTOCTL). // Will panic if used when tracking has been disabled. @@ -773,7 +830,7 @@ func (c *Client) debugLogEvent(e *Event, dropped bool) { var prefix string if dropped { - prefix = "dropping event (disconnected):" + prefix = "dropping event (disconnected or timeout):" } else { prefix = ">" } diff --git a/vendor/github.com/lrstanley/girc/commands.go b/vendor/github.com/lrstanley/girc/commands.go index 91a8b96a..a3bec879 100644 --- a/vendor/github.com/lrstanley/girc/commands.go +++ b/vendor/github.com/lrstanley/girc/commands.go @@ -25,8 +25,8 @@ func (cmd *Commands) Nick(name string) { // prevent sending extensive JOIN commands. func (cmd *Commands) Join(channels ...string) { // We can join multiple channels at once, however we need to ensure that - // we are not exceeding the line length. (see maxLength) - max := maxLength - len(JOIN) - 1 + // we are not exceeding the line length (see Client.MaxEventLength()). + max := cmd.c.MaxEventLength() - len(JOIN) - 1 var buffer string @@ -329,8 +329,8 @@ func (cmd *Commands) List(channels ...string) { } // We can LIST multiple channels at once, however we need to ensure that - // we are not exceeding the line length. (see maxLength) - max := maxLength - len(JOIN) - 1 + // we are not exceeding the line length (see Client.MaxEventLength()). + max := cmd.c.MaxEventLength() - len(JOIN) - 1 var buffer string diff --git a/vendor/github.com/lrstanley/girc/conn.go b/vendor/github.com/lrstanley/girc/conn.go index 626a6dca..c32eca69 100644 --- a/vendor/github.com/lrstanley/girc/conn.go +++ b/vendor/github.com/lrstanley/girc/conn.go @@ -12,6 +12,8 @@ import ( "net" "sync" "time" + + "github.com/lrstanley/girc/internal/ctxgroup" ) // Messages are delimited with CR and LF line endings, we're using the last @@ -142,17 +144,44 @@ type ErrParseEvent struct { func (e ErrParseEvent) Error() string { return "unable to parse event: " + e.Line } -func (c *ircConn) decode() (event *Event, err error) { - line, err := c.io.ReadString(delim) - if err != nil { - return nil, err - } +type decodedEvent struct { + event *Event + err error +} - if event = ParseEvent(line); event == nil { - return nil, ErrParseEvent{line} - } +func (c *ircConn) decode() <-chan decodedEvent { + ch := make(chan decodedEvent) + + go func() { + defer close(ch) + + line, err := c.io.ReadString(delim) + if err != nil { + select { + case ch <- decodedEvent{err: err}: + default: + } + + return + } + + event := ParseEvent(line) + if event == nil { + select { + case ch <- decodedEvent{err: ErrParseEvent{Line: line}}: + default: + } + + return + } - return event, nil + select { + case ch <- decodedEvent{event: event}: + default: + } + }() + + return ch } func (c *ircConn) encode(event *Event) error { @@ -291,20 +320,17 @@ startConn: } else { c.conn = newMockConn(mock) } + c.mu.Unlock() var ctx context.Context ctx, c.stop = context.WithCancel(context.Background()) - c.mu.Unlock() - errs := make(chan error, 4) - var wg sync.WaitGroup - // 4 being the number of goroutines we need to finish when this function - // returns. - wg.Add(4) - go c.execLoop(ctx, errs, &wg) - go c.readLoop(ctx, errs, &wg) - go c.sendLoop(ctx, errs, &wg) - go c.pingLoop(ctx, errs, &wg) + group := ctxgroup.New(ctx) + + group.Go(c.execLoop) + group.Go(c.readLoop) + group.Go(c.sendLoop) + group.Go(c.pingLoop) // Passwords first. @@ -338,16 +364,15 @@ startConn: c.RunHandlers(&Event{Command: INITIALIZED, Params: []string{addr}}) // Wait for the first error. - var result error - select { - case <-ctx.Done(): + err := group.Wait() + if err != nil { + c.debug.Printf("received error, beginning cleanup: %v", err) + } else { if !c.state.sts.beginUpgrade { c.debug.Print("received request to close, beginning clean up") } + c.RunHandlers(&Event{Command: CLOSED, Params: []string{addr}}) - case err := <-errs: - c.debug.Printf("received error, beginning cleanup: %v", err) - result = err } // Make sure that the connection is closed if not already. @@ -363,20 +388,13 @@ startConn: c.RunHandlers(&Event{Command: DISCONNECTED, Params: []string{addr}}) - // Once we have our error/result, let all other functions know we're done. - c.debug.Print("waiting for all routines to finish") - - // Wait for all goroutines to finish. - wg.Wait() - close(errs) - // This helps ensure that the end user isn't improperly using the client // more than once. If they want to do this, they should be using multiple // clients, not multiple instances of Connect(). c.mu.Lock() c.conn = nil - if result == nil { + if err == nil { if c.state.sts.beginUpgrade { c.state.sts.beginUpgrade = false c.mu.Unlock() @@ -389,76 +407,85 @@ startConn: } c.mu.Unlock() - return result + return err } // readLoop sets a timeout of 300 seconds, and then attempts to read from the // IRC server. If there is an error, it calls Reconnect. -func (c *Client) readLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { +func (c *Client) readLoop(ctx context.Context) error { c.debug.Print("starting readLoop") defer c.debug.Print("closing readLoop") - var event *Event - var err error + var de decodedEvent for { select { case <-ctx.Done(): - wg.Done() - return + return nil default: _ = c.conn.sock.SetReadDeadline(time.Now().Add(300 * time.Second)) - event, err = c.conn.decode() - if err != nil { - errs <- err - wg.Done() - return + + select { + case <-ctx.Done(): + return nil + case de = <-c.conn.decode(): + } + + if de.err != nil { + return de.err } // Check if it's an echo-message. if !c.Config.disableTracking { - event.Echo = (event.Command == PRIVMSG || event.Command == NOTICE) && - event.Source != nil && event.Source.ID() == c.GetID() + de.event.Echo = (de.event.Command == PRIVMSG || de.event.Command == NOTICE) && + de.event.Source != nil && de.event.Source.ID() == c.GetID() } - c.rx <- event + c.receive(de.event) } } } -// Send sends an event to the server. Use Client.RunHandlers() if you are -// simply looking to trigger handlers with an event. +// Send sends an event to the server. Send will split events if the event is longer +// than what the server supports, and is an event that supports splitting. Use +// Client.RunHandlers() if you are simply looking to trigger handlers with an event. func (c *Client) Send(event *Event) { var delay time.Duration - if !c.Config.AllowFlood { - c.mu.RLock() - - // Drop the event early as we're disconnected, this way we don't have to wait - // the (potentially long) rate limit delay before dropping. - if c.conn == nil { - c.debugLogEvent(event, true) - c.mu.RUnlock() - return - } - - c.conn.mu.Lock() - delay = c.conn.rate(event.Len()) - c.conn.mu.Unlock() - c.mu.RUnlock() - } - if c.Config.GlobalFormat && len(event.Params) > 0 && event.Params[len(event.Params)-1] != "" && (event.Command == PRIVMSG || event.Command == TOPIC || event.Command == NOTICE) { event.Params[len(event.Params)-1] = Fmt(event.Params[len(event.Params)-1]) } - <-time.After(delay) - c.write(event) + var events []*Event + events = event.split(c.MaxEventLength()) + + for _, e := range events { + if !c.Config.AllowFlood { + c.mu.RLock() + + // Drop the event early as we're disconnected, this way we don't have to wait + // the (potentially long) rate limit delay before dropping. + if c.conn == nil { + c.debugLogEvent(e, true) + c.mu.RUnlock() + return + } + + c.conn.mu.Lock() + delay = c.conn.rate(e.Len()) + c.conn.mu.Unlock() + c.mu.RUnlock() + } + + <-time.After(delay) + c.write(e) + } } // write is the lower level function to write an event. It does not have a -// write-delay when sending events. +// write-delay when sending events. write will timeout after 30s if the event +// can't be sent. func (c *Client) write(event *Event) { c.mu.RLock() defer c.mu.RUnlock() @@ -468,7 +495,19 @@ func (c *Client) write(event *Event) { c.debugLogEvent(event, true) return } - c.tx <- event + + t := time.NewTimer(30 * time.Second) + defer func() { + if !t.Stop() { + <-t.C + } + }() + + select { + case c.tx <- event: + case <-t.C: + c.debugLogEvent(event, true) + } } // rate allows limiting events based on how frequent the event is being sent, @@ -487,7 +526,7 @@ func (c *ircConn) rate(chars int) time.Duration { return 0 } -func (c *Client) sendLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { +func (c *Client) sendLoop(ctx context.Context) error { c.debug.Print("starting sendLoop") defer c.debug.Print("closing sendLoop") @@ -537,18 +576,14 @@ func (c *Client) sendLoop(ctx context.Context, errs chan error, wg *sync.WaitGro if event.Command == QUIT { c.Close() - wg.Done() - return + return nil } if err != nil { - errs <- err - wg.Done() - return + return err } case <-ctx.Done(): - wg.Done() - return + return nil } } } @@ -568,11 +603,10 @@ type ErrTimedOut struct { func (ErrTimedOut) Error() string { return "timed out waiting for a requested PING response" } -func (c *Client) pingLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { +func (c *Client) pingLoop(ctx context.Context) error { // Don't run the pingLoop if they want to disable it. if c.Config.PingDelay <= 0 { - wg.Done() - return + return nil } c.debug.Print("starting pingLoop") @@ -604,9 +638,8 @@ func (c *Client) pingLoop(ctx context.Context, errs chan error, wg *sync.WaitGro } c.conn.mu.RLock() - if pingSent && time.Since(c.conn.lastPong) > c.Config.PingDelay+(60*time.Second) { - // It's 60 seconds over what out ping delay is, connection - // has probably dropped. + if pingSent && time.Since(c.conn.lastPong) > c.Config.PingDelay+c.Config.PingTimeout { + // PingTimeout exceeded, connection has probably dropped. err := ErrTimedOut{ TimeSinceSuccess: time.Since(c.conn.lastPong), LastPong: c.conn.lastPong, @@ -615,9 +648,7 @@ func (c *Client) pingLoop(ctx context.Context, errs chan error, wg *sync.WaitGro } c.conn.mu.RUnlock() - errs <- err - wg.Done() - return + return err } c.conn.mu.RUnlock() @@ -628,8 +659,7 @@ func (c *Client) pingLoop(ctx context.Context, errs chan error, wg *sync.WaitGro c.Cmd.Ping(fmt.Sprintf("%d", time.Now().UnixNano())) pingSent = true case <-ctx.Done(): - wg.Done() - return + return nil } } } diff --git a/vendor/github.com/lrstanley/girc/event.go b/vendor/github.com/lrstanley/girc/event.go index 7801615d..2622f89e 100644 --- a/vendor/github.com/lrstanley/girc/event.go +++ b/vendor/github.com/lrstanley/girc/event.go @@ -13,7 +13,41 @@ import ( const ( eventSpace byte = ' ' // Separator. - maxLength int = 510 // Maximum length is 510 (2 for line endings). + + // TODO: if state tracking is enabled, we SHOULD be able to use it's known length. + + // Can be overridden by the NICKLEN (or MAXNICKLEN) ISUPPORT parameter. 30 or 31 + // are typical values for this parameter advertised by servers today. + defaultNickLength = 30 + // The maximum length of <username> may be specified by the USERLEN RPL_ISUPPORT + // parameter. If this length is advertised, the username MUST be silently truncated + // to the given length before being used. + defaultUserLength = 18 + // If a looked-up domain name is longer than this length (or overridden by the + // HOSTLEN ISUPPORT parameter), the server SHOULD opt to use the IP address instead, + // so that the hostname is underneath this length. + defaultHostLength = 63 + + // defaultPrefixPadding defaults the estimated prefix padding length of a given + // event. See also: + // [ ":" ( servername / ( nickname [ [ "!" user ] "@" host ] ) ) SPACE ] + defaultPrefixPadding = 4 +) + +var ( + // DefaultMaxLineLength is the default maximum length for an event. 510 (+2 for line endings) + // is used as a default as this is used by many older implementations. + // + // See also: RFC 2812 + // IRC messages are always lines of characters terminated with a CR-LF + // (Carriage Return - Line Feed) pair, and these messages SHALL NOT + // exceed 512 characters in length, counting all characters including + // the trailing CR-LF. + DefaultMaxLineLength = 510 + + // DefaultMaxPrefixLength defines the default max ":nickname!user@host " length + // that's used to calculate line splitting. + DefaultMaxPrefixLength = defaultPrefixPadding + defaultNickLength + defaultUserLength + defaultHostLength ) // cutCRFunc is used to trim CR characters from prefixes/messages. @@ -125,16 +159,16 @@ func ParseEvent(raw string) (e *Event) { // Event represents an IRC protocol message, see RFC1459 section 2.3.1 // -// <message> :: [':' <prefix> <SPACE>] <command> <params> <crlf> -// <prefix> :: <servername> | <nick> ['!' <user>] ['@' <host>] -// <command> :: <letter>{<letter>} | <number> <number> <number> -// <SPACE> :: ' '{' '} -// <params> :: <SPACE> [':' <trailing> | <middle> <params>] -// <middle> :: <Any *non-empty* sequence of octets not including SPACE or NUL -// or CR or LF, the first of which may not be ':'> -// <trailing> :: <Any, possibly empty, sequence of octets not including NUL or -// CR or LF> -// <crlf> :: CR LF +// <message> :: [':' <prefix> <SPACE>] <command> <params> <crlf> +// <prefix> :: <servername> | <nick> ['!' <user>] ['@' <host>] +// <command> :: <letter>{<letter>} | <number> <number> <number> +// <SPACE> :: ' '{' '} +// <params> :: <SPACE> [':' <trailing> | <middle> <params>] +// <middle> :: <Any *non-empty* sequence of octets not including SPACE or NUL +// or CR or LF, the first of which may not be ':'> +// <trailing> :: <Any, possibly empty, sequence of octets not including NUL or +// CR or LF> +// <crlf> :: CR LF type Event struct { // Source is the origin of the event. Source *Source `json:"source"` @@ -223,11 +257,80 @@ func (e *Event) Equals(ev *Event) bool { return true } -// Len calculates the length of the string representation of event. Note that -// this will return the true length (even if longer than what IRC supports), -// which may be useful if you are trying to check and see if a message is -// too long, to trim it down yourself. +// split will split a potentially large event that is larger than what the server +// supports, into multiple events. split will ignore events that cannot be split, and +// if the event isn't longer than what the server supports, it will just return an array +// with 1 entry, the original event. +func (e *Event) split(maxLength int) []*Event { + if len(e.Params) < 1 || (e.Command != PRIVMSG && e.Command != NOTICE) { + return []*Event{e} + } + + // Exclude source, even if it does exist, because the server will likely ignore the + // sent source anyway. + event := e.Copy() + event.Source = nil + + if event.LenOpts(false) < maxLength { + return []*Event{e} + } + + results := []*Event{} + + // Will force the length check to include " :". This will allow us to get the length + // of the commands and necessary prefixes. + text := event.Last() + event.Params[len(event.Params)-1] = "" + cmdLen := event.LenOpts(false) + + var ok bool + var ctcp *CTCPEvent + if ok, ctcp = e.IsCTCP(); ok { + if text == "" { + return []*Event{e} + } + + text = ctcp.Text + + // ctcpDelim's at start and end, and space between command and trailing text. + maxLength -= len(ctcp.Command) + 4 + } + + // If the command itself is longer than the limit, there is a problem. PRIVMSG should + // be 1->1 per RFC. Just return the original message and let it be the user of the + // libraries problem. + if cmdLen > maxLength { + return []*Event{e} + } + + // Split the text into correctly size segments, and make the necessary number of + // events that duplicate the original event. + for _, split := range splitMessage(text, maxLength-cmdLen) { + if ctcp != nil { + split = string(ctcpDelim) + ctcp.Command + string(eventSpace) + split + string(ctcpDelim) + } + clonedEvent := event.Copy() + clonedEvent.Source = e.Source + clonedEvent.Params[len(e.Params)-1] = split + results = append(results, clonedEvent) + } + + return results +} + +// Len calculates the length of the string representation of event (including tags). +// Note that this will return the true length (even if longer than what IRC supports), +// which may be useful if you are trying to check and see if a message is too long, to +// trim it down yourself. func (e *Event) Len() (length int) { + return e.LenOpts(true) +} + +// LenOpts calculates the length of the string representation of event (with a toggle +// for tags). Note that this will return the true length (even if longer than what IRC +// supports), which may be useful if you are trying to check and see if a message is +// too long, to trim it down yourself. +func (e *Event) LenOpts(includeTags bool) (length int) { if e.Tags != nil { // Include tags and trailing space. length = e.Tags.Len() + 1 @@ -248,7 +351,7 @@ func (e *Event) Len() (length int) { // If param contains a space or it's empty, it's trailing, so it should be // prefixed with a colon (:). - if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || strings.HasPrefix(e.Params[i], ":") || e.Params[i] == "") { + if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "" || strings.HasPrefix(e.Params[i], ":")) { length++ } } @@ -259,10 +362,6 @@ func (e *Event) Len() (length int) { // Bytes returns a []byte representation of event. Strips all newlines and // carriage returns. -// -// Per RFC2812 section 2.3, messages should not exceed 512 characters in -// length. This method forces that limit by discarding any characters -// exceeding the length limit. func (e *Event) Bytes() []byte { buffer := new(bytes.Buffer) @@ -284,7 +383,7 @@ func (e *Event) Bytes() []byte { // Space separated list of arguments. if len(e.Params) > 0 { for i := 0; i < len(e.Params); i++ { - if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || strings.HasPrefix(e.Params[i], ":") || e.Params[i] == "") { + if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "" || strings.HasPrefix(e.Params[i], ":")) { buffer.WriteString(string(eventSpace) + string(messagePrefix) + e.Params[i]) continue } @@ -292,11 +391,6 @@ func (e *Event) Bytes() []byte { } } - // We need the limit the buffer length. - if buffer.Len() > (maxLength) { - buffer.Truncate(maxLength) - } - // If we truncated in the middle of a utf8 character, we need to remove // the other (now invalid) bytes. out := bytes.ToValidUTF8(buffer.Bytes(), nil) diff --git a/vendor/github.com/lrstanley/girc/format.go b/vendor/github.com/lrstanley/girc/format.go index 85e3e387..3b9d60af 100644 --- a/vendor/github.com/lrstanley/girc/format.go +++ b/vendor/github.com/lrstanley/girc/format.go @@ -7,13 +7,21 @@ package girc import ( "bytes" "fmt" + "net/url" "regexp" "strings" + "unicode/utf8" ) const ( - fmtOpenChar = '{' - fmtCloseChar = '}' + fmtOpenChar = '{' + fmtCloseChar = '}' + maxWordSplitLength = 30 +) + +var ( + reCode = regexp.MustCompile(`(\x02|\x1d|\x0f|\x03|\x16|\x1f|\x01)`) + reColor = regexp.MustCompile(`\x03([019]?\d(,[019]?\d)?)`) ) var fmtColors = map[string]int{ @@ -66,9 +74,9 @@ var fmtCodes = map[string]string{ // // For example: // -// client.Message("#channel", Fmt("{red}{b}Hello {red,blue}World{c}")) +// client.Message("#channel", Fmt("{red}{b}Hello {red,blue}World{c}")) func Fmt(text string) string { - var last = -1 + last := -1 for i := 0; i < len(text); i++ { if text[i] == fmtOpenChar { last = i @@ -136,16 +144,12 @@ func TrimFmt(text string) string { return text } -// This is really the only fastest way of doing this (marginally better than -// actually trying to parse it manually.) -var reStripColor = regexp.MustCompile(`\x03([019]?\d(,[019]?\d)?)?`) - // StripRaw tries to strip all ASCII format codes that are used for IRC. // Primarily, foreground/background colors, and other control bytes like // reset, bold, italic, reverse, etc. This also is done in a specific way // in order to ensure no truncation of other non-irc formatting. func StripRaw(text string) string { - text = reStripColor.ReplaceAllString(text, "") + text = reColor.ReplaceAllString(text, "") for _, code := range fmtCodes { text = strings.ReplaceAll(text, code, "") @@ -164,12 +168,12 @@ func StripRaw(text string) string { // all ASCII printable chars. This function will NOT do that for // compatibility reasons. // -// channel = ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring -// [ ":" chanstring ] -// chanstring = 0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B -// chanstring = / 0x2D-0x39 / 0x3B-0xFF -// ; any octet except NUL, BELL, CR, LF, " ", "," and ":" -// channelid = 5( 0x41-0x5A / digit ) ; 5( A-Z / 0-9 ) +// channel = ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring +// [ ":" chanstring ] +// chanstring = 0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B +// chanstring = / 0x2D-0x39 / 0x3B-0xFF +// ; any octet except NUL, BELL, CR, LF, " ", "," and ":" +// channelid = 5( 0x41-0x5A / digit ) ; 5( A-Z / 0-9 ) func IsValidChannel(channel string) bool { if len(channel) <= 1 || len(channel) > 50 { return false @@ -214,10 +218,10 @@ func IsValidChannel(channel string) bool { // IsValidNick validates an IRC nickname. Note that this does not validate // IRC nickname length. // -// nickname = ( letter / special ) *8( letter / digit / special / "-" ) -// letter = 0x41-0x5A / 0x61-0x7A -// digit = 0x30-0x39 -// special = 0x5B-0x60 / 0x7B-0x7D +// nickname = ( letter / special ) *8( letter / digit / special / "-" ) +// letter = 0x41-0x5A / 0x61-0x7A +// digit = 0x30-0x39 +// special = 0x5B-0x60 / 0x7B-0x7D func IsValidNick(nick string) bool { if nick == "" { return false @@ -253,8 +257,9 @@ func IsValidNick(nick string) bool { // not be supported on all networks. Some limit this to only a single period. // // Per RFC: -// user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF ) -// ; any octet except NUL, CR, LF, " " and "@" +// +// user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF ) +// ; any octet except NUL, CR, LF, " " and "@" func IsValidUser(name string) bool { if name == "" { return false @@ -350,3 +355,172 @@ func Glob(input, match string) bool { // Check suffix last. return trailingGlob || strings.HasSuffix(input, parts[last]) } + +// sliceInsert inserts a string into a slice at a specific index, while trying +// to avoid as many allocations as possible. +func sliceInsert(input []string, i int, v ...string) []string { + total := len(input) + len(v) + if total <= cap(input) { + output := input[:total] + copy(output[i+len(v):], input[i:]) + copy(output[i:], v) + return output + } + output := make([]string, total) + copy(output, input[:i]) + copy(output[i:], v) + copy(output[i+len(v):], input[i:]) + return output +} + +// splitMessage is a text splitter that takes into consideration a few things: +// - Ensuring the returned text is no longer than maxWidth. +// - Attempting to split at the closest word boundary, while still staying inside +// of the specific maxWidth. +// - if there is no good word boundary for longer words (or e.g. links, raw data, etc) +// that are above maxWordSplitLength characters, split the word into chunks to fit the +// +// maximum width. +func splitMessage(input string, maxWidth int) (output []string) { + input = strings.ToValidUTF8(input, "?") + + words := strings.FieldsFunc(strings.TrimSpace(input), func(r rune) bool { + switch r { // Same as unicode.IsSpace, but without ctrl/lf. + case '\t', '\v', '\f', ' ', 0x85, 0xA0: + return true + } + return false + }) + + output = []string{""} + codes := []string{} + + var lastColor string + var match []string + + for i := 0; i < len(words); i++ { + j := strings.IndexAny(words[i], "\n\r") + if j == -1 { + continue + } + + word := words[i] + words[i] = word[:j] + + words = sliceInsert(words, i+1, "", strings.TrimLeft(word[j:], "\n\r")) + } + + for _, word := range words { + // Used in place of a single newline. + if word == "" { + // Last line was already empty or already only had control characters. + if output[len(output)-1] == "" || output[len(output)-1] == lastColor+word { + continue + } + + output = append(output, strings.Join(codes, "")+lastColor+word) + continue + } + + // Keep track of the last used color codes. + match = reColor.FindAllString(word, -1) + if len(match) > 0 { + lastColor = match[len(match)-1] + } + + // Find all sequence codes -- this approach isn't perfect (ideally, a lexer + // should be used to track each exact type of code), but it's good enough for + // most cases. + match = reCode.FindAllString(word, -1) + if len(match) > 0 { + for _, m := range match { + // Reset was used, so clear all codes. + if m == fmtCodes["reset"] { + lastColor = "" + codes = []string{} + continue + } + + // Check if we already have the code, and if so, remove it (closing). + contains := false + for i := 0; i < len(codes); i++ { + if m == codes[i] { + contains = true + codes = append(codes[:i], codes[i+1:]...) + + // If it's a closing color code, reset the last used color + // as well. + if m == fmtCodes["clear"] { + lastColor = "" + } + + break + } + } + + // Track the new code, unless it's a color clear but we aren't + // tracking a color right now. + if !contains && (lastColor == "" || m != fmtCodes["clear"]) { + codes = append(codes, m) + } + } + } + + checkappend: + + // Check if we can append, otherwise we must split. + if 1+utf8.RuneCountInString(word)+utf8.RuneCountInString(output[len(output)-1]) < maxWidth { + if output[len(output)-1] != "" { + output[len(output)-1] += " " + } + output[len(output)-1] += word + continue + } + + // If the word can fit on a line by itself, check if it's a url. If it is, + // put it on it's own line. + if utf8.RuneCountInString(word+strings.Join(codes, "")+lastColor) < maxWidth { + if _, err := url.Parse(word); err == nil { + output = append(output, strings.Join(codes, "")+lastColor+word) + continue + } + } + + // Check to see if we can split by misc symbols, but must be at least a few + // characters long to be split by it. + if j := strings.IndexAny(word, "-+_=|/~:;,."); j > 3 && 1+utf8.RuneCountInString(word[0:j])+utf8.RuneCountInString(output[len(output)-1]) < maxWidth { + if output[len(output)-1] != "" { + output[len(output)-1] += " " + } + output[len(output)-1] += word[0:j] + word = word[j+1:] + goto checkappend + } + + // If the word is longer than is acceptable to just put on the next line, + // split it into chunks. Also don't split the word if only a few characters + // left of the word would be on the next line. + if 1+utf8.RuneCountInString(word) > maxWordSplitLength && maxWidth-utf8.RuneCountInString(output[len(output)-1]) > 5 { + left := maxWidth - utf8.RuneCountInString(output[len(output)-1]) - 1 // -1 for the space + + if output[len(output)-1] != "" { + output[len(output)-1] += " " + } + output[len(output)-1] += word[0:left] + word = word[left:] + goto checkappend + } + + left := maxWidth - utf8.RuneCountInString(output[len(output)-1]) + output[len(output)-1] += word[0:left] + + output = append(output, strings.Join(codes, "")+lastColor) + word = word[left:] + goto checkappend + } + + for i := 0; i < len(output); i++ { + output[i] = strings.ToValidUTF8(output[i], "?") + } + return output +} diff --git a/vendor/github.com/lrstanley/girc/internal/ctxgroup/ctxgroup.go b/vendor/github.com/lrstanley/girc/internal/ctxgroup/ctxgroup.go new file mode 100644 index 00000000..e66ed2b5 --- /dev/null +++ b/vendor/github.com/lrstanley/girc/internal/ctxgroup/ctxgroup.go @@ -0,0 +1,67 @@ +// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use +// of this source code is governed by the MIT license that can be found in +// the LICENSE file. + +package ctxgroup + +import ( + "context" + "sync" +) + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +type Group struct { + ctx context.Context + cancel func() + + wg sync.WaitGroup + + errOnce sync.Once + err error +} + +// New returns a new Group and an associated context derived from ctx. +// Obtain the derived context from calling Group.Context(). +// +// The derived context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func New(ctx context.Context) *Group { + nctx, cancel := context.WithCancel(ctx) + return &Group{ctx: nctx, cancel: cancel} +} + +// Context returns the context for this group. It may be canceled by the first +// function to return a non-nil error. +func (g *Group) Context() context.Context { + return g.ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. The first call to return a +// non-nil error cancels the group; its error will be returned by Wait. +func (g *Group) Go(f func(ctx context.Context) error) { + g.wg.Add(1) + go func() { + defer g.wg.Done() + + if err := f(g.ctx); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +} diff --git a/vendor/github.com/lrstanley/girc/modes.go b/vendor/github.com/lrstanley/girc/modes.go index 35ff103a..127b0a79 100644 --- a/vendor/github.com/lrstanley/girc/modes.go +++ b/vendor/github.com/lrstanley/girc/modes.go @@ -118,13 +118,14 @@ func (c *CModes) Get(mode string) (args string, ok bool) { } // hasArg checks to see if the mode supports arguments. What ones support this?: -// A = Mode that adds or removes a nick or address to a list. Always has a parameter. -// B = Mode that changes a setting and always has a parameter. -// C = Mode that changes a setting and only has a parameter when set. -// D = Mode that changes a setting and never has a parameter. -// Note: Modes of type A return the list when there is no parameter present. -// Note: Some clients assumes that any mode not listed is of type D. -// Note: Modes in PREFIX are not listed but could be considered type B. +// +// A = Mode that adds or removes a nick or address to a list. Always has a parameter. +// B = Mode that changes a setting and always has a parameter. +// C = Mode that changes a setting and only has a parameter when set. +// D = Mode that changes a setting and never has a parameter. +// Note: Modes of type A return the list when there is no parameter present. +// Note: Some clients assumes that any mode not listed is of type D. +// Note: Modes in PREFIX are not listed but could be considered type B. func (c *CModes) hasArg(set bool, mode byte) (hasArgs, isSetting bool) { if len(c.raw) < 1 { return false, true diff --git a/vendor/github.com/lrstanley/girc/state.go b/vendor/github.com/lrstanley/girc/state.go index d9e72981..96d2ef88 100644 --- a/vendor/github.com/lrstanley/girc/state.go +++ b/vendor/github.com/lrstanley/girc/state.go @@ -28,10 +28,21 @@ type state struct { // last capability check. These will get sent once we have received the // last capability list command from the server. tmpCap map[string]map[string]string + // serverOptions are the standard capabilities and configurations // supported by the server at connection time. This also includes // RPL_ISUPPORT entries. serverOptions map[string]string + + // maxLineLength defines how long before we truncate (or split) messages. + // DefaultMaxLineLength is what is used by default, as this is going to be a common + // standard. However, protocols like IRCv3, or ISUPPORT can override this. + maxLineLength int + + // maxPrefixLength defines the estimated prefix length (":nick!user@host ") that + // we can use to calculate line splits. + maxPrefixLength int + // motd is the servers message of the day. motd string @@ -51,9 +62,11 @@ func (s *state) reset(initial bool) { s.host = "" s.channels = make(map[string]*Channel) s.users = make(map[string]*User) - s.serverOptions = make(map[string]string) s.enabledCap = make(map[string]map[string]string) s.tmpCap = make(map[string]map[string]string) + s.serverOptions = make(map[string]string) + s.maxLineLength = DefaultMaxLineLength + s.maxPrefixLength = DefaultMaxPrefixLength s.motd = "" if initial { diff --git a/vendor/github.com/matterbridge/matterclient/matterclient.go b/vendor/github.com/matterbridge/matterclient/matterclient.go index fe845efc..73d4290d 100644 --- a/vendor/github.com/matterbridge/matterclient/matterclient.go +++ b/vendor/github.com/matterbridge/matterclient/matterclient.go @@ -144,10 +144,6 @@ func (m *Client) Login() error { return err } - if err := m.initUserChannels(); err != nil { - return err - } - if m.Team == nil { validTeamNames := make([]string, len(m.OtherTeams)) for i, t := range m.OtherTeams { @@ -157,6 +153,10 @@ func (m *Client) Login() error { return fmt.Errorf("Team '%s' not found in %v", m.Credentials.Team, validTeamNames) } + if err := m.initUserChannels(); err != nil { + return err + } + // connect websocket m.wsConnect() @@ -532,7 +532,7 @@ func (m *Client) wsConnect() { } func (m *Client) doCheckAlive() error { - if _, _, err := m.Client.GetMe(""); err != nil { + if _, _, err := m.Client.GetPing(); err != nil { return err } diff --git a/vendor/github.com/mattermost/logr/.gitignore b/vendor/github.com/mattermost/logr/.gitignore deleted file mode 100644 index c2c0a9e2..00000000 --- a/vendor/github.com/mattermost/logr/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib -debug -dynip - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Output of profiler -*.prof - -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ - -# IntelliJ config -.idea - -# log files -*.log - -# transient directories -vendor -output -build -app -logs - -# test apps -test/cmd/testapp1/testapp1 -test/cmd/simple/simple diff --git a/vendor/github.com/mattermost/logr/.travis.yml b/vendor/github.com/mattermost/logr/.travis.yml deleted file mode 100644 index e6c7caf1..00000000 --- a/vendor/github.com/mattermost/logr/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: go -sudo: false -go: - - 1.x
\ No newline at end of file diff --git a/vendor/github.com/mattermost/logr/LICENSE b/vendor/github.com/mattermost/logr/LICENSE deleted file mode 100644 index 3bea6788..00000000 --- a/vendor/github.com/mattermost/logr/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 wiggin77 - -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. diff --git a/vendor/github.com/mattermost/logr/README.md b/vendor/github.com/mattermost/logr/README.md deleted file mode 100644 index a25d6de0..00000000 --- a/vendor/github.com/mattermost/logr/README.md +++ /dev/null @@ -1,193 +0,0 @@ -# logr - -[![GoDoc](https://godoc.org/github.com/mattermost/logr?status.svg)](http://godoc.org/github.com/mattermost/logr) -[![Report Card](https://goreportcard.com/badge/github.com/mattermost/logr)](https://goreportcard.com/report/github.com/mattermost/logr) - -Logr is a fully asynchronous, contextual logger for Go. - -It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but addresses two issues: - -1. Logr is fully asynchronous, meaning that all formatting and writing is done in the background. Latency sensitive applications benefit from not waiting for logging to complete. - -2. Logr provides custom filters which provide more flexibility than Trace, Debug, Info... levels. If you need to temporarily increase verbosity of logging while tracking down a problem you can avoid the fire-hose that typically comes from Debug or Trace by using custom filters. - -## Concepts - -<!-- markdownlint-disable MD033 --> -| entity | description | -| ------ | ----------- | -| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr := &Logr{}```| -| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.<br>```logger := lgr.NewLogger()```<br>```logger2 := logger.WithField("user", "Sam")```| -| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).| -| Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.<br>```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```| -| Formatter | Formats the output. Logr includes built-in formatters for JSON and plain text with delimiters. It is easy to create your own formatters or you can also use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).<br>```formatter := &format.Plain{Delim: " \| "}```| - -## Usage - -```go -// Create Logr instance. -lgr := &logr.Logr{} - -// Create a filter and formatter. Both can be shared by multiple -// targets. -filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error} -formatter := &format.Plain{Delim: " | "} - -// WriterTarget outputs to any io.Writer -t := target.NewWriterTarget(filter, formatter, os.StdOut, 1000) -lgr.AddTarget(t) - -// One or more Loggers can be created, shared, used concurrently, -// or created on demand. -logger := lgr.NewLogger().WithField("user", "Sarah") - -// Now we can log to the target(s). -logger.Debug("login attempt") -logger.Error("login failed") - -// Ensure targets are drained before application exit. -lgr.Shutdown() -``` - -## Fields - -Fields allow for contextual logging, meaning information can be added to log statements without changing the statements themselves. Information can be shared across multiple logging statements thus allowing log analysis tools to group them. - -Fields are added via Loggers: - -```go -lgr := &Logr{} -// ... add targets ... -logger := lgr.NewLogger().WithFields(logr.Fields{ - "user": user, - "role": role}) -logger.Info("login attempt") -// ... later ... -logger.Info("login successful") -``` - -`Logger.WithFields` can be used to create additional Loggers that add more fields. - -Logr fields are inspired by and work the same as [Logrus fields](https://github.com/sirupsen/logrus#fields). - -## Filters - -Logr supports the traditional seven log levels via `logr.StdFilter`: Panic, Fatal, Error, Warning, Info, Debug, and Trace. - -```go -// When added to a target, this filter will only allow -// log statements with level severity Warn or higher. -// It will also generate stack traces for Error or higher. -filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error} -``` - -Logr also supports custom filters (logr.CustomFilter) which allow fine grained inclusion of log items without turning on the fire-hose. - -```go - // create custom levels; use IDs > 10. - LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false} - LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false} - - lgr := &logr.Logr{} - - // create a custom filter with custom levels. - filter := &logr.CustomFilter{} - filter.Add(LoginLevel, LogoutLevel) - - formatter := &format.Plain{Delim: " | "} - tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000) - lgr.AddTarget(tgr) - logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"}) - - logger.Log(LoginLevel, "this item will get logged") - logger.Debug("won't be logged since Debug wasn't added to custom filter") -``` - -Both filter types allow you to determine which levels require a stack trace to be output. Note that generating stack traces cannot happen fully asynchronously and thus add latency to the calling goroutine. - -## Targets - -There are built-in targets for outputting to syslog, file, or any `io.Writer`. More will be added. - -You can use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr). - -You can create your own target by implementing the [Target](./target.go) interface. - -An easier method is to use the [logr.Basic](./target.go) type target and build your functionality on that. Basic handles all the queuing and other plumbing so you only need to implement two methods. Example target that outputs to `io.Writer`: - -```go -type Writer struct { - logr.Basic - out io.Writer -} - -func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer { - w := &Writer{out: out} - w.Basic.Start(w, w, filter, formatter, maxQueue) - return w -} - -// Write will always be called by a single goroutine, so no locking needed. -// Just convert a log record to a []byte using the formatter and output the -// bytes to your sink. -func (w *Writer) Write(rec *logr.LogRec) error { - _, stacktrace := w.IsLevelEnabled(rec.Level()) - - // take a buffer from the pool to avoid allocations or just allocate a new one. - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := w.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = w.out.Write(buf.Bytes()) - return err -} -``` - -## Formatters - -Logr has two built-in formatters, one for JSON and the other plain, delimited text. - -You can use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr). - -You can create your own formatter by implementing the [Formatter](./formatter.go) interface: - -```go -Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) -``` - -## Handlers - -When creating the Logr instance, you can add several handlers that get called when exceptional events occur: - -### ```Logr.OnLoggerError(err error)``` - -Called any time an internal logging error occurs. For example, this can happen when a target cannot connect to its data sink. - -It may be tempting to log this error, however there is a danger that logging this will simply generate another error and so on. If you must log it, use a target and custom level specifically for this event and ensure it cannot generate more errors. - -### ```Logr.OnQueueFull func(rec *LogRec, maxQueueSize int) bool``` - -Called on an attempt to add a log record to a full Logr queue. This generally means the Logr maximum queue size is too small, or at least one target is very slow. Logr maximum queue size can be changed before adding any targets via: - -```go -lgr := logr.Logr{MaxQueueSize: 10000} -``` - -Returning true will drop the log record. False will block until the log record can be added, which creates a natural throttle at the expense of latency for the calling goroutine. The default is to block. - -### ```Logr.OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool``` - -Called on an attempt to add a log record to a full target queue. This generally means your target's max queue size is too small, or the target is very slow to output. - -As with the Logr queue, returning true will drop the log record. False will block until the log record can be added, which creates a natural throttle at the expense of latency for the calling goroutine. The default is to block. - -### ```Logr.OnExit func(code int) and Logr.OnPanic func(err interface{})``` - -OnExit and OnPanic are called when the Logger.FatalXXX and Logger.PanicXXX functions are called respectively. - -In both cases the default behavior is to shut down gracefully, draining all targets, and calling `os.Exit` or `panic` respectively. - -When adding your own handlers, be sure to call `Logr.Shutdown` before exiting the application to avoid losing log records. diff --git a/vendor/github.com/mattermost/logr/config.go b/vendor/github.com/mattermost/logr/config.go deleted file mode 100644 index 83d4b0c1..00000000 --- a/vendor/github.com/mattermost/logr/config.go +++ /dev/null @@ -1,11 +0,0 @@ -package logr - -import ( - "fmt" - - "github.com/wiggin77/cfg" -) - -func ConfigLogger(config *cfg.Config) error { - return fmt.Errorf("Not implemented yet") -} diff --git a/vendor/github.com/mattermost/logr/const.go b/vendor/github.com/mattermost/logr/const.go deleted file mode 100644 index 704d0507..00000000 --- a/vendor/github.com/mattermost/logr/const.go +++ /dev/null @@ -1,34 +0,0 @@ -package logr - -import "time" - -// Defaults. -const ( - // DefaultMaxQueueSize is the default maximum queue size for Logr instances. - DefaultMaxQueueSize = 1000 - - // DefaultMaxStackFrames is the default maximum max number of stack frames collected - // when generating stack traces for logging. - DefaultMaxStackFrames = 30 - - // MaxLevelID is the maximum value of a level ID. Some level cache implementations will - // allocate a cache of this size. Cannot exceed uint. - MaxLevelID = 256 - - // DefaultEnqueueTimeout is the default amount of time a log record can take to be queued. - // This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called - // and returns false. - DefaultEnqueueTimeout = time.Second * 30 - - // DefaultShutdownTimeout is the default amount of time `logr.Shutdown` can execute before - // timing out. - DefaultShutdownTimeout = time.Second * 30 - - // DefaultFlushTimeout is the default amount of time `logr.Flush` can execute before - // timing out. - DefaultFlushTimeout = time.Second * 30 - - // DefaultMaxPooledBuffer is the maximum size a pooled buffer can be. - // Buffers that grow beyond this size are garbage collected. - DefaultMaxPooledBuffer = 1024 * 1024 -) diff --git a/vendor/github.com/mattermost/logr/filter.go b/vendor/github.com/mattermost/logr/filter.go deleted file mode 100644 index 6e654cd7..00000000 --- a/vendor/github.com/mattermost/logr/filter.go +++ /dev/null @@ -1,26 +0,0 @@ -package logr - -// LevelID is the unique id of each level. -type LevelID uint - -// Level provides a mechanism to enable/disable specific log lines. -type Level struct { - ID LevelID - Name string - Stacktrace bool -} - -// String returns the name of this level. -func (level Level) String() string { - return level.Name -} - -// Filter allows targets to determine which Level(s) are active -// for logging and which Level(s) require a stack trace to be output. -// A default implementation using "panic, fatal..." is provided, and -// a more flexible alternative implementation is also provided that -// allows any number of custom levels. -type Filter interface { - IsEnabled(Level) bool - IsStacktraceEnabled(Level) bool -} diff --git a/vendor/github.com/mattermost/logr/format/json.go b/vendor/github.com/mattermost/logr/format/json.go deleted file mode 100644 index 8f56c6cb..00000000 --- a/vendor/github.com/mattermost/logr/format/json.go +++ /dev/null @@ -1,273 +0,0 @@ -package format - -import ( - "bytes" - "fmt" - "runtime" - "sort" - "sync" - "time" - - "github.com/francoispqt/gojay" - "github.com/mattermost/logr" -) - -// ContextField is a name/value pair within the context fields. -type ContextField struct { - Key string - Val interface{} -} - -// JSON formats log records as JSON. -type JSON struct { - // DisableTimestamp disables output of timestamp field. - DisableTimestamp bool - // DisableLevel disables output of level field. - DisableLevel bool - // DisableMsg disables output of msg field. - DisableMsg bool - // DisableContext disables output of all context fields. - DisableContext bool - // DisableStacktrace disables output of stack trace. - DisableStacktrace bool - - // TimestampFormat is an optional format for timestamps. If empty - // then DefTimestampFormat is used. - TimestampFormat string - - // Deprecated: this has no effect. - Indent string - - // EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`) - // are escaped. - EscapeHTML bool - - // KeyTimestamp overrides the timestamp field key name. - KeyTimestamp string - - // KeyLevel overrides the level field key name. - KeyLevel string - - // KeyMsg overrides the msg field key name. - KeyMsg string - - // KeyContextFields when not empty will group all context fields - // under this key. - KeyContextFields string - - // KeyStacktrace overrides the stacktrace field key name. - KeyStacktrace string - - // ContextSorter allows custom sorting for the context fields. - ContextSorter func(fields logr.Fields) []ContextField - - once sync.Once -} - -// Format converts a log record to bytes in JSON format. -func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - j.once.Do(j.applyDefaultKeyNames) - - if buf == nil { - buf = &bytes.Buffer{} - } - enc := gojay.BorrowEncoder(buf) - defer func() { - enc.Release() - }() - - sorter := j.ContextSorter - if sorter == nil { - sorter = j.defaultContextSorter - } - - jlr := JSONLogRec{ - LogRec: rec, - JSON: j, - stacktrace: stacktrace, - sorter: sorter, - } - - err := enc.EncodeObject(jlr) - if err != nil { - return nil, err - } - buf.WriteByte('\n') - return buf, nil -} - -func (j *JSON) applyDefaultKeyNames() { - if j.KeyTimestamp == "" { - j.KeyTimestamp = "timestamp" - } - if j.KeyLevel == "" { - j.KeyLevel = "level" - } - if j.KeyMsg == "" { - j.KeyMsg = "msg" - } - if j.KeyStacktrace == "" { - j.KeyStacktrace = "stacktrace" - } -} - -// defaultContextSorter sorts the context fields alphabetically by key. -func (j *JSON) defaultContextSorter(fields logr.Fields) []ContextField { - keys := make([]string, 0, len(fields)) - for k := range fields { - keys = append(keys, k) - } - sort.Strings(keys) - - cf := make([]ContextField, 0, len(keys)) - for _, k := range keys { - cf = append(cf, ContextField{Key: k, Val: fields[k]}) - } - return cf -} - -// JSONLogRec decorates a LogRec adding JSON encoding. -type JSONLogRec struct { - *logr.LogRec - *JSON - stacktrace bool - sorter func(fields logr.Fields) []ContextField -} - -// MarshalJSONObject encodes the LogRec as JSON. -func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) { - if !rec.DisableTimestamp { - timestampFmt := rec.TimestampFormat - if timestampFmt == "" { - timestampFmt = logr.DefTimestampFormat - } - time := rec.Time() - enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt) - } - if !rec.DisableLevel { - enc.AddStringKey(rec.KeyLevel, rec.Level().Name) - } - if !rec.DisableMsg { - enc.AddStringKey(rec.KeyMsg, rec.Msg()) - } - if !rec.DisableContext { - ctxFields := rec.sorter(rec.Fields()) - if rec.KeyContextFields != "" { - enc.AddObjectKey(rec.KeyContextFields, jsonFields(ctxFields)) - } else { - if len(ctxFields) > 0 { - for _, cf := range ctxFields { - key := rec.prefixCollision(cf.Key) - encodeField(enc, key, cf.Val) - } - } - } - } - if rec.stacktrace && !rec.DisableStacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames)) - } - } - -} - -// IsNil returns true if the LogRec pointer is nil. -func (rec JSONLogRec) IsNil() bool { - return rec.LogRec == nil -} - -func (rec JSONLogRec) prefixCollision(key string) string { - switch key { - case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace: - return rec.prefixCollision("_" + key) - } - return key -} - -type stackFrames []runtime.Frame - -// MarshalJSONArray encodes stackFrames slice as JSON. -func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) { - for _, frame := range s { - enc.AddObject(stackFrame(frame)) - } -} - -// IsNil returns true if stackFrames is empty slice. -func (s stackFrames) IsNil() bool { - return len(s) == 0 -} - -type stackFrame runtime.Frame - -// MarshalJSONArray encodes stackFrame as JSON. -func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) { - enc.AddStringKey("Function", f.Function) - enc.AddStringKey("File", f.File) - enc.AddIntKey("Line", f.Line) -} - -func (f stackFrame) IsNil() bool { - return false -} - -type jsonFields []ContextField - -// MarshalJSONObject encodes Fields map to JSON. -func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) { - for _, ctxField := range f { - encodeField(enc, ctxField.Key, ctxField.Val) - } -} - -// IsNil returns true if map is nil. -func (f jsonFields) IsNil() bool { - return f == nil -} - -func encodeField(enc *gojay.Encoder, key string, val interface{}) { - switch vt := val.(type) { - case gojay.MarshalerJSONObject: - enc.AddObjectKey(key, vt) - case gojay.MarshalerJSONArray: - enc.AddArrayKey(key, vt) - case string: - enc.AddStringKey(key, vt) - case error: - enc.AddStringKey(key, vt.Error()) - case bool: - enc.AddBoolKey(key, vt) - case int: - enc.AddIntKey(key, vt) - case int64: - enc.AddInt64Key(key, vt) - case int32: - enc.AddIntKey(key, int(vt)) - case int16: - enc.AddIntKey(key, int(vt)) - case int8: - enc.AddIntKey(key, int(vt)) - case uint64: - enc.AddIntKey(key, int(vt)) - case uint32: - enc.AddIntKey(key, int(vt)) - case uint16: - enc.AddIntKey(key, int(vt)) - case uint8: - enc.AddIntKey(key, int(vt)) - case float64: - enc.AddFloatKey(key, vt) - case float32: - enc.AddFloat32Key(key, vt) - case *gojay.EmbeddedJSON: - enc.AddEmbeddedJSONKey(key, vt) - case time.Time: - enc.AddTimeKey(key, &vt, logr.DefTimestampFormat) - case *time.Time: - enc.AddTimeKey(key, vt, logr.DefTimestampFormat) - default: - s := fmt.Sprintf("%v", vt) - enc.AddStringKey(key, s) - } -} diff --git a/vendor/github.com/mattermost/logr/format/plain.go b/vendor/github.com/mattermost/logr/format/plain.go deleted file mode 100644 index 3fa92b49..00000000 --- a/vendor/github.com/mattermost/logr/format/plain.go +++ /dev/null @@ -1,75 +0,0 @@ -package format - -import ( - "bytes" - "fmt" - - "github.com/mattermost/logr" -) - -// Plain is the simplest formatter, outputting only text with -// no colors. -type Plain struct { - // DisableTimestamp disables output of timestamp field. - DisableTimestamp bool - // DisableLevel disables output of level field. - DisableLevel bool - // DisableMsg disables output of msg field. - DisableMsg bool - // DisableContext disables output of all context fields. - DisableContext bool - // DisableStacktrace disables output of stack trace. - DisableStacktrace bool - - // Delim is an optional delimiter output between each log field. - // Defaults to a single space. - Delim string - - // TimestampFormat is an optional format for timestamps. If empty - // then DefTimestampFormat is used. - TimestampFormat string -} - -// Format converts a log record to bytes. -func (p *Plain) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - delim := p.Delim - if delim == "" { - delim = " " - } - if buf == nil { - buf = &bytes.Buffer{} - } - - timestampFmt := p.TimestampFormat - if timestampFmt == "" { - timestampFmt = logr.DefTimestampFormat - } - - if !p.DisableTimestamp { - var arr [128]byte - tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt) - buf.Write(tbuf) - buf.WriteString(delim) - } - if !p.DisableLevel { - fmt.Fprintf(buf, "%v%s", rec.Level().Name, delim) - } - if !p.DisableMsg { - fmt.Fprint(buf, rec.Msg(), delim) - } - if !p.DisableContext { - ctx := rec.Fields() - if len(ctx) > 0 { - logr.WriteFields(buf, ctx, " ") - } - } - if stacktrace && !p.DisableStacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - buf.WriteString("\n") - logr.WriteStacktrace(buf, rec.StackFrames()) - } - } - buf.WriteString("\n") - return buf, nil -} diff --git a/vendor/github.com/mattermost/logr/formatter.go b/vendor/github.com/mattermost/logr/formatter.go deleted file mode 100644 index bb8df2d4..00000000 --- a/vendor/github.com/mattermost/logr/formatter.go +++ /dev/null @@ -1,119 +0,0 @@ -package logr - -import ( - "bytes" - "fmt" - "io" - "runtime" - "sort" -) - -// Formatter turns a LogRec into a formatted string. -type Formatter interface { - // Format converts a log record to bytes. If buf is not nil then it will be - // be filled with the formatted results, otherwise a new buffer will be allocated. - Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) -} - -const ( - // DefTimestampFormat is the default time stamp format used by - // Plain formatter and others. - DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00" -) - -// DefaultFormatter is the default formatter, outputting only text with -// no colors and a space delimiter. Use `format.Plain` instead. -type DefaultFormatter struct { -} - -// Format converts a log record to bytes. -func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - if buf == nil { - buf = &bytes.Buffer{} - } - delim := " " - timestampFmt := DefTimestampFormat - - fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim) - fmt.Fprintf(buf, "%v%s", rec.Level(), delim) - fmt.Fprint(buf, rec.Msg(), delim) - - ctx := rec.Fields() - if len(ctx) > 0 { - WriteFields(buf, ctx, " ") - } - - if stacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - buf.WriteString("\n") - WriteStacktrace(buf, rec.StackFrames()) - } - } - buf.WriteString("\n") - - return buf, nil -} - -// WriteFields writes zero or more name value pairs to the io.Writer. -// The pairs are sorted by key name and output in key=value format -// with optional separator between fields. -func WriteFields(w io.Writer, flds Fields, separator string) { - keys := make([]string, 0, len(flds)) - for k := range flds { - keys = append(keys, k) - } - sort.Strings(keys) - sep := "" - for _, key := range keys { - writeField(w, key, flds[key], sep) - sep = separator - } -} - -func writeField(w io.Writer, key string, val interface{}, sep string) { - var template string - switch v := val.(type) { - case error: - val := v.Error() - if shouldQuote(val) { - template = "%s%s=%q" - } else { - template = "%s%s=%s" - } - case string: - if shouldQuote(v) { - template = "%s%s=%q" - } else { - template = "%s%s=%s" - } - default: - template = "%s%s=%v" - } - fmt.Fprintf(w, template, sep, key, val) -} - -// shouldQuote returns true if val contains any characters that might be unsafe -// when injecting log output into an aggregator, viewer or report. -func shouldQuote(val string) bool { - for _, c := range val { - if !((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z')) { - return true - } - } - return false -} - -// WriteStacktrace formats and outputs a stack trace to an io.Writer. -func WriteStacktrace(w io.Writer, frames []runtime.Frame) { - for _, frame := range frames { - if frame.Function != "" { - fmt.Fprintf(w, " %s\n", frame.Function) - } - if frame.File != "" { - fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line) - } - } -} diff --git a/vendor/github.com/mattermost/logr/levelcache.go b/vendor/github.com/mattermost/logr/levelcache.go deleted file mode 100644 index 2cefb61d..00000000 --- a/vendor/github.com/mattermost/logr/levelcache.go +++ /dev/null @@ -1,98 +0,0 @@ -package logr - -import ( - "fmt" - "sync" -) - -// LevelStatus represents whether a level is enabled and -// requires a stack trace. -type LevelStatus struct { - Enabled bool - Stacktrace bool - empty bool -} - -type levelCache interface { - setup() - get(id LevelID) (LevelStatus, bool) - put(id LevelID, status LevelStatus) error - clear() -} - -// syncMapLevelCache uses sync.Map which may better handle large concurrency -// scenarios. -type syncMapLevelCache struct { - m sync.Map -} - -func (c *syncMapLevelCache) setup() { - c.clear() -} - -func (c *syncMapLevelCache) get(id LevelID) (LevelStatus, bool) { - if id > MaxLevelID { - return LevelStatus{}, false - } - s, _ := c.m.Load(id) - status := s.(LevelStatus) - return status, !status.empty -} - -func (c *syncMapLevelCache) put(id LevelID, status LevelStatus) error { - if id > MaxLevelID { - return fmt.Errorf("level id cannot exceed MaxLevelID (%d)", MaxLevelID) - } - c.m.Store(id, status) - return nil -} - -func (c *syncMapLevelCache) clear() { - var i LevelID - for i = 0; i < MaxLevelID; i++ { - c.m.Store(i, LevelStatus{empty: true}) - } -} - -// arrayLevelCache using array and a mutex. -type arrayLevelCache struct { - arr [MaxLevelID + 1]LevelStatus - mux sync.RWMutex -} - -func (c *arrayLevelCache) setup() { - c.clear() -} - -//var dummy = LevelStatus{} - -func (c *arrayLevelCache) get(id LevelID) (LevelStatus, bool) { - if id > MaxLevelID { - return LevelStatus{}, false - } - c.mux.RLock() - status := c.arr[id] - ok := !status.empty - c.mux.RUnlock() - return status, ok -} - -func (c *arrayLevelCache) put(id LevelID, status LevelStatus) error { - if id > MaxLevelID { - return fmt.Errorf("level id cannot exceed MaxLevelID (%d)", MaxLevelID) - } - c.mux.Lock() - defer c.mux.Unlock() - - c.arr[id] = status - return nil -} - -func (c *arrayLevelCache) clear() { - c.mux.Lock() - defer c.mux.Unlock() - - for i := range c.arr { - c.arr[i] = LevelStatus{empty: true} - } -} diff --git a/vendor/github.com/mattermost/logr/levelcustom.go b/vendor/github.com/mattermost/logr/levelcustom.go deleted file mode 100644 index 384fe4e9..00000000 --- a/vendor/github.com/mattermost/logr/levelcustom.go +++ /dev/null @@ -1,45 +0,0 @@ -package logr - -import ( - "sync" -) - -// CustomFilter allows targets to enable logging via a list of levels. -type CustomFilter struct { - mux sync.RWMutex - levels map[LevelID]Level -} - -// IsEnabled returns true if the specified Level exists in this list. -func (st *CustomFilter) IsEnabled(level Level) bool { - st.mux.RLock() - defer st.mux.RUnlock() - _, ok := st.levels[level.ID] - return ok -} - -// IsStacktraceEnabled returns true if the specified Level requires a stack trace. -func (st *CustomFilter) IsStacktraceEnabled(level Level) bool { - st.mux.RLock() - defer st.mux.RUnlock() - lvl, ok := st.levels[level.ID] - if ok { - return lvl.Stacktrace - } - return false -} - -// Add adds one or more levels to the list. Adding a level enables logging for -// that level on any targets using this CustomFilter. -func (st *CustomFilter) Add(levels ...Level) { - st.mux.Lock() - defer st.mux.Unlock() - - if st.levels == nil { - st.levels = make(map[LevelID]Level) - } - - for _, s := range levels { - st.levels[s.ID] = s - } -} diff --git a/vendor/github.com/mattermost/logr/levelstd.go b/vendor/github.com/mattermost/logr/levelstd.go deleted file mode 100644 index f5e0fa46..00000000 --- a/vendor/github.com/mattermost/logr/levelstd.go +++ /dev/null @@ -1,37 +0,0 @@ -package logr - -// StdFilter allows targets to filter via classic log levels where any level -// beyond a certain verbosity/severity is enabled. -type StdFilter struct { - Lvl Level - Stacktrace Level -} - -// IsEnabled returns true if the specified Level is at or above this verbosity. Also -// determines if a stack trace is required. -func (lt StdFilter) IsEnabled(level Level) bool { - return level.ID <= lt.Lvl.ID -} - -// IsStacktraceEnabled returns true if the specified Level requires a stack trace. -func (lt StdFilter) IsStacktraceEnabled(level Level) bool { - return level.ID <= lt.Stacktrace.ID -} - -var ( - // Panic is the highest level of severity. Logs the message and then panics. - Panic = Level{ID: 0, Name: "panic"} - // Fatal designates a catastrophic error. Logs the message and then calls - // `logr.Exit(1)`. - Fatal = Level{ID: 1, Name: "fatal"} - // Error designates a serious but possibly recoverable error. - Error = Level{ID: 2, Name: "error"} - // Warn designates non-critical error. - Warn = Level{ID: 3, Name: "warn"} - // Info designates information regarding application events. - Info = Level{ID: 4, Name: "info"} - // Debug designates verbose information typically used for debugging. - Debug = Level{ID: 5, Name: "debug"} - // Trace designates the highest verbosity of log output. - Trace = Level{ID: 6, Name: "trace"} -) diff --git a/vendor/github.com/mattermost/logr/logger.go b/vendor/github.com/mattermost/logr/logger.go deleted file mode 100644 index c2386312..00000000 --- a/vendor/github.com/mattermost/logr/logger.go +++ /dev/null @@ -1,218 +0,0 @@ -package logr - -import ( - "fmt" -) - -// Fields type, used to pass to `WithFields`. -type Fields map[string]interface{} - -// Logger provides context for logging via fields. -type Logger struct { - logr *Logr - fields Fields -} - -// Logr returns the `Logr` instance that created this `Logger`. -func (logger Logger) Logr() *Logr { - return logger.logr -} - -// WithField creates a new `Logger` with any existing fields -// plus the new one. -func (logger Logger) WithField(key string, value interface{}) Logger { - return logger.WithFields(Fields{key: value}) -} - -// WithFields creates a new `Logger` with any existing fields -// plus the new ones. -func (logger Logger) WithFields(fields Fields) Logger { - l := Logger{logr: logger.logr} - // if parent has no fields then avoid creating a new map. - oldLen := len(logger.fields) - if oldLen == 0 { - l.fields = fields - return l - } - - l.fields = make(Fields, len(fields)+oldLen) - for k, v := range logger.fields { - l.fields[k] = v - } - for k, v := range fields { - l.fields[k] = v - } - return l -} - -// Log checks that the level matches one or more targets, and -// if so, generates a log record that is added to the Logr queue. -// Arguments are handled in the manner of fmt.Print. -func (logger Logger) Log(lvl Level, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, "", args, status.Stacktrace) - logger.logr.enqueue(rec) - } -} - -// Trace is a convenience method equivalent to `Log(TraceLevel, args...)`. -func (logger Logger) Trace(args ...interface{}) { - logger.Log(Trace, args...) -} - -// Debug is a convenience method equivalent to `Log(DebugLevel, args...)`. -func (logger Logger) Debug(args ...interface{}) { - logger.Log(Debug, args...) -} - -// Print ensures compatibility with std lib logger. -func (logger Logger) Print(args ...interface{}) { - logger.Info(args...) -} - -// Info is a convenience method equivalent to `Log(InfoLevel, args...)`. -func (logger Logger) Info(args ...interface{}) { - logger.Log(Info, args...) -} - -// Warn is a convenience method equivalent to `Log(WarnLevel, args...)`. -func (logger Logger) Warn(args ...interface{}) { - logger.Log(Warn, args...) -} - -// Error is a convenience method equivalent to `Log(ErrorLevel, args...)`. -func (logger Logger) Error(args ...interface{}) { - logger.Log(Error, args...) -} - -// Fatal is a convenience method equivalent to `Log(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatal(args ...interface{}) { - logger.Log(Fatal, args...) - logger.logr.exit(1) -} - -// Panic is a convenience method equivalent to `Log(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panic(args ...interface{}) { - logger.Log(Panic, args...) - panic(fmt.Sprint(args...)) -} - -// -// Printf style -// - -// Logf checks that the level matches one or more targets, and -// if so, generates a log record that is added to the main -// queue (channel). Arguments are handled in the manner of fmt.Printf. -func (logger Logger) Logf(lvl Level, format string, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, format, args, status.Stacktrace) - logger.logr.enqueue(rec) - } -} - -// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`. -func (logger Logger) Tracef(format string, args ...interface{}) { - logger.Logf(Trace, format, args...) -} - -// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`. -func (logger Logger) Debugf(format string, args ...interface{}) { - logger.Logf(Debug, format, args...) -} - -// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`. -func (logger Logger) Infof(format string, args ...interface{}) { - logger.Logf(Info, format, args...) -} - -// Printf ensures compatibility with std lib logger. -func (logger Logger) Printf(format string, args ...interface{}) { - logger.Infof(format, args...) -} - -// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`. -func (logger Logger) Warnf(format string, args ...interface{}) { - logger.Logf(Warn, format, args...) -} - -// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`. -func (logger Logger) Errorf(format string, args ...interface{}) { - logger.Logf(Error, format, args...) -} - -// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatalf(format string, args ...interface{}) { - logger.Logf(Fatal, format, args...) - logger.logr.exit(1) -} - -// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panicf(format string, args ...interface{}) { - logger.Logf(Panic, format, args...) -} - -// -// Println style -// - -// Logln checks that the level matches one or more targets, and -// if so, generates a log record that is added to the main -// queue (channel). Arguments are handled in the manner of fmt.Println. -func (logger Logger) Logln(lvl Level, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, "", args, status.Stacktrace) - rec.newline = true - logger.logr.enqueue(rec) - } -} - -// Traceln is a convenience method equivalent to `Logln(TraceLevel, args...)`. -func (logger Logger) Traceln(args ...interface{}) { - logger.Logln(Trace, args...) -} - -// Debugln is a convenience method equivalent to `Logln(DebugLevel, args...)`. -func (logger Logger) Debugln(args ...interface{}) { - logger.Logln(Debug, args...) -} - -// Infoln is a convenience method equivalent to `Logln(InfoLevel, args...)`. -func (logger Logger) Infoln(args ...interface{}) { - logger.Logln(Info, args...) -} - -// Println ensures compatibility with std lib logger. -func (logger Logger) Println(args ...interface{}) { - logger.Infoln(args...) -} - -// Warnln is a convenience method equivalent to `Logln(WarnLevel, args...)`. -func (logger Logger) Warnln(args ...interface{}) { - logger.Logln(Warn, args...) -} - -// Errorln is a convenience method equivalent to `Logln(ErrorLevel, args...)`. -func (logger Logger) Errorln(args ...interface{}) { - logger.Logln(Error, args...) -} - -// Fatalln is a convenience method equivalent to `Logln(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatalln(args ...interface{}) { - logger.Logln(Fatal, args...) - logger.logr.exit(1) -} - -// Panicln is a convenience method equivalent to `Logln(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panicln(args ...interface{}) { - logger.Logln(Panic, args...) -} diff --git a/vendor/github.com/mattermost/logr/logr.go b/vendor/github.com/mattermost/logr/logr.go deleted file mode 100644 index 631366a5..00000000 --- a/vendor/github.com/mattermost/logr/logr.go +++ /dev/null @@ -1,664 +0,0 @@ -package logr - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "sync" - "time" - - "github.com/wiggin77/cfg" - "github.com/wiggin77/merror" -) - -// Logr maintains a list of log targets and accepts incoming -// log records. -type Logr struct { - tmux sync.RWMutex // target mutex - targets []Target - - mux sync.RWMutex - maxQueueSizeActual int - in chan *LogRec - done chan struct{} - once sync.Once - shutdown bool - lvlCache levelCache - - metricsInitOnce sync.Once - metricsCloseOnce sync.Once - metricsDone chan struct{} - metrics MetricsCollector - queueSizeGauge Gauge - loggedCounter Counter - errorCounter Counter - - bufferPool sync.Pool - - // MaxQueueSize is the maximum number of log records that can be queued. - // If exceeded, `OnQueueFull` is called which determines if the log - // record will be dropped or block until add is successful. - // If this is modified, it must be done before `Configure` or - // `AddTarget`. Defaults to DefaultMaxQueueSize. - MaxQueueSize int - - // OnLoggerError, when not nil, is called any time an internal - // logging error occurs. For example, this can happen when a - // target cannot connect to its data sink. - OnLoggerError func(error) - - // OnQueueFull, when not nil, is called on an attempt to add - // a log record to a full Logr queue. - // `MaxQueueSize` can be used to modify the maximum queue size. - // This function should return quickly, with a bool indicating whether - // the log record should be dropped (true) or block until the log record - // is successfully added (false). If nil then blocking (false) is assumed. - OnQueueFull func(rec *LogRec, maxQueueSize int) bool - - // OnTargetQueueFull, when not nil, is called on an attempt to add - // a log record to a full target queue provided the target supports reporting - // this condition. - // This function should return quickly, with a bool indicating whether - // the log record should be dropped (true) or block until the log record - // is successfully added (false). If nil then blocking (false) is assumed. - OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool - - // OnExit, when not nil, is called when a FatalXXX style log API is called. - // When nil, then the default behavior is to cleanly shut down this Logr and - // call `os.Exit(code)`. - OnExit func(code int) - - // OnPanic, when not nil, is called when a PanicXXX style log API is called. - // When nil, then the default behavior is to cleanly shut down this Logr and - // call `panic(err)`. - OnPanic func(err interface{}) - - // EnqueueTimeout is the amount of time a log record can take to be queued. - // This only applies to blocking enqueue which happen after `logr.OnQueueFull` - // is called and returns false. - EnqueueTimeout time.Duration - - // ShutdownTimeout is the amount of time `logr.Shutdown` can execute before - // timing out. - ShutdownTimeout time.Duration - - // FlushTimeout is the amount of time `logr.Flush` can execute before - // timing out. - FlushTimeout time.Duration - - // UseSyncMapLevelCache can be set to true before the first target is added - // when high concurrency (e.g. >32 cores) is expected. This may improve - // performance with large numbers of cores - benchmark for your use case. - UseSyncMapLevelCache bool - - // MaxPooledFormatBuffer determines the maximum size of a buffer that can be - // pooled. To reduce allocations, the buffers needed during formatting (etc) - // are pooled. A very large log item will grow a buffer that could stay in - // memory indefinitely. This settings lets you control how big a pooled buffer - // can be - anything larger will be garbage collected after use. - // Defaults to 1MB. - MaxPooledBuffer int - - // DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer. - DisableBufferPool bool - - // MetricsUpdateFreqMillis determines how often polled metrics are updated - // when metrics are enabled. - MetricsUpdateFreqMillis int64 -} - -// Configure adds/removes targets via the supplied `Config`. -func (logr *Logr) Configure(config *cfg.Config) error { - // TODO - return fmt.Errorf("not implemented yet") -} - -func (logr *Logr) ensureInit() { - logr.once.Do(func() { - defer func() { - go logr.start() - }() - - logr.mux.Lock() - defer logr.mux.Unlock() - - logr.maxQueueSizeActual = logr.MaxQueueSize - if logr.maxQueueSizeActual == 0 { - logr.maxQueueSizeActual = DefaultMaxQueueSize - } - - if logr.maxQueueSizeActual < 0 { - logr.maxQueueSizeActual = 0 - } - - logr.in = make(chan *LogRec, logr.maxQueueSizeActual) - logr.done = make(chan struct{}) - - if logr.UseSyncMapLevelCache { - logr.lvlCache = &syncMapLevelCache{} - } else { - logr.lvlCache = &arrayLevelCache{} - } - - if logr.MaxPooledBuffer == 0 { - logr.MaxPooledBuffer = DefaultMaxPooledBuffer - } - logr.bufferPool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - } - - logr.lvlCache.setup() - }) -} - -// AddTarget adds one or more targets to the logger which will receive -// log records for outputting. -func (logr *Logr) AddTarget(targets ...Target) error { - if logr.IsShutdown() { - return fmt.Errorf("AddTarget called after Logr shut down") - } - - logr.ensureInit() - metrics := logr.getMetricsCollector() - defer logr.ResetLevelCache() // call this after tmux is released - - logr.tmux.Lock() - defer logr.tmux.Unlock() - - errs := merror.New() - for _, t := range targets { - if t == nil { - continue - } - - logr.targets = append(logr.targets, t) - if metrics != nil { - if tm, ok := t.(TargetWithMetrics); ok { - if err := tm.EnableMetrics(metrics, logr.MetricsUpdateFreqMillis); err != nil { - errs.Append(err) - } - } - } - } - return errs.ErrorOrNil() -} - -// NewLogger creates a Logger using defaults. A `Logger` is light-weight -// enough to create on-demand, but typically one or more Loggers are -// created and re-used. -func (logr *Logr) NewLogger() Logger { - logger := Logger{logr: logr} - return logger -} - -var levelStatusDisabled = LevelStatus{} - -// IsLevelEnabled returns true if at least one target has the specified -// level enabled. The result is cached so that subsequent checks are fast. -func (logr *Logr) IsLevelEnabled(lvl Level) LevelStatus { - status, ok := logr.isLevelEnabledFromCache(lvl) - if ok { - return status - } - - // Check each target. - logr.tmux.RLock() - for _, t := range logr.targets { - e, s := t.IsLevelEnabled(lvl) - if e { - status.Enabled = true - if s { - status.Stacktrace = true - break // if both enabled then no sense checking more targets - } - } - } - logr.tmux.RUnlock() - - // Cache and return the result. - if err := logr.updateLevelCache(lvl.ID, status); err != nil { - logr.ReportError(err) - return LevelStatus{} - } - return status -} - -func (logr *Logr) isLevelEnabledFromCache(lvl Level) (LevelStatus, bool) { - logr.mux.RLock() - defer logr.mux.RUnlock() - - // Don't accept new log records after shutdown. - if logr.shutdown { - return levelStatusDisabled, true - } - - // Check cache. lvlCache may still be nil if no targets added. - if logr.lvlCache == nil { - return levelStatusDisabled, true - } - status, ok := logr.lvlCache.get(lvl.ID) - if ok { - return status, true - } - return LevelStatus{}, false -} - -func (logr *Logr) updateLevelCache(id LevelID, status LevelStatus) error { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.lvlCache != nil { - return logr.lvlCache.put(id, status) - } - return nil -} - -// HasTargets returns true only if at least one target exists within the Logr. -func (logr *Logr) HasTargets() bool { - logr.tmux.RLock() - defer logr.tmux.RUnlock() - return len(logr.targets) > 0 -} - -// TargetInfo provides name and type for a Target. -type TargetInfo struct { - Name string - Type string -} - -// TargetInfos enumerates all the targets added to this Logr. -// The resulting slice represents a snapshot at time of calling. -func (logr *Logr) TargetInfos() []TargetInfo { - logr.tmux.RLock() - defer logr.tmux.RUnlock() - - infos := make([]TargetInfo, 0) - - for _, t := range logr.targets { - inf := TargetInfo{ - Name: fmt.Sprintf("%v", t), - Type: fmt.Sprintf("%T", t), - } - infos = append(infos, inf) - } - return infos -} - -// RemoveTargets safely removes one or more targets based on the filtering method. -// f should return true to delete the target, false to keep it. -// When removing a target, best effort is made to write any queued log records before -// closing, with cxt determining how much time can be spent in total. -// Note, keep the timeout short since this method blocks certain logging operations. -func (logr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error { - var removed bool - defer func() { - if removed { - // call this after tmux is released since - // it will lock mux and we don't want to - // introduce possible deadlock. - logr.ResetLevelCache() - } - }() - - errs := merror.New() - - logr.tmux.Lock() - defer logr.tmux.Unlock() - - cp := make([]Target, 0) - - for _, t := range logr.targets { - inf := TargetInfo{ - Name: fmt.Sprintf("%v", t), - Type: fmt.Sprintf("%T", t), - } - if f(inf) { - if err := t.Shutdown(cxt); err != nil { - errs.Append(err) - } - removed = true - } else { - cp = append(cp, t) - } - } - logr.targets = cp - return errs.ErrorOrNil() -} - -// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is -// called any time a Target is added or a target's level is changed. -func (logr *Logr) ResetLevelCache() { - // Write lock so that new cache entries cannot be stored while we - // clear the cache. - logr.mux.Lock() - defer logr.mux.Unlock() - logr.resetLevelCache() -} - -// resetLevelCache empties the level cache without locking. -// mux.Lock must be held before calling this function. -func (logr *Logr) resetLevelCache() { - // lvlCache may still be nil if no targets added. - if logr.lvlCache != nil { - logr.lvlCache.clear() - } -} - -// enqueue adds a log record to the logr queue. If the queue is full then -// this function either blocks or the log record is dropped, depending on -// the result of calling `OnQueueFull`. -func (logr *Logr) enqueue(rec *LogRec) { - if logr.in == nil { - logr.ReportError(fmt.Errorf("AddTarget or Configure must be called before enqueue")) - } - - select { - case logr.in <- rec: - default: - if logr.OnQueueFull != nil && logr.OnQueueFull(rec, logr.maxQueueSizeActual) { - return // drop the record - } - select { - case <-time.After(logr.enqueueTimeout()): - logr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec)) - case logr.in <- rec: // block until success or timeout - } - } -} - -// exit is called by one of the FatalXXX style APIS. If `logr.OnExit` is not nil -// then that method is called, otherwise the default behavior is to shut down this -// Logr cleanly then call `os.Exit(code)`. -func (logr *Logr) exit(code int) { - if logr.OnExit != nil { - logr.OnExit(code) - return - } - - if err := logr.Shutdown(); err != nil { - logr.ReportError(err) - } - os.Exit(code) -} - -// panic is called by one of the PanicXXX style APIS. If `logr.OnPanic` is not nil -// then that method is called, otherwise the default behavior is to shut down this -// Logr cleanly then call `panic(err)`. -func (logr *Logr) panic(err interface{}) { - if logr.OnPanic != nil { - logr.OnPanic(err) - return - } - - if err := logr.Shutdown(); err != nil { - logr.ReportError(err) - } - panic(err) -} - -// Flush blocks while flushing the logr queue and all target queues, by -// writing existing log records to valid targets. -// Any attempts to add new log records will block until flush is complete. -// `logr.FlushTimeout` determines how long flush can execute before -// timing out. Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) Flush() error { - ctx, cancel := context.WithTimeout(context.Background(), logr.flushTimeout()) - defer cancel() - return logr.FlushWithTimeout(ctx) -} - -// Flush blocks while flushing the logr queue and all target queues, by -// writing existing log records to valid targets. -// Any attempts to add new log records will block until flush is complete. -// Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) FlushWithTimeout(ctx context.Context) error { - if !logr.HasTargets() { - return nil - } - - if logr.IsShutdown() { - return errors.New("Flush called on shut down Logr") - } - - rec := newFlushLogRec(logr.NewLogger()) - logr.enqueue(rec) - - select { - case <-ctx.Done(): - return newTimeoutError("logr queue shutdown timeout") - case <-rec.flush: - } - return nil -} - -// IsShutdown returns true if this Logr instance has been shut down. -// No further log records can be enqueued and no targets added after -// shutdown. -func (logr *Logr) IsShutdown() bool { - logr.mux.Lock() - defer logr.mux.Unlock() - return logr.shutdown -} - -// Shutdown cleanly stops the logging engine after making best efforts -// to flush all targets. Call this function right before application -// exit - logr cannot be restarted once shut down. -// `logr.ShutdownTimeout` determines how long shutdown can execute before -// timing out. Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) Shutdown() error { - ctx, cancel := context.WithTimeout(context.Background(), logr.shutdownTimeout()) - defer cancel() - return logr.ShutdownWithTimeout(ctx) -} - -// Shutdown cleanly stops the logging engine after making best efforts -// to flush all targets. Call this function right before application -// exit - logr cannot be restarted once shut down. -// Use `IsTimeoutError` to determine if the returned error is due to a -// timeout. -func (logr *Logr) ShutdownWithTimeout(ctx context.Context) error { - logr.mux.Lock() - if logr.shutdown { - logr.mux.Unlock() - return errors.New("Shutdown called again after shut down") - } - logr.shutdown = true - logr.resetLevelCache() - logr.mux.Unlock() - - logr.metricsCloseOnce.Do(func() { - if logr.metricsDone != nil { - close(logr.metricsDone) - } - }) - - errs := merror.New() - - // close the incoming channel and wait for read loop to exit. - if logr.in != nil { - close(logr.in) - select { - case <-ctx.Done(): - errs.Append(newTimeoutError("logr queue shutdown timeout")) - case <-logr.done: - } - } - - // logr.in channel should now be drained to targets and no more log records - // can be added. - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, t := range logr.targets { - err := t.Shutdown(ctx) - if err != nil { - errs.Append(err) - } - } - return errs.ErrorOrNil() -} - -// ReportError is used to notify the host application of any internal logging errors. -// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is -// output to `os.Stderr`. -func (logr *Logr) ReportError(err interface{}) { - logr.incErrorCounter() - - if logr.OnLoggerError == nil { - fmt.Fprintln(os.Stderr, err) - return - } - logr.OnLoggerError(fmt.Errorf("%v", err)) -} - -// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection. -func (logr *Logr) BorrowBuffer() *bytes.Buffer { - if logr.DisableBufferPool { - return &bytes.Buffer{} - } - return logr.bufferPool.Get().(*bytes.Buffer) -} - -// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only -// retained if less than MaxPooledBuffer. -func (logr *Logr) ReleaseBuffer(buf *bytes.Buffer) { - if !logr.DisableBufferPool && buf.Cap() < logr.MaxPooledBuffer { - buf.Reset() - logr.bufferPool.Put(buf) - } -} - -// enqueueTimeout returns amount of time a log record can take to be queued. -// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called -// and returns false. -func (logr *Logr) enqueueTimeout() time.Duration { - if logr.EnqueueTimeout == 0 { - return DefaultEnqueueTimeout - } - return logr.EnqueueTimeout -} - -// shutdownTimeout returns the timeout duration for `logr.Shutdown`. -func (logr *Logr) shutdownTimeout() time.Duration { - if logr.ShutdownTimeout == 0 { - return DefaultShutdownTimeout - } - return logr.ShutdownTimeout -} - -// flushTimeout returns the timeout duration for `logr.Flush`. -func (logr *Logr) flushTimeout() time.Duration { - if logr.FlushTimeout == 0 { - return DefaultFlushTimeout - } - return logr.FlushTimeout -} - -// start selects on incoming log records until done channel signals. -// Incoming log records are fanned out to all log targets. -func (logr *Logr) start() { - defer func() { - if r := recover(); r != nil { - logr.ReportError(r) - go logr.start() - } - }() - - for rec := range logr.in { - if rec.flush != nil { - logr.flush(rec.flush) - } else { - rec.prep() - logr.fanout(rec) - } - } - close(logr.done) -} - -// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until -// logr is closed. -func (logr *Logr) startMetricsUpdater() { - for { - updateFreq := logr.getMetricsUpdateFreqMillis() - if updateFreq == 0 { - updateFreq = DefMetricsUpdateFreqMillis - } - if updateFreq < 250 { - updateFreq = 250 // don't peg the CPU - } - - select { - case <-logr.metricsDone: - return - case <-time.After(time.Duration(updateFreq) * time.Millisecond): - logr.setQueueSizeGauge(float64(len(logr.in))) - } - } -} - -func (logr *Logr) getMetricsUpdateFreqMillis() int64 { - logr.mux.RLock() - defer logr.mux.RUnlock() - return logr.MetricsUpdateFreqMillis -} - -// fanout pushes a LogRec to all targets. -func (logr *Logr) fanout(rec *LogRec) { - var target Target - defer func() { - if r := recover(); r != nil { - logr.ReportError(fmt.Errorf("fanout failed for target %s, %v", target, r)) - } - }() - - var logged bool - defer func() { - if logged { - logr.incLoggedCounter() // call this after tmux is released - } - }() - - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target = range logr.targets { - if enabled, _ := target.IsLevelEnabled(rec.Level()); enabled { - target.Log(rec) - logged = true - } - } -} - -// flush drains the queue and notifies when done. -func (logr *Logr) flush(done chan<- struct{}) { - // first drain the logr queue. -loop: - for { - var rec *LogRec - select { - case rec = <-logr.in: - if rec.flush == nil { - rec.prep() - logr.fanout(rec) - } - default: - break loop - } - } - - logger := logr.NewLogger() - - // drain all the targets; block until finished. - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target := range logr.targets { - rec := newFlushLogRec(logger) - target.Log(rec) - <-rec.flush - } - done <- struct{}{} -} diff --git a/vendor/github.com/mattermost/logr/logrec.go b/vendor/github.com/mattermost/logr/logrec.go deleted file mode 100644 index 9428aaec..00000000 --- a/vendor/github.com/mattermost/logr/logrec.go +++ /dev/null @@ -1,189 +0,0 @@ -package logr - -import ( - "fmt" - "runtime" - "strings" - "sync" - "time" -) - -var ( - logrPkg string -) - -func init() { - // Calc current package name - pcs := make([]uintptr, 2) - _ = runtime.Callers(0, pcs) - tmp := runtime.FuncForPC(pcs[1]).Name() - logrPkg = getPackageName(tmp) -} - -// LogRec collects raw, unformatted data to be logged. -// TODO: pool these? how to reliably know when targets are done with them? Copy for each target? -type LogRec struct { - mux sync.RWMutex - time time.Time - - level Level - logger Logger - - template string - newline bool - args []interface{} - - stackPC []uintptr - stackCount int - - // flushes Logr and target queues when not nil. - flush chan struct{} - - // remaining fields calculated by `prep` - msg string - frames []runtime.Frame -} - -// NewLogRec creates a new LogRec with the current time and optional stack trace. -func NewLogRec(lvl Level, logger Logger, template string, args []interface{}, incStacktrace bool) *LogRec { - rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args} - if incStacktrace { - rec.stackPC = make([]uintptr, DefaultMaxStackFrames) - rec.stackCount = runtime.Callers(2, rec.stackPC) - } - return rec -} - -// newFlushLogRec creates a LogRec that flushes the Logr queue and -// any target queues that support flushing. -func newFlushLogRec(logger Logger) *LogRec { - return &LogRec{logger: logger, flush: make(chan struct{})} -} - -// prep resolves all args and field values to strings, and -// resolves stack trace to frames. -func (rec *LogRec) prep() { - rec.mux.Lock() - defer rec.mux.Unlock() - - // resolve args - if rec.template == "" { - if rec.newline { - rec.msg = fmt.Sprintln(rec.args...) - } else { - rec.msg = fmt.Sprint(rec.args...) - } - } else { - rec.msg = fmt.Sprintf(rec.template, rec.args...) - } - - // resolve stack trace - if rec.stackCount > 0 { - frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount]) - for { - f, more := frames.Next() - rec.frames = append(rec.frames, f) - if !more { - break - } - } - - // remove leading logr package entries. - var start int - for i, frame := range rec.frames { - pkg := getPackageName(frame.Function) - if pkg != "" && pkg != logrPkg { - start = i - break - } - } - rec.frames = rec.frames[start:] - } -} - -// WithTime returns a shallow copy of the log record while replacing -// the time. This can be used by targets and formatters to adjust -// the time, or take ownership of the log record. -func (rec *LogRec) WithTime(time time.Time) *LogRec { - rec.mux.RLock() - defer rec.mux.RUnlock() - - return &LogRec{ - time: time, - level: rec.level, - logger: rec.logger, - template: rec.template, - newline: rec.newline, - args: rec.args, - msg: rec.msg, - stackPC: rec.stackPC, - stackCount: rec.stackCount, - frames: rec.frames, - } -} - -// Logger returns the `Logger` that created this `LogRec`. -func (rec *LogRec) Logger() Logger { - return rec.logger -} - -// Time returns this log record's time stamp. -func (rec *LogRec) Time() time.Time { - // no locking needed as this field is not mutated. - return rec.time -} - -// Level returns this log record's Level. -func (rec *LogRec) Level() Level { - // no locking needed as this field is not mutated. - return rec.level -} - -// Fields returns this log record's Fields. -func (rec *LogRec) Fields() Fields { - // no locking needed as this field is not mutated. - return rec.logger.fields -} - -// Msg returns this log record's message text. -func (rec *LogRec) Msg() string { - rec.mux.RLock() - defer rec.mux.RUnlock() - return rec.msg -} - -// StackFrames returns this log record's stack frames or -// nil if no stack trace was required. -func (rec *LogRec) StackFrames() []runtime.Frame { - rec.mux.RLock() - defer rec.mux.RUnlock() - return rec.frames -} - -// String returns a string representation of this log record. -func (rec *LogRec) String() string { - if rec.flush != nil { - return "[flusher]" - } - - f := &DefaultFormatter{} - buf := rec.logger.logr.BorrowBuffer() - defer rec.logger.logr.ReleaseBuffer(buf) - buf, _ = f.Format(rec, true, buf) - return strings.TrimSpace(buf.String()) -} - -// getPackageName reduces a fully qualified function name to the package name -// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go -func getPackageName(f string) string { - for { - lastPeriod := strings.LastIndex(f, ".") - lastSlash := strings.LastIndex(f, "/") - if lastPeriod > lastSlash { - f = f[:lastPeriod] - } else { - break - } - } - return f -} diff --git a/vendor/github.com/mattermost/logr/metrics.go b/vendor/github.com/mattermost/logr/metrics.go deleted file mode 100644 index 24fe22b6..00000000 --- a/vendor/github.com/mattermost/logr/metrics.go +++ /dev/null @@ -1,117 +0,0 @@ -package logr - -import ( - "errors" - - "github.com/wiggin77/merror" -) - -const ( - DefMetricsUpdateFreqMillis = 15000 // 15 seconds -) - -// Counter is a simple metrics sink that can only increment a value. -// Implementations are external to Logr and provided via `MetricsCollector`. -type Counter interface { - // Inc increments the counter by 1. Use Add to increment it by arbitrary non-negative values. - Inc() - // Add adds the given value to the counter. It panics if the value is < 0. - Add(float64) -} - -// Gauge is a simple metrics sink that can receive values and increase or decrease. -// Implementations are external to Logr and provided via `MetricsCollector`. -type Gauge interface { - // Set sets the Gauge to an arbitrary value. - Set(float64) - // Add adds the given value to the Gauge. (The value can be negative, resulting in a decrease of the Gauge.) - Add(float64) - // Sub subtracts the given value from the Gauge. (The value can be negative, resulting in an increase of the Gauge.) - Sub(float64) -} - -// MetricsCollector provides a way for users of this Logr package to have metrics pushed -// in an efficient way to any backend, e.g. Prometheus. -// For each target added to Logr, the supplied MetricsCollector will provide a Gauge -// and Counters that will be called frequently as logging occurs. -type MetricsCollector interface { - // QueueSizeGauge returns a Gauge that will be updated by the named target. - QueueSizeGauge(target string) (Gauge, error) - // LoggedCounter returns a Counter that will be incremented by the named target. - LoggedCounter(target string) (Counter, error) - // ErrorCounter returns a Counter that will be incremented by the named target. - ErrorCounter(target string) (Counter, error) - // DroppedCounter returns a Counter that will be incremented by the named target. - DroppedCounter(target string) (Counter, error) - // BlockedCounter returns a Counter that will be incremented by the named target. - BlockedCounter(target string) (Counter, error) -} - -// TargetWithMetrics is a target that provides metrics. -type TargetWithMetrics interface { - EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error -} - -func (logr *Logr) getMetricsCollector() MetricsCollector { - logr.mux.RLock() - defer logr.mux.RUnlock() - return logr.metrics -} - -// SetMetricsCollector enables metrics collection by supplying a MetricsCollector. -// The MetricsCollector provides counters and gauges that are updated by log targets. -func (logr *Logr) SetMetricsCollector(collector MetricsCollector) error { - if collector == nil { - return errors.New("collector cannot be nil") - } - - logr.mux.Lock() - logr.metrics = collector - logr.queueSizeGauge, _ = collector.QueueSizeGauge("_logr") - logr.loggedCounter, _ = collector.LoggedCounter("_logr") - logr.errorCounter, _ = collector.ErrorCounter("_logr") - logr.mux.Unlock() - - logr.metricsInitOnce.Do(func() { - logr.metricsDone = make(chan struct{}) - go logr.startMetricsUpdater() - }) - - merr := merror.New() - - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target := range logr.targets { - if tm, ok := target.(TargetWithMetrics); ok { - if err := tm.EnableMetrics(collector, logr.MetricsUpdateFreqMillis); err != nil { - merr.Append(err) - } - } - - } - return merr.ErrorOrNil() -} - -func (logr *Logr) setQueueSizeGauge(val float64) { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.queueSizeGauge != nil { - logr.queueSizeGauge.Set(val) - } -} - -func (logr *Logr) incLoggedCounter() { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.loggedCounter != nil { - logr.loggedCounter.Inc() - } -} - -func (logr *Logr) incErrorCounter() { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.errorCounter != nil { - logr.errorCounter.Inc() - } -} diff --git a/vendor/github.com/mattermost/logr/target.go b/vendor/github.com/mattermost/logr/target.go deleted file mode 100644 index f8e7bf75..00000000 --- a/vendor/github.com/mattermost/logr/target.go +++ /dev/null @@ -1,299 +0,0 @@ -package logr - -import ( - "context" - "fmt" - "os" - "sync" - "time" -) - -// Target represents a destination for log records such as file, -// database, TCP socket, etc. -type Target interface { - // SetName provides an optional name for the target. - SetName(name string) - - // IsLevelEnabled returns true if this target should emit - // logs for the specified level. Also determines if - // a stack trace is required. - IsLevelEnabled(Level) (enabled bool, stacktrace bool) - - // Formatter returns the Formatter associated with this Target. - Formatter() Formatter - - // Log outputs the log record to this target's destination. - Log(rec *LogRec) - - // Shutdown makes best effort to flush target queue and - // frees/closes all resources. - Shutdown(ctx context.Context) error -} - -// RecordWriter can convert a LogRecord to bytes and output to some data sink. -type RecordWriter interface { - Write(rec *LogRec) error -} - -// Basic provides the basic functionality of a Target that can be used -// to more easily compose your own Targets. To use, just embed Basic -// in your target type, implement `RecordWriter`, and call `(*Basic).Start`. -type Basic struct { - target Target - - filter Filter - formatter Formatter - - in chan *LogRec - done chan struct{} - w RecordWriter - - mux sync.RWMutex - name string - - metrics bool - queueSizeGauge Gauge - loggedCounter Counter - errorCounter Counter - droppedCounter Counter - blockedCounter Counter - - metricsUpdateFreqMillis int64 -} - -// Start initializes this target helper and starts accepting log records for processing. -func (b *Basic) Start(target Target, rw RecordWriter, filter Filter, formatter Formatter, maxQueued int) { - if filter == nil { - filter = &StdFilter{Lvl: Fatal} - } - if formatter == nil { - formatter = &DefaultFormatter{} - } - - b.target = target - b.filter = filter - b.formatter = formatter - b.in = make(chan *LogRec, maxQueued) - b.done = make(chan struct{}, 1) - b.w = rw - go b.start() - - if b.hasMetrics() { - go b.startMetricsUpdater() - } -} - -func (b *Basic) SetName(name string) { - b.mux.Lock() - defer b.mux.Unlock() - b.name = name -} - -// IsLevelEnabled returns true if this target should emit -// logs for the specified level. Also determines if -// a stack trace is required. -func (b *Basic) IsLevelEnabled(lvl Level) (enabled bool, stacktrace bool) { - return b.filter.IsEnabled(lvl), b.filter.IsStacktraceEnabled(lvl) -} - -// Formatter returns the Formatter associated with this Target. -func (b *Basic) Formatter() Formatter { - return b.formatter -} - -// Shutdown stops processing log records after making best -// effort to flush queue. -func (b *Basic) Shutdown(ctx context.Context) error { - // close the incoming channel and wait for read loop to exit. - close(b.in) - select { - case <-ctx.Done(): - case <-b.done: - } - - // b.in channel should now be drained. - return nil -} - -// Log outputs the log record to this targets destination. -func (b *Basic) Log(rec *LogRec) { - lgr := rec.Logger().Logr() - select { - case b.in <- rec: - default: - handler := lgr.OnTargetQueueFull - if handler != nil && handler(b.target, rec, cap(b.in)) { - b.incDroppedCounter() - return // drop the record - } - b.incBlockedCounter() - - select { - case <-time.After(lgr.enqueueTimeout()): - lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec)) - case b.in <- rec: // block until success or timeout - } - } -} - -// Metrics enables metrics collection using the provided MetricsCollector. -func (b *Basic) EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error { - name := fmt.Sprintf("%v", b) - - b.mux.Lock() - defer b.mux.Unlock() - - b.metrics = true - b.metricsUpdateFreqMillis = updateFreqMillis - - var err error - - if b.queueSizeGauge, err = collector.QueueSizeGauge(name); err != nil { - return err - } - if b.loggedCounter, err = collector.LoggedCounter(name); err != nil { - return err - } - if b.errorCounter, err = collector.ErrorCounter(name); err != nil { - return err - } - if b.droppedCounter, err = collector.DroppedCounter(name); err != nil { - return err - } - if b.blockedCounter, err = collector.BlockedCounter(name); err != nil { - return err - } - return nil -} - -func (b *Basic) hasMetrics() bool { - b.mux.RLock() - defer b.mux.RUnlock() - return b.metrics -} - -func (b *Basic) setQueueSizeGauge(val float64) { - b.mux.RLock() - defer b.mux.RUnlock() - if b.queueSizeGauge != nil { - b.queueSizeGauge.Set(val) - } -} - -func (b *Basic) incLoggedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.loggedCounter != nil { - b.loggedCounter.Inc() - } -} - -func (b *Basic) incErrorCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.errorCounter != nil { - b.errorCounter.Inc() - } -} - -func (b *Basic) incDroppedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.droppedCounter != nil { - b.droppedCounter.Inc() - } -} - -func (b *Basic) incBlockedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.blockedCounter != nil { - b.blockedCounter.Inc() - } -} - -// String returns a name for this target. Use `SetName` to specify a name. -func (b *Basic) String() string { - b.mux.RLock() - defer b.mux.RUnlock() - - if b.name != "" { - return b.name - } - return fmt.Sprintf("%T", b.target) -} - -// Start accepts log records via In channel and writes to the -// supplied writer, until Done channel signaled. -func (b *Basic) start() { - defer func() { - if r := recover(); r != nil { - fmt.Fprintln(os.Stderr, "Basic.start -- ", r) - go b.start() - } - }() - - for rec := range b.in { - if rec.flush != nil { - b.flush(rec.flush) - } else { - err := b.w.Write(rec) - if err != nil { - b.incErrorCounter() - rec.Logger().Logr().ReportError(err) - } else { - b.incLoggedCounter() - } - } - } - close(b.done) -} - -// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until -// target is closed. -func (b *Basic) startMetricsUpdater() { - for { - updateFreq := b.getMetricsUpdateFreqMillis() - if updateFreq == 0 { - updateFreq = DefMetricsUpdateFreqMillis - } - if updateFreq < 250 { - updateFreq = 250 // don't peg the CPU - } - - select { - case <-b.done: - return - case <-time.After(time.Duration(updateFreq) * time.Millisecond): - b.setQueueSizeGauge(float64(len(b.in))) - } - } -} - -func (b *Basic) getMetricsUpdateFreqMillis() int64 { - b.mux.RLock() - defer b.mux.RUnlock() - return b.metricsUpdateFreqMillis -} - -// flush drains the queue and notifies when done. -func (b *Basic) flush(done chan<- struct{}) { - for { - var rec *LogRec - var err error - select { - case rec = <-b.in: - // ignore any redundant flush records. - if rec.flush == nil { - err = b.w.Write(rec) - if err != nil { - b.incErrorCounter() - rec.Logger().Logr().ReportError(err) - } - } - default: - done <- struct{}{} - return - } - } -} diff --git a/vendor/github.com/mattermost/logr/target/file.go b/vendor/github.com/mattermost/logr/target/file.go deleted file mode 100644 index 0fd50768..00000000 --- a/vendor/github.com/mattermost/logr/target/file.go +++ /dev/null @@ -1,87 +0,0 @@ -package target - -import ( - "context" - "io" - - "github.com/mattermost/logr" - "github.com/wiggin77/merror" - "gopkg.in/natefinch/lumberjack.v2" -) - -type FileOptions struct { - // Filename is the file to write logs to. Backup log files will be retained - // in the same directory. It uses <processname>-lumberjack.log in - // os.TempDir() if empty. - Filename string - - // MaxSize is the maximum size in megabytes of the log file before it gets - // rotated. It defaults to 100 megabytes. - MaxSize int - - // MaxAge is the maximum number of days to retain old log files based on the - // timestamp encoded in their filename. Note that a day is defined as 24 - // hours and may not exactly correspond to calendar days due to daylight - // savings, leap seconds, etc. The default is not to remove old log files - // based on age. - MaxAge int - - // MaxBackups is the maximum number of old log files to retain. The default - // is to retain all old log files (though MaxAge may still cause them to get - // deleted.) - MaxBackups int - - // Compress determines if the rotated log files should be compressed - // using gzip. The default is not to perform compression. - Compress bool -} - -// File outputs log records to a file which can be log rotated based on size or age. -// Uses `https://github.com/natefinch/lumberjack` for rotation. -type File struct { - logr.Basic - out io.WriteCloser -} - -// NewFileTarget creates a target capable of outputting log records to a rotated file. -func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOptions, maxQueue int) *File { - lumber := &lumberjack.Logger{ - Filename: opts.Filename, - MaxSize: opts.MaxSize, - MaxBackups: opts.MaxBackups, - MaxAge: opts.MaxAge, - Compress: opts.Compress, - } - f := &File{out: lumber} - f.Basic.Start(f, f, filter, formatter, maxQueue) - return f -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to a file. -func (f *File) Write(rec *logr.LogRec) error { - _, stacktrace := f.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := f.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = f.out.Write(buf.Bytes()) - return err -} - -// Shutdown flushes any remaining log records and closes the file. -func (f *File) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := f.Basic.Shutdown(ctx) - errs.Append(err) - - err = f.out.Close() - errs.Append(err) - - return errs.ErrorOrNil() -} diff --git a/vendor/github.com/mattermost/logr/target/syslog.go b/vendor/github.com/mattermost/logr/target/syslog.go deleted file mode 100644 index 1d2013b6..00000000 --- a/vendor/github.com/mattermost/logr/target/syslog.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build !windows,!nacl,!plan9 - -package target - -import ( - "context" - "fmt" - "log/syslog" - - "github.com/mattermost/logr" - "github.com/wiggin77/merror" -) - -// Syslog outputs log records to local or remote syslog. -type Syslog struct { - logr.Basic - w *syslog.Writer -} - -// SyslogParams provides parameters for dialing a syslog daemon. -type SyslogParams struct { - Network string - Raddr string - Priority syslog.Priority - Tag string -} - -// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog. -func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) { - writer, err := syslog.Dial(params.Network, params.Raddr, params.Priority, params.Tag) - if err != nil { - return nil, err - } - - s := &Syslog{w: writer} - s.Basic.Start(s, s, filter, formatter, maxQueue) - - return s, nil -} - -// Shutdown stops processing log records after making best -// effort to flush queue. -func (s *Syslog) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := s.Basic.Shutdown(ctx) - errs.Append(err) - - err = s.w.Close() - errs.Append(err) - - return errs.ErrorOrNil() -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to syslog. -func (s *Syslog) Write(rec *logr.LogRec) error { - _, stacktrace := s.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := s.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - txt := buf.String() - - switch rec.Level() { - case logr.Panic, logr.Fatal: - err = s.w.Crit(txt) - case logr.Error: - err = s.w.Err(txt) - case logr.Warn: - err = s.w.Warning(txt) - case logr.Debug, logr.Trace: - err = s.w.Debug(txt) - default: - // logr.Info plus all custom levels. - err = s.w.Info(txt) - } - - if err != nil { - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("syslog write fail: %w", err)) - // syslog writer will try to reconnect. - } - return err -} diff --git a/vendor/github.com/mattermost/logr/target/writer.go b/vendor/github.com/mattermost/logr/target/writer.go deleted file mode 100644 index 2250da51..00000000 --- a/vendor/github.com/mattermost/logr/target/writer.go +++ /dev/null @@ -1,40 +0,0 @@ -package target - -import ( - "io" - "io/ioutil" - - "github.com/mattermost/logr" -) - -// Writer outputs log records to any `io.Writer`. -type Writer struct { - logr.Basic - out io.Writer -} - -// NewWriterTarget creates a target capable of outputting log records to an io.Writer. -func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer { - if out == nil { - out = ioutil.Discard - } - w := &Writer{out: out} - w.Basic.Start(w, w, filter, formatter, maxQueue) - return w -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to the io.Writer. -func (w *Writer) Write(rec *logr.LogRec) error { - _, stacktrace := w.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := w.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = w.out.Write(buf.Bytes()) - return err -} diff --git a/vendor/github.com/mattermost/logr/timeout.go b/vendor/github.com/mattermost/logr/timeout.go deleted file mode 100644 index 37737bcf..00000000 --- a/vendor/github.com/mattermost/logr/timeout.go +++ /dev/null @@ -1,34 +0,0 @@ -package logr - -import "github.com/wiggin77/merror" - -// timeoutError is returned from functions that can timeout. -type timeoutError struct { - text string -} - -// newTimeoutError returns a TimeoutError. -func newTimeoutError(text string) timeoutError { - return timeoutError{text: text} -} - -// IsTimeoutError returns true if err is a TimeoutError. -func IsTimeoutError(err error) bool { - if _, ok := err.(timeoutError); ok { - return true - } - // if a multi-error, return true if any of the errors - // are TimeoutError - if merr, ok := err.(*merror.MError); ok { - for _, e := range merr.Errors() { - if IsTimeoutError(e) { - return true - } - } - } - return false -} - -func (err timeoutError) Error() string { - return err.text -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/LICENSE.txt b/vendor/github.com/mattermost/mattermost-server/v5/LICENSE.txt deleted file mode 100644 index 8382687d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/LICENSE.txt +++ /dev/null @@ -1,897 +0,0 @@ -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/default.json, i18n/, model/, -plugin/ 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/>. diff --git a/vendor/github.com/mattermost/mattermost-server/v5/NOTICE.txt b/vendor/github.com/mattermost/mattermost-server/v5/NOTICE.txt deleted file mode 100644 index 0d3d8dd9..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/NOTICE.txt +++ /dev/null @@ -1,4844 +0,0 @@ -Mattermost Server -© 2015-present Mattermost, Inc. All Rights Reserved. See LICENSE.txt for license information. - -NOTICES: --------- - -This document includes a list of open source components used in Mattermost Server, including those that have been modified. - ------ - -## Go - -This product uses the Go programming language by the Go authors. - -* HOMEPAGE: - * https://golang.org - -* LICENSE: BSD-style - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## Masterminds/squirrel - -This product contains 'squirrel' by GitHub user "Masterminds". - -Fluent SQL generation for golang - -* HOMEPAGE: - * https://github.com/Masterminds/squirrel - -* LICENSE: MIT - -Squirrel -The Masterminds -Copyright (C) 2014-2015, Lann Martin -Copyright (C) 2015-2016, Google -Copyright (C) 2015, Matt Farina and Matt Butcher - -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. - ---- - -## NYTimes/gziphandler - -This product contains 'gziphandler' by The New York Times. - -Go middleware to gzip HTTP responses - -* HOMEPAGE: - * https://github.com/NYTimes/gziphandler - -* LICENSE: Apache-2.0 - - 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 2016-2017 The New York Times Company - - 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. - ---- - -## avct/uasurfer - -This product contains 'uasurfer' by Avocet. - -Go package for fast and reliable abstraction of browser user agent strings. - -* HOMEPAGE: - * https://github.com/avct/uasurfer - -* LICENSE: Apache-2.0 - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - Copyright 2015 Avocet Systems Ltd. - http://avocet.io/opensource - - 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. - - Copyright 2015 Avocet Systems Ltd. - - 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. - ---- - -## blang/semver - -This product contains 'semver' by Benedikt Lang. - -Semantic Versioning (semver) library written in golang - -* HOMEPAGE: - * https://github.com/blang/semver - -* LICENSE: MIT - -The MIT License - -Copyright (c) 2014 Benedikt Lang <github at benediktlang.de> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ---- - -## dgryski/dgoogauth - -This product contains 'dgoogauth' by Damian Gryski. - -Google Authenticator for Go - -* HOMEPAGE: - * https://github.com/dgryski/dgoogauth - -* LICENSE: Apache-2.0 - -Note: An original license file for this dependency is not available. We determined the type of license based on the official project website. The following text has been prepared using a template from the SPDX Workgroup (https://spdx.org) for this type of license. - -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 2019 Damian Gryski - -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. - ---- - -## disintegration/imaging - -This product contains 'imaging' by Grigory Dryapak. - -Imaging is a simple image processing package for Go - -* HOMEPAGE: - * https://github.com/disintegration/imaging - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2012 Grigory Dryapak - -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. - ---- - -## dyatlov/go-opengraph - -This product contains 'go-opengraph' by Vitaly Dyatlov. - -Golang package for parsing OpenGraph data from HTML into regular structures - -* HOMEPAGE: - * https://github.com/dyatlov/go-opengraph - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 Vitaly Dyatlov - -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. - ---- - -## fsnotify/fsnotify - -This product contains 'fsnotify' by GitHub user "fsnotify". - -Cross-platform file system notifications for Go. - -* HOMEPAGE: - * https://github.com/fsnotify/fsnotify - -* LICENSE: BSD-3-Clause - -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## go-ldap/ldap - -This product contains 'ldap' by GitHub user "go-ldap". - -Basic LDAP v3 functionality for the GO programming language. - -* HOMEPAGE: - * https://github.com/go-ldap/ldap - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) -Portions copyright (c) 2015-2016 go-ldap Authors - -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. - ---- - -## go-redis/redis - -This product contains 'redis' by GitHub user "go-redis". - -Type-safe Redis client for Golang - -* HOMEPAGE: - * https://github.com/go-redis/redis - -* LICENSE: BSD-2-Clause - -Copyright (c) 2013 The github.com/go-redis/redis Authors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## go-sql-driver/mysql - -This product contains 'mysql' by Go SQL Drivers. - -Go MySQL Driver is a MySQL driver for Go's (golang) database/sql package - -* HOMEPAGE: - * https://github.com/go-sql-driver/mysql - -* LICENSE: MPL-2.0 - -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. - ---- - -## golang/freetype - -This product contains 'freetype' by Go. - -The Freetype font rasterizer in the Go programming language. - -* HOMEPAGE: - * https://github.com/golang/freetype - -* LICENSE: (FTL OR GPL-2.0) - -Use of the Freetype-Go software is subject to your choice of exactly one of -the following two licenses: - * The FreeType License, which is similar to the original BSD license with - an advertising clause, or - * The GNU General Public License (GPL), version 2 or later. - -The text of these licenses are available in the licenses/ftl.txt and the -licenses/gpl.txt files respectively. They are also available at -http://freetype.sourceforge.net/license.html - -The Luxi fonts in the testdata directory are licensed separately. See the -testdata/COPYING file for details. - ---- - -## gorilla/handlers - -This product contains 'handlers' by Gorilla web toolkit. - -A collection of useful handlers for Go's net/http package. - -* HOMEPAGE: - * https://github.com/gorilla/handlers - -* LICENSE: BSD-2-Clause - -Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## gorilla/mux - -This product contains 'mux' by Gorilla web toolkit. - -A powerful URL router and dispatcher for golang. - -* HOMEPAGE: - * https://github.com/gorilla/mux - -* LICENSE: BSD-3-Clause - -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## gorilla/schema - -This product contains 'schema' by Gorilla web toolkit. - -Package gorilla/schema fills a struct with form values. - -* HOMEPAGE: - * https://github.com/gorilla/schema - -* LICENSE: BSD-3-Clause - -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## gorilla/websocket - -This product contains 'websocket' by Gorilla web toolkit. - -A WebSocket implementation for Go. - -* HOMEPAGE: - * https://github.com/gorilla/websocket - -* LICENSE: BSD-2-Clause - -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## hako/durafmt - -This product contains 'durafmt' by Wesley Hill. - -:clock8: Better time duration formatting in Go! - -* HOMEPAGE: - * https://github.com/hako/durafmt - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2016 Wesley Hill - -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. - ---- - -## hashicorp/go-hclog - -This product contains 'go-hclog' by HashiCorp. - -A common logging package for HashiCorp tools - -* HOMEPAGE: - * https://github.com/hashicorp/go-hclog - -* LICENSE: MIT - -MIT License - -Copyright (c) 2017 HashiCorp - -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. - ---- - -## hashicorp/go-plugin - -This product contains 'go-plugin' by HashiCorp. - -Golang plugin system over RPC. - -* HOMEPAGE: - * https://github.com/hashicorp/go-plugin - -* LICENSE: MPL-2.0 - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. - ---- - -## hashicorp/memberlist - -This product contains 'memberlist' by HashiCorp. - -Golang package for gossip based membership and failure detection - -* HOMEPAGE: - * https://github.com/hashicorp/memberlist - -* LICENSE: MPL-2.0 - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. - ---- - -## icrowley/fake - -This product contains 'fake' by GitHub user "icrowley". - -Fake data generator for Go (Golang) - -* HOMEPAGE: - * https://github.com/icrowley/fake - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Dmitry Afanasyev - -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. - ---- - -## jaytaylor/html2text - -This product contains 'html2text' by J. Elliot Taylor. - -Golang HTML to plaintext conversion library - -* HOMEPAGE: - * https://github.com/jaytaylor/html2text - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 Jay Taylor - -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. - ---- - -## lib/pq - -This product contains 'pq' by GitHub user "lib". - -Pure Go Postgres driver for database/sql - -* HOMEPAGE: - * https://github.com/lib/pq - -* LICENSE: MIT - -Copyright (c) 2011-2013, 'pq' Contributors -Portions Copyright (C) 2011 Blake Mizerany - -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. - ---- - -## mattermost/gorp - -This product contains 'gorp' by Mattermost (forked from original GitHub repo 'go-gorp/gorp' owned by GitHub user "go-gorp"). - -Go Relational Persistence - an ORM-ish library for Go - -* HOMEPAGE: - * https://github.com/mattermost/gorp - -* LICENSE: MIT - -(The MIT License) - -Copyright (c) 2012 James Cooper <james@bitmechanic.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. - ---- - -## mattermost/rsc - -This product contains 'rsc' by Mattermost (forked from original GitHub repo 'petar/rsc' owned by Petar Maymounkov). - -fork of Russ Cox's code.google.com/p/rsc - -* HOMEPAGE: - * https://github.com/mattermost/rsc - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## mattermost/viper - -This product contains 'viper' by Mattermost (forked from original GitHub repo 'spf13/viper' owned by Steve Francia). - -Go configuration with fangs - -* HOMEPAGE: - * https://github.com/mattermost/viper - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Steve Francia - -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. - ---- - -## minio/minio-go - -This product contains 'minio-go' by Object Storage for AI. - -MinIO Client SDK for Go - -* HOMEPAGE: - * https://github.com/minio/minio-go - -* LICENSE: Apache-2.0 - - - 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. - -* This package includes the following NOTICE: - -minio-go -Copyright 2015-2017 MinIO, Inc. - ---- - -## nicksnyder/go-i18n - -This product contains 'go-i18n' by Mattermost, modified (forked) from original GitHub repo 'nicksnyder/go-i18n' owned by Nick Snyder. - -Translate your Go program into multiple languages. - -* HOMEPAGE: - * https://github.com/mattermost/go-i18n - -* LICENSE: MIT - -Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder - -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. - ---- - -## pborman/uuid - -This product contains 'uuid' by GitHub user "pborman". - -Automatically exported from code.google.com/p/go-uuid - -* HOMEPAGE: - * https://github.com/pborman/uuid - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## pkg/errors - -This product contains 'errors' by GitHub user "pkg". - -Simple error handling primitives - -* HOMEPAGE: - * https://github.com/pkg/errors - -* LICENSE: BSD-2-Clause - -Copyright (c) 2015, Dave Cheney <dave@cheney.net> -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## prometheus/client_golang - -This product contains 'client_golang' by Prometheus. - -Prometheus instrumentation library for Go applications - -* HOMEPAGE: - * https://github.com/prometheus/client_golang - -* LICENSE: Apache-2.0 - - 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. - -* This package includes the following NOTICE: - -Prometheus instrumentation library for Go applications -Copyright 2012-2015 The Prometheus Authors - -This product includes software developed at -SoundCloud Ltd. (http://soundcloud.com/). - - -The following components are included in this product: - -perks - a fork of https://github.com/bmizerany/perks -https://github.com/beorn7/perks -Copyright 2013-2015 Blake Mizerany, Björn Rabenstein -See https://github.com/beorn7/perks/blob/master/README.md for license details. - -Go support for Protocol Buffers - Google's data interchange format -http://github.com/golang/protobuf/ -Copyright 2010 The Go Authors -See source code for license details. - -Support for streaming Protocol Buffer messages for the Go language (golang). -https://github.com/matttproud/golang_protobuf_extensions -Copyright 2013 Matt T. Proud -Licensed under the Apache License, Version 2.0 - ---- - -## rs/cors - -This product contains 'cors' by Olivier Poitrey. - -Go net/http configurable handler to handle CORS requests - -* HOMEPAGE: - * https://github.com/rs/cors - -* LICENSE: MIT - -Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.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. - ---- - -## rwcarlsen/goexif - -This product contains 'goexif' by Robert Carlsen. - -Decode embedded EXIF meta data from image files. - -* HOMEPAGE: - * https://github.com/rwcarlsen/goexif - -* LICENSE: BSD-2-Clause - - -Copyright (c) 2012, Robert Carlsen & Contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## segmentio/analytics-go - -This product contains 'analytics-go' by Segment. - -Segment analytics client for Go - -* HOMEPAGE: - * https://github.com/segmentio/analytics-go - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2016 Segment, Inc. - -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. - ---- - -## sirupsen/logrus - -This product contains 'logrus' by Simon Eskildsen. - -Structured, pluggable logging for Go. - -* HOMEPAGE: - * https://github.com/sirupsen/logrus - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ---- - -## spf13/cobra - -This product contains 'cobra' by Steve Francia. - -A Commander for modern Go CLI interactions - -* HOMEPAGE: - * https://github.com/spf13/cobra - -* LICENSE: Apache-2.0 - - 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. - ---- - -## jmoiron/sqlx - -This product contains 'sqlx' by Jason Moiron. - -general purpose extensions to golang's database/sql - -* HOMEPAGE: - * https://github.com/jmoiron/sqlx - -* LICENSE: MIT - - Copyright (c) 2013, Jason Moiron - - 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. - ---- - -## stretchr/testify - -This product contains 'testify' by Stretchr, Inc.. - -A toolkit with common assertions and mocks that plays nicely with the standard library - -* HOMEPAGE: - * https://github.com/stretchr/testify - -* LICENSE: MIT - -MIT License - -Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell - -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. - ---- - -## throttled/throttled - -This product contains 'throttled' by GitHub user "throttled". - -Package throttled implements rate limiting access to resources such as HTTP endpoints. - -* HOMEPAGE: - * https://github.com/throttled/throttled - -* LICENSE: BSD-3-Clause - -Copyright (c) 2018, Martin Angers and Contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## tylerb/graceful - -This product contains 'graceful' by Tyler Stillwater. - -Graceful is a Go package enabling graceful shutdown of an http.Handler server. - -* HOMEPAGE: - * https://github.com/tylerb/graceful - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Tyler Bunnell - -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. - ---- - -## zap - -This product contains 'zap' by Uber Technologies, Inc.. - -Blazing fast, structured, leveled logging in Go. - -* HOMEPAGE: - * https://github.com/uber-go/zap - -* LICENSE: MIT - -Copyright (c) 2016-2017 Uber Technologies, Inc. - -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. - ---- - -## x/crypto - -This product contains 'crypto' by The Go Authors. - -[mirror] Go supplementary cryptography libraries - -* HOMEPAGE: - * https://github.com/golang/crypto - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## x/image - -This product contains 'image' by The Go Authors. - -[mirror] Go supplementary image libraries - -* HOMEPAGE: - * https://github.com/golang/image - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## x/net - -This product contains 'net' by The Go Authors. - -[mirror] Go supplementary network libraries - -* HOMEPAGE: - * https://github.com/golang/net - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## x/text - -This product contains 'text' by The Go Authors. - -[mirror] Go text processing support - -* HOMEPAGE: - * https://github.com/golang/text - -* LICENSE: BSD-3-Clause - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## mail - -This product contains 'mail' by GitHub user "go-mail", modified (forked) from original GitHub repo 'go-gomail/gomail' owned by Gomail. - -Actively maintained fork of gomail. The best way to send emails in Go. - -* HOMEPAGE: - * https://github.com/go-mail/mail - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Alexandre Cesaro - -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. - ---- - -## natefinch/lumberjack - -This product contains 'lumberjack' by Nate Finch. - -lumberjack is a log rolling package for Go - -* HOMEPAGE: - * https://github.com/natefinch/lumberjack - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2014 Nate Finch - -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. - ---- - -## olivere/elastic - -This product contains 'elastic' by Oliver Eilhard. - -Elasticsearch client for Go. - -* HOMEPAGE: - * https://github.com/olivere/elastic - -* LICENSE: MIT - -The MIT License (MIT) -Copyright © 2012-2015 Oliver Eilhard - -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. - ---- - -## go-yaml/yaml - -This product contains 'yaml' by GitHub user "go-yaml". - -YAML support for the Go language. - -* HOMEPAGE: - * https://github.com/go-yaml/yaml - -* LICENSE: Apache-2.0 - - 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. - -* This package includes the following NOTICE: - -Copyright 2011-2016 Canonical Ltd. - -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. - ---- - -## go/imageproxy - -This product contains 'imageproxy' by Will Norris. - -A caching, resizing image proxy written in Go - -* HOMEPAGE: - * https://github.com/willnorris/imageproxy - -* LICENSE: Apache-2.0 - - - 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 - ---- - -## oov/psd - -This product contains 'psd' by oov. - -A PSD/PSB file reader for go - -* HOMEPAGE: - * https://github.com/oov/psd - -* LICENSE: MIT - -MIT License - -Copyright (c) 2016 oov - -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. - ---- - -## gopherjs - -This product contains 'gopherjs' by Richard Musiol. - -A Go code to javascript code compiler. - -* HOMEPAGE: - * https://github.com/gopherjs/gopherjs - -* LICENSE: - -Copyright (c) 2013 Richard Musiol. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -## AWS SDK for Go - -This product contains 'aws-sdk' by Amazon. - -AWS-SDK support for the Go language. - -* HOMEPAGE: - * https://github.com/aws/aws-sdk-go - -* LICENSE: - - 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. - - ---- - -## semver - -This product contains 'semver' by Masterminds. - -The semver package provides the ability to work with Semantic Versions in Go. - -* HOMEPAGE: - * https://github.com/Masterminds/semver - -* LICENSE: - -Copyright (C) 2014-2019, Matt Butcher and Matt Farina - -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. - ---- - -## Date Constraints - -This product contains 'dateconstraints' by Eli Yukelzon. - -Go library to validate a date against constraints - -* HOMEPAGE: - * https://github.com/reflog/dateconstraints - -* LICENSE: - -MIT License - -Copyright (c) 2020 Eli Yukelzon - -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. - ---- - -## Archiver - -This product contains 'archiver' by Matthew Holt - -A library to handle direferen archive files (zip, rar, tar.gz...) - -* HOMEPAGE: - * https://github.com/mholt/archiver - -* LICENSE: - -MIT License - -Copyright (c) 2016 Matthew Holt - -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. - ---- - -## PDF Reader library - -This product contains 'pdf' by the Go team and modified by Thuc Le - -A library to provide pdf reading support - -* HOMEPAGE: - * https://github.com/ledongthuc/pdf - -* LICENSE: - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- - -## GoOse - -This product contains 'GoOse' by Antonio Linari - -A library to provide html text extraction support - -* HOMEPAGE: - * https://github.com/advancedlogic/GoOse - -* LICENSE: - -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. - ---- - -## Docconv - -This product contains 'docconv' by Sajari Pty Ltd - -A library to provide text extraction support for different documents - -* HOMEPAGE: - * https://github.com/sajari/docconv - -* LICENSE: - -The MIT License (MIT) - -Copyright (c) 2014 Sajari Pty Ltd - -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. - ---- - -## JWT-Go - -This product contains `jwt-go` by Dave Grijalva - -* HOMEPAGE: - * https://github.com/dgrijalva/jwt-go - -* LICENSE: - -The MIT License (MIT) - -Copyright (c) 2012 Dave Grijalva - -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. - diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/access.go b/vendor/github.com/mattermost/mattermost-server/v5/model/access.go deleted file mode 100644 index 6b60ea9e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/access.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" -) - -const ( - ACCESS_TOKEN_GRANT_TYPE = "authorization_code" - ACCESS_TOKEN_TYPE = "bearer" - REFRESH_TOKEN_GRANT_TYPE = "refresh_token" -) - -type AccessData struct { - ClientId string `json:"client_id"` - UserId string `json:"user_id"` - Token string `json:"token"` - RefreshToken string `json:"refresh_token"` - RedirectUri string `json:"redirect_uri"` - ExpiresAt int64 `json:"expires_at"` - Scope string `json:"scope"` -} - -type AccessResponse struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int32 `json:"expires_in"` - Scope string `json:"scope"` - RefreshToken string `json:"refresh_token"` - IdToken string `json:"id_token"` -} - -// IsValid validates the AccessData and returns an error if it isn't configured -// correctly. -func (ad *AccessData) IsValid() *AppError { - - if ad.ClientId == "" || len(ad.ClientId) > 26 { - return NewAppError("AccessData.IsValid", "model.access.is_valid.client_id.app_error", nil, "", http.StatusBadRequest) - } - - if ad.UserId == "" || len(ad.UserId) > 26 { - return NewAppError("AccessData.IsValid", "model.access.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(ad.Token) != 26 { - return NewAppError("AccessData.IsValid", "model.access.is_valid.access_token.app_error", nil, "", http.StatusBadRequest) - } - - if len(ad.RefreshToken) > 26 { - return NewAppError("AccessData.IsValid", "model.access.is_valid.refresh_token.app_error", nil, "", http.StatusBadRequest) - } - - if ad.RedirectUri == "" || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) { - return NewAppError("AccessData.IsValid", "model.access.is_valid.redirect_uri.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (ad *AccessData) IsExpired() bool { - - if ad.ExpiresAt <= 0 { - return false - } - - if GetMillis() > ad.ExpiresAt { - return true - } - - return false -} - -func (ad *AccessData) ToJson() string { - b, _ := json.Marshal(ad) - return string(b) -} - -func AccessDataFromJson(data io.Reader) *AccessData { - var ad *AccessData - json.NewDecoder(data).Decode(&ad) - return ad -} - -func (ar *AccessResponse) ToJson() string { - b, _ := json.Marshal(ar) - return string(b) -} - -func AccessResponseFromJson(data io.Reader) *AccessResponse { - var ar *AccessResponse - json.NewDecoder(data).Decode(&ar) - return ar -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/analytics_row.go b/vendor/github.com/mattermost/mattermost-server/v5/model/analytics_row.go deleted file mode 100644 index d1216664..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/analytics_row.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type AnalyticsRow struct { - Name string `json:"name"` - Value float64 `json:"value"` -} - -type AnalyticsRows []*AnalyticsRow - -func (ar *AnalyticsRow) ToJson() string { - b, _ := json.Marshal(ar) - return string(b) -} - -func AnalyticsRowFromJson(data io.Reader) *AnalyticsRow { - var ar *AnalyticsRow - json.NewDecoder(data).Decode(&ar) - return ar -} - -func (ar AnalyticsRows) ToJson() string { - b, err := json.Marshal(ar) - if err != nil { - return "[]" - } - return string(b) -} - -func AnalyticsRowsFromJson(data io.Reader) AnalyticsRows { - var ar AnalyticsRows - json.NewDecoder(data).Decode(&ar) - return ar -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/at_mentions.go b/vendor/github.com/mattermost/mattermost-server/v5/model/at_mentions.go deleted file mode 100644 index b5bfc0c7..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/at_mentions.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "regexp" - "strings" -) - -var atMentionRegexp = regexp.MustCompile(`\B@[[:alnum:]][[:alnum:]\.\-_:]*`) - -const usernameSpecialChars = ".-_" - -// PossibleAtMentions returns all substrings in message that look like valid @ -// mentions. -func PossibleAtMentions(message string) []string { - var names []string - - if !strings.Contains(message, "@") { - return names - } - - alreadyMentioned := make(map[string]bool) - for _, match := range atMentionRegexp.FindAllString(message, -1) { - name := NormalizeUsername(match[1:]) - if !alreadyMentioned[name] && IsValidUsernameAllowRemote(name) { - names = append(names, name) - alreadyMentioned[name] = true - } - } - - return names -} - -// TrimUsernameSpecialChar tries to remove the last character from word if it -// is a special character for usernames (dot, dash or underscore). If not, it -// returns the same string. -func TrimUsernameSpecialChar(word string) (string, bool) { - len := len(word) - - if len > 0 && strings.LastIndexAny(word, usernameSpecialChars) == (len-1) { - return word[:len-1], true - } - - return word, false -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/audit.go b/vendor/github.com/mattermost/mattermost-server/v5/model/audit.go deleted file mode 100644 index dd1e0602..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/audit.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type Audit struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UserId string `json:"user_id"` - Action string `json:"action"` - ExtraInfo string `json:"extra_info"` - IpAddress string `json:"ip_address"` - SessionId string `json:"session_id"` -} - -func (o *Audit) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func AuditFromJson(data io.Reader) *Audit { - var o *Audit - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/auditconv.go b/vendor/github.com/mattermost/mattermost-server/v5/model/auditconv.go deleted file mode 100644 index b3cf6062..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/auditconv.go +++ /dev/null @@ -1,713 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "github.com/francoispqt/gojay" -) - -// AuditModelTypeConv converts key model types to something better suited for audit output. -func AuditModelTypeConv(val interface{}) (newVal interface{}, converted bool) { - if val == nil { - return nil, false - } - switch v := val.(type) { - case *Channel: - return newAuditChannel(v), true - case *Team: - return newAuditTeam(v), true - case *User: - return newAuditUser(v), true - case *Command: - return newAuditCommand(v), true - case *CommandArgs: - return newAuditCommandArgs(v), true - case *Bot: - return newAuditBot(v), true - case *ChannelModerationPatch: - return newAuditChannelModerationPatch(v), true - case *Emoji: - return newAuditEmoji(v), true - case *FileInfo: - return newAuditFileInfo(v), true - case *Group: - return newAuditGroup(v), true - case *Job: - return newAuditJob(v), true - case *OAuthApp: - return newAuditOAuthApp(v), true - case *Post: - return newAuditPost(v), true - case *Role: - return newAuditRole(v), true - case *Scheme: - return newAuditScheme(v), true - case *SchemeRoles: - return newAuditSchemeRoles(v), true - case *Session: - return newAuditSession(v), true - case *IncomingWebhook: - return newAuditIncomingWebhook(v), true - case *OutgoingWebhook: - return newAuditOutgoingWebhook(v), true - case *RemoteCluster: - return newRemoteCluster(v), true - } - return val, false -} - -type auditChannel struct { - ID string - Name string - Type string -} - -// newAuditChannel creates a simplified representation of Channel for output to audit log. -func newAuditChannel(c *Channel) auditChannel { - var channel auditChannel - if c != nil { - channel.ID = c.Id - channel.Name = c.Name - channel.Type = c.Type - } - return channel -} - -func (c auditChannel) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", c.ID) - enc.StringKey("name", c.Name) - enc.StringKey("type", c.Type) -} - -func (c auditChannel) IsNil() bool { - return false -} - -type auditTeam struct { - ID string - Name string - Type string -} - -// newAuditTeam creates a simplified representation of Team for output to audit log. -func newAuditTeam(t *Team) auditTeam { - var team auditTeam - if t != nil { - team.ID = t.Id - team.Name = t.Name - team.Type = t.Type - } - return team -} - -func (t auditTeam) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", t.ID) - enc.StringKey("name", t.Name) - enc.StringKey("type", t.Type) -} - -func (t auditTeam) IsNil() bool { - return false -} - -type auditUser struct { - ID string - Name string - Roles string -} - -// newAuditUser creates a simplified representation of User for output to audit log. -func newAuditUser(u *User) auditUser { - var user auditUser - if u != nil { - user.ID = u.Id - user.Name = u.Username - user.Roles = u.Roles - } - return user -} - -func (u auditUser) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", u.ID) - enc.StringKey("name", u.Name) - enc.StringKey("roles", u.Roles) -} - -func (u auditUser) IsNil() bool { - return false -} - -type auditCommand struct { - ID string - CreatorID string - TeamID string - Trigger string - Method string - Username string - IconURL string - AutoComplete bool - AutoCompleteDesc string - AutoCompleteHint string - DisplayName string - Description string - URL string -} - -// newAuditCommand creates a simplified representation of Command for output to audit log. -func newAuditCommand(c *Command) auditCommand { - var cmd auditCommand - if c != nil { - cmd.ID = c.Id - cmd.CreatorID = c.CreatorId - cmd.TeamID = c.TeamId - cmd.Trigger = c.Trigger - cmd.Method = c.Method - cmd.Username = c.Username - cmd.IconURL = c.IconURL - cmd.AutoComplete = c.AutoComplete - cmd.AutoCompleteDesc = c.AutoCompleteDesc - cmd.AutoCompleteHint = c.AutoCompleteHint - cmd.DisplayName = c.DisplayName - cmd.Description = c.Description - cmd.URL = c.URL - } - return cmd -} - -func (cmd auditCommand) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", cmd.ID) - enc.StringKey("creator_id", cmd.CreatorID) - enc.StringKey("team_id", cmd.TeamID) - enc.StringKey("trigger", cmd.Trigger) - enc.StringKey("method", cmd.Method) - enc.StringKey("username", cmd.Username) - enc.StringKey("icon_url", cmd.IconURL) - enc.BoolKey("auto_complete", cmd.AutoComplete) - enc.StringKey("auto_complete_desc", cmd.AutoCompleteDesc) - enc.StringKey("auto_complete_hint", cmd.AutoCompleteHint) - enc.StringKey("display", cmd.DisplayName) - enc.StringKey("desc", cmd.Description) - enc.StringKey("url", cmd.URL) -} - -func (cmd auditCommand) IsNil() bool { - return false -} - -type auditCommandArgs struct { - ChannelID string - TeamID string - TriggerID string - Command string -} - -// newAuditCommandArgs creates a simplified representation of CommandArgs for output to audit log. -func newAuditCommandArgs(ca *CommandArgs) auditCommandArgs { - var cmdargs auditCommandArgs - if ca != nil { - cmdargs.ChannelID = ca.ChannelId - cmdargs.TeamID = ca.TeamId - cmdargs.TriggerID = ca.TriggerId - cmdargs.Command = ca.Command - } - return cmdargs -} - -func (ca auditCommandArgs) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("channel_id", ca.ChannelID) - enc.StringKey("team_id", ca.TriggerID) - enc.StringKey("trigger_id", ca.TeamID) - enc.StringKey("command", ca.Command) -} - -func (ca auditCommandArgs) IsNil() bool { - return false -} - -type auditBot struct { - UserID string - Username string - Displayname string -} - -// newAuditBot creates a simplified representation of Bot for output to audit log. -func newAuditBot(b *Bot) auditBot { - var bot auditBot - if b != nil { - bot.UserID = b.UserId - bot.Username = b.Username - bot.Displayname = b.DisplayName - } - return bot -} - -func (b auditBot) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("user_id", b.UserID) - enc.StringKey("username", b.Username) - enc.StringKey("display", b.Displayname) -} - -func (b auditBot) IsNil() bool { - return false -} - -type auditChannelModerationPatch struct { - Name string - RoleGuests bool - RoleMembers bool -} - -// newAuditChannelModerationPatch creates a simplified representation of ChannelModerationPatch for output to audit log. -func newAuditChannelModerationPatch(p *ChannelModerationPatch) auditChannelModerationPatch { - var patch auditChannelModerationPatch - if p != nil { - if p.Name != nil { - patch.Name = *p.Name - } - if p.Roles.Guests != nil { - patch.RoleGuests = *p.Roles.Guests - } - if p.Roles.Members != nil { - patch.RoleMembers = *p.Roles.Members - } - } - return patch -} - -func (p auditChannelModerationPatch) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("name", p.Name) - enc.BoolKey("role_guests", p.RoleGuests) - enc.BoolKey("role_members", p.RoleMembers) -} - -func (p auditChannelModerationPatch) IsNil() bool { - return false -} - -type auditEmoji struct { - ID string - Name string -} - -// newAuditEmoji creates a simplified representation of Emoji for output to audit log. -func newAuditEmoji(e *Emoji) auditEmoji { - var emoji auditEmoji - if e != nil { - emoji.ID = e.Id - emoji.Name = e.Name - } - return emoji -} - -func (e auditEmoji) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", e.ID) - enc.StringKey("name", e.Name) -} - -func (e auditEmoji) IsNil() bool { - return false -} - -type auditFileInfo struct { - ID string - PostID string - Path string - Name string - Extension string - Size int64 -} - -// newAuditFileInfo creates a simplified representation of FileInfo for output to audit log. -func newAuditFileInfo(f *FileInfo) auditFileInfo { - var fi auditFileInfo - if f != nil { - fi.ID = f.Id - fi.PostID = f.PostId - fi.Path = f.Path - fi.Name = f.Name - fi.Extension = f.Extension - fi.Size = f.Size - } - return fi -} - -func (fi auditFileInfo) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", fi.ID) - enc.StringKey("post_id", fi.PostID) - enc.StringKey("path", fi.Path) - enc.StringKey("name", fi.Name) - enc.StringKey("ext", fi.Extension) - enc.Int64Key("size", fi.Size) -} - -func (fi auditFileInfo) IsNil() bool { - return false -} - -type auditGroup struct { - ID string - Name string - DisplayName string - Description string -} - -// newAuditGroup creates a simplified representation of Group for output to audit log. -func newAuditGroup(g *Group) auditGroup { - var group auditGroup - if g != nil { - group.ID = g.Id - if g.Name == nil { - group.Name = "" - } else { - group.Name = *g.Name - } - group.DisplayName = g.DisplayName - group.Description = g.Description - } - return group -} - -func (g auditGroup) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", g.ID) - enc.StringKey("name", g.Name) - enc.StringKey("display", g.DisplayName) - enc.StringKey("desc", g.Description) -} - -func (g auditGroup) IsNil() bool { - return false -} - -type auditJob struct { - ID string - Type string - Priority int64 - StartAt int64 -} - -// newAuditJob creates a simplified representation of Job for output to audit log. -func newAuditJob(j *Job) auditJob { - var job auditJob - if j != nil { - job.ID = j.Id - job.Type = j.Type - job.Priority = j.Priority - job.StartAt = j.StartAt - } - return job -} - -func (j auditJob) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", j.ID) - enc.StringKey("type", j.Type) - enc.Int64Key("priority", j.Priority) - enc.Int64Key("start_at", j.StartAt) -} - -func (j auditJob) IsNil() bool { - return false -} - -type auditOAuthApp struct { - ID string - CreatorID string - Name string - Description string - IsTrusted bool -} - -// newAuditOAuthApp creates a simplified representation of OAuthApp for output to audit log. -func newAuditOAuthApp(o *OAuthApp) auditOAuthApp { - var oauth auditOAuthApp - if o != nil { - oauth.ID = o.Id - oauth.CreatorID = o.CreatorId - oauth.Name = o.Name - oauth.Description = o.Description - oauth.IsTrusted = o.IsTrusted - } - return oauth -} - -func (o auditOAuthApp) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", o.ID) - enc.StringKey("creator_id", o.CreatorID) - enc.StringKey("name", o.Name) - enc.StringKey("desc", o.Description) - enc.BoolKey("trusted", o.IsTrusted) -} - -func (o auditOAuthApp) IsNil() bool { - return false -} - -type auditPost struct { - ID string - ChannelID string - Type string - IsPinned bool -} - -// newAuditPost creates a simplified representation of Post for output to audit log. -func newAuditPost(p *Post) auditPost { - var post auditPost - if p != nil { - post.ID = p.Id - post.ChannelID = p.ChannelId - post.Type = p.Type - post.IsPinned = p.IsPinned - } - return post -} - -func (p auditPost) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", p.ID) - enc.StringKey("channel_id", p.ChannelID) - enc.StringKey("type", p.Type) - enc.BoolKey("pinned", p.IsPinned) -} - -func (p auditPost) IsNil() bool { - return false -} - -type auditRole struct { - ID string - Name string - DisplayName string - Permissions []string - SchemeManaged bool - BuiltIn bool -} - -// newAuditRole creates a simplified representation of Role for output to audit log. -func newAuditRole(r *Role) auditRole { - var role auditRole - if r != nil { - role.ID = r.Id - role.Name = r.Name - role.DisplayName = r.DisplayName - role.Permissions = append(role.Permissions, r.Permissions...) - role.SchemeManaged = r.SchemeManaged - role.BuiltIn = r.BuiltIn - } - return role -} - -func (r auditRole) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", r.ID) - enc.StringKey("name", r.Name) - enc.StringKey("display", r.DisplayName) - enc.SliceStringKey("perms", r.Permissions) - enc.BoolKey("schemeManaged", r.SchemeManaged) - enc.BoolKey("builtin", r.BuiltIn) -} - -func (r auditRole) IsNil() bool { - return false -} - -type auditScheme struct { - ID string - Name string - DisplayName string - Scope string -} - -// newAuditScheme creates a simplified representation of Scheme for output to audit log. -func newAuditScheme(s *Scheme) auditScheme { - var scheme auditScheme - if s != nil { - scheme.ID = s.Id - scheme.Name = s.Name - scheme.DisplayName = s.DisplayName - scheme.Scope = s.Scope - } - return scheme -} - -func (s auditScheme) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", s.ID) - enc.StringKey("name", s.Name) - enc.StringKey("display", s.DisplayName) - enc.StringKey("scope", s.Scope) -} - -func (s auditScheme) IsNil() bool { - return false -} - -type auditSchemeRoles struct { - SchemeAdmin bool - SchemeUser bool - SchemeGuest bool -} - -// newAuditSchemeRoles creates a simplified representation of SchemeRoles for output to audit log. -func newAuditSchemeRoles(s *SchemeRoles) auditSchemeRoles { - var roles auditSchemeRoles - if s != nil { - roles.SchemeAdmin = s.SchemeAdmin - roles.SchemeUser = s.SchemeUser - roles.SchemeGuest = s.SchemeGuest - } - return roles -} - -func (s auditSchemeRoles) MarshalJSONObject(enc *gojay.Encoder) { - enc.BoolKey("admin", s.SchemeAdmin) - enc.BoolKey("user", s.SchemeUser) - enc.BoolKey("guest", s.SchemeGuest) -} - -func (s auditSchemeRoles) IsNil() bool { - return false -} - -type auditSession struct { - ID string - UserId string - DeviceId string -} - -// newAuditSession creates a simplified representation of Session for output to audit log. -func newAuditSession(s *Session) auditSession { - var session auditSession - if s != nil { - session.ID = s.Id - session.UserId = s.UserId - session.DeviceId = s.DeviceId - } - return session -} - -func (s auditSession) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", s.ID) - enc.StringKey("user_id", s.UserId) - enc.StringKey("device_id", s.DeviceId) -} - -func (s auditSession) IsNil() bool { - return false -} - -type auditIncomingWebhook struct { - ID string - ChannelID string - TeamId string - DisplayName string - Description string -} - -// newAuditIncomingWebhook creates a simplified representation of IncomingWebhook for output to audit log. -func newAuditIncomingWebhook(h *IncomingWebhook) auditIncomingWebhook { - var hook auditIncomingWebhook - if h != nil { - hook.ID = h.Id - hook.ChannelID = h.ChannelId - hook.TeamId = h.TeamId - hook.DisplayName = h.DisplayName - hook.Description = h.Description - } - return hook -} - -func (h auditIncomingWebhook) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", h.ID) - enc.StringKey("channel_id", h.ChannelID) - enc.StringKey("team_id", h.TeamId) - enc.StringKey("display", h.DisplayName) - enc.StringKey("desc", h.Description) -} - -func (h auditIncomingWebhook) IsNil() bool { - return false -} - -type auditOutgoingWebhook struct { - ID string - ChannelID string - TeamID string - TriggerWords StringArray - TriggerWhen int - DisplayName string - Description string - ContentType string - Username string -} - -// newAuditOutgoingWebhook creates a simplified representation of OutgoingWebhook for output to audit log. -func newAuditOutgoingWebhook(h *OutgoingWebhook) auditOutgoingWebhook { - var hook auditOutgoingWebhook - if h != nil { - hook.ID = h.Id - hook.ChannelID = h.ChannelId - hook.TeamID = h.TeamId - hook.TriggerWords = h.TriggerWords - hook.TriggerWhen = h.TriggerWhen - hook.DisplayName = h.DisplayName - hook.Description = h.Description - hook.ContentType = h.ContentType - hook.Username = h.Username - } - return hook -} - -func (h auditOutgoingWebhook) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("id", h.ID) - enc.StringKey("channel_id", h.ChannelID) - enc.StringKey("team_id", h.TeamID) - enc.SliceStringKey("trigger_words", h.TriggerWords) - enc.IntKey("trigger_when", h.TriggerWhen) - enc.StringKey("display", h.DisplayName) - enc.StringKey("desc", h.Description) - enc.StringKey("content_type", h.ContentType) - enc.StringKey("username", h.Username) -} - -func (h auditOutgoingWebhook) IsNil() bool { - return false -} - -type auditRemoteCluster struct { - RemoteId string - RemoteTeamId string - Name string - DisplayName string - SiteURL string - CreateAt int64 - LastPingAt int64 - CreatorId string -} - -// newRemoteCluster creates a simplified representation of RemoteCluster for output to audit log. -func newRemoteCluster(r *RemoteCluster) auditRemoteCluster { - var rc auditRemoteCluster - if r != nil { - rc.RemoteId = r.RemoteId - rc.RemoteTeamId = r.RemoteTeamId - rc.Name = r.Name - rc.DisplayName = r.DisplayName - rc.SiteURL = r.SiteURL - rc.CreateAt = r.CreateAt - rc.LastPingAt = r.LastPingAt - rc.CreatorId = r.CreatorId - } - return rc -} - -func (r auditRemoteCluster) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("remote_id", r.RemoteId) - enc.StringKey("remote_team_id", r.RemoteTeamId) - enc.StringKey("name", r.Name) - enc.StringKey("display_name", r.DisplayName) - enc.StringKey("site_url", r.SiteURL) - enc.Int64Key("create_at", r.CreateAt) - enc.Int64Key("last_ping_at", r.LastPingAt) - enc.StringKey("creator_id", r.CreatorId) -} - -func (r auditRemoteCluster) IsNil() bool { - return false -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/audits.go b/vendor/github.com/mattermost/mattermost-server/v5/model/audits.go deleted file mode 100644 index 54e361f9..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/audits.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type Audits []Audit - -func (o Audits) Etag() string { - if len(o) > 0 { - // the first in the list is always the most current - return Etag(o[0].CreateAt) - } - return "" -} - -func (o Audits) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func AuditsFromJson(data io.Reader) Audits { - var o Audits - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/authorize.go b/vendor/github.com/mattermost/mattermost-server/v5/model/authorize.go deleted file mode 100644 index f2a8e8dc..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/authorize.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" -) - -const ( - AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes - AUTHCODE_RESPONSE_TYPE = "code" - IMPLICIT_RESPONSE_TYPE = "token" - DEFAULT_SCOPE = "user" -) - -type AuthData struct { - ClientId string `json:"client_id"` - UserId string `json:"user_id"` - Code string `json:"code"` - ExpiresIn int32 `json:"expires_in"` - CreateAt int64 `json:"create_at"` - RedirectUri string `json:"redirect_uri"` - State string `json:"state"` - Scope string `json:"scope"` -} - -type AuthorizeRequest struct { - ResponseType string `json:"response_type"` - ClientId string `json:"client_id"` - RedirectUri string `json:"redirect_uri"` - Scope string `json:"scope"` - State string `json:"state"` -} - -// IsValid validates the AuthData and returns an error if it isn't configured -// correctly. -func (ad *AuthData) IsValid() *AppError { - - if !IsValidId(ad.ClientId) { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(ad.UserId) { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if ad.Code == "" || len(ad.Code) > 128 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.auth_code.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest) - } - - if ad.ExpiresIn == 0 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.expires.app_error", nil, "", http.StatusBadRequest) - } - - if ad.CreateAt <= 0 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.create_at.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest) - } - - if len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest) - } - - if len(ad.State) > 1024 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest) - } - - if len(ad.Scope) > 128 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest) - } - - return nil -} - -// IsValid validates the AuthorizeRequest and returns an error if it isn't configured -// correctly. -func (ar *AuthorizeRequest) IsValid() *AppError { - - if !IsValidId(ar.ClientId) { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "", http.StatusBadRequest) - } - - if ar.ResponseType == "" { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.response_type.app_error", nil, "", http.StatusBadRequest) - } - - if ar.RedirectUri == "" || len(ar.RedirectUri) > 256 || !IsValidHttpUrl(ar.RedirectUri) { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) - } - - if len(ar.State) > 1024 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) - } - - if len(ar.Scope) > 128 { - return NewAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) - } - - return nil -} - -func (ad *AuthData) PreSave() { - if ad.ExpiresIn == 0 { - ad.ExpiresIn = AUTHCODE_EXPIRE_TIME - } - - if ad.CreateAt == 0 { - ad.CreateAt = GetMillis() - } - - if ad.Scope == "" { - ad.Scope = DEFAULT_SCOPE - } -} - -func (ad *AuthData) ToJson() string { - b, _ := json.Marshal(ad) - return string(b) -} - -func AuthDataFromJson(data io.Reader) *AuthData { - var ad *AuthData - json.NewDecoder(data).Decode(&ad) - return ad -} - -func (ar *AuthorizeRequest) ToJson() string { - b, _ := json.Marshal(ar) - return string(b) -} - -func AuthorizeRequestFromJson(data io.Reader) *AuthorizeRequest { - var ar *AuthorizeRequest - json.NewDecoder(data).Decode(&ar) - return ar -} - -func (ad *AuthData) IsExpired() bool { - return GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/bot.go b/vendor/github.com/mattermost/mattermost-server/v5/model/bot.go deleted file mode 100644 index e58fc0be..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/bot.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - "unicode/utf8" -) - -const ( - BOT_DISPLAY_NAME_MAX_RUNES = USER_FIRST_NAME_MAX_RUNES - BOT_DESCRIPTION_MAX_RUNES = 1024 - BOT_CREATOR_ID_MAX_RUNES = KEY_VALUE_PLUGIN_ID_MAX_RUNES // UserId or PluginId - BOT_WARN_METRIC_BOT_USERNAME = "mattermost-advisor" - BOT_SYSTEM_BOT_USERNAME = "system-bot" -) - -// Bot is a special type of User meant for programmatic interactions. -// Note that the primary key of a bot is the UserId, and matches the primary key of the -// corresponding user. -type Bot struct { - UserId string `json:"user_id"` - Username string `json:"username"` - DisplayName string `json:"display_name,omitempty"` - Description string `json:"description,omitempty"` - OwnerId string `json:"owner_id"` - LastIconUpdate int64 `json:"last_icon_update,omitempty"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` -} - -// BotPatch is a description of what fields to update on an existing bot. -type BotPatch struct { - Username *string `json:"username"` - DisplayName *string `json:"display_name"` - Description *string `json:"description"` -} - -// BotGetOptions acts as a filter on bulk bot fetching queries. -type BotGetOptions struct { - OwnerId string - IncludeDeleted bool - OnlyOrphaned bool - Page int - PerPage int -} - -// BotList is a list of bots. -type BotList []*Bot - -// Trace describes the minimum information required to identify a bot for the purpose of logging. -func (b *Bot) Trace() map[string]interface{} { - return map[string]interface{}{"user_id": b.UserId} -} - -// Clone returns a shallow copy of the bot. -func (b *Bot) Clone() *Bot { - copy := *b - return © -} - -// IsValid validates the bot and returns an error if it isn't configured correctly. -func (b *Bot) IsValid() *AppError { - if !IsValidId(b.UserId) { - return NewAppError("Bot.IsValid", "model.bot.is_valid.user_id.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if !IsValidUsername(b.Username) { - return NewAppError("Bot.IsValid", "model.bot.is_valid.username.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if utf8.RuneCountInString(b.DisplayName) > BOT_DISPLAY_NAME_MAX_RUNES { - return NewAppError("Bot.IsValid", "model.bot.is_valid.user_id.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if utf8.RuneCountInString(b.Description) > BOT_DESCRIPTION_MAX_RUNES { - return NewAppError("Bot.IsValid", "model.bot.is_valid.description.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if b.OwnerId == "" || utf8.RuneCountInString(b.OwnerId) > BOT_CREATOR_ID_MAX_RUNES { - return NewAppError("Bot.IsValid", "model.bot.is_valid.creator_id.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if b.CreateAt == 0 { - return NewAppError("Bot.IsValid", "model.bot.is_valid.create_at.app_error", b.Trace(), "", http.StatusBadRequest) - } - - if b.UpdateAt == 0 { - return NewAppError("Bot.IsValid", "model.bot.is_valid.update_at.app_error", b.Trace(), "", http.StatusBadRequest) - } - - return nil -} - -// PreSave should be run before saving a new bot to the database. -func (b *Bot) PreSave() { - b.CreateAt = GetMillis() - b.UpdateAt = b.CreateAt - b.DeleteAt = 0 -} - -// PreUpdate should be run before saving an updated bot to the database. -func (b *Bot) PreUpdate() { - b.UpdateAt = GetMillis() -} - -// Etag generates an etag for caching. -func (b *Bot) Etag() string { - return Etag(b.UserId, b.UpdateAt) -} - -// ToJson serializes the bot to json. -func (b *Bot) ToJson() []byte { - data, _ := json.Marshal(b) - return data -} - -// BotFromJson deserializes a bot from json. -func BotFromJson(data io.Reader) *Bot { - var bot *Bot - json.NewDecoder(data).Decode(&bot) - return bot -} - -// Patch modifies an existing bot with optional fields from the given patch. -// TODO 6.0: consider returning a boolean to indicate whether or not the patch -// applied any changes. -func (b *Bot) Patch(patch *BotPatch) { - if patch.Username != nil { - b.Username = *patch.Username - } - - if patch.DisplayName != nil { - b.DisplayName = *patch.DisplayName - } - - if patch.Description != nil { - b.Description = *patch.Description - } -} - -// WouldPatch returns whether or not the given patch would be applied or not. -func (b *Bot) WouldPatch(patch *BotPatch) bool { - if patch == nil { - return false - } - if patch.Username != nil && *patch.Username != b.Username { - return true - } - if patch.DisplayName != nil && *patch.DisplayName != b.DisplayName { - return true - } - if patch.Description != nil && *patch.Description != b.Description { - return true - } - return false -} - -// ToJson serializes the bot patch to json. -func (b *BotPatch) ToJson() []byte { - data, err := json.Marshal(b) - if err != nil { - return nil - } - - return data -} - -// BotPatchFromJson deserializes a bot patch from json. -func BotPatchFromJson(data io.Reader) *BotPatch { - decoder := json.NewDecoder(data) - var botPatch BotPatch - err := decoder.Decode(&botPatch) - if err != nil { - return nil - } - - return &botPatch -} - -// UserFromBot returns a user model describing the bot fields stored in the User store. -func UserFromBot(b *Bot) *User { - return &User{ - Id: b.UserId, - Username: b.Username, - Email: NormalizeEmail(fmt.Sprintf("%s@localhost", b.Username)), - FirstName: b.DisplayName, - Roles: SYSTEM_USER_ROLE_ID, - } -} - -// BotFromUser returns a bot model given a user model -func BotFromUser(u *User) *Bot { - return &Bot{ - OwnerId: u.Id, - UserId: u.Id, - Username: u.Username, - DisplayName: u.GetDisplayName(SHOW_USERNAME), - } -} - -// BotListFromJson deserializes a list of bots from json. -func BotListFromJson(data io.Reader) BotList { - var bots BotList - json.NewDecoder(data).Decode(&bots) - return bots -} - -// ToJson serializes a list of bots to json. -func (l *BotList) ToJson() []byte { - b, _ := json.Marshal(l) - return b -} - -// Etag computes the etag for a list of bots. -func (l *BotList) Etag() string { - id := "0" - var t int64 = 0 - var delta int64 = 0 - - for _, v := range *l { - if v.UpdateAt > t { - t = v.UpdateAt - id = v.UserId - } - - } - - return Etag(id, t, delta, len(*l)) -} - -// MakeBotNotFoundError creates the error returned when a bot does not exist, or when the user isn't allowed to query the bot. -// The errors must the same in both cases to avoid leaking that a user is a bot. -func MakeBotNotFoundError(userId string) *AppError { - return NewAppError("SqlBotStore.Get", "store.sql_bot.get.missing.app_error", map[string]interface{}{"user_id": userId}, "", http.StatusNotFound) -} - -func IsBotDMChannel(channel *Channel, botUserID string) bool { - if channel.Type != CHANNEL_DIRECT { - return false - } - - if !strings.HasPrefix(channel.Name, botUserID+"__") && !strings.HasSuffix(channel.Name, "__"+botUserID) { - return false - } - - return true -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/builtin.go b/vendor/github.com/mattermost/mattermost-server/v5/model/builtin.go deleted file mode 100644 index 38e01f8b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/builtin.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -func NewBool(b bool) *bool { return &b } -func NewInt(n int) *int { return &n } -func NewInt64(n int64) *int64 { return &n } -func NewString(s string) *string { return &s } diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/bundle_info.go b/vendor/github.com/mattermost/mattermost-server/v5/model/bundle_info.go deleted file mode 100644 index e4cfe125..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/bundle_info.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -type BundleInfo struct { - Path string - - Manifest *Manifest - ManifestPath string - ManifestError error -} - -func (b *BundleInfo) WrapLogger(logger *mlog.Logger) *mlog.Logger { - if b.Manifest != nil { - return logger.With(mlog.String("plugin_id", b.Manifest.Id)) - } - return logger.With(mlog.String("plugin_path", b.Path)) -} - -// Returns bundle info for the given path. The return value is never nil. -func BundleInfoForPath(path string) *BundleInfo { - m, mpath, err := FindManifest(path) - return &BundleInfo{ - Path: path, - Manifest: m, - ManifestPath: mpath, - ManifestError: err, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel.go deleted file mode 100644 index 8dc3fa8d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel.go +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto/sha1" - "encoding/hex" - "encoding/json" - "io" - "net/http" - "sort" - "strings" - "unicode/utf8" -) - -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 - - CHANNEL_SORT_BY_USERNAME = "username" - CHANNEL_SORT_BY_STATUS = "status" -) - -type Channel struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - TeamId string `json:"team_id"` - Type string `json:"type"` - DisplayName string `json:"display_name"` - Name string `json:"name"` - Header string `json:"header"` - Purpose string `json:"purpose"` - LastPostAt int64 `json:"last_post_at"` - TotalMsgCount int64 `json:"total_msg_count"` - ExtraUpdateAt int64 `json:"extra_update_at"` - CreatorId string `json:"creator_id"` - SchemeId *string `json:"scheme_id"` - Props map[string]interface{} `json:"props" db:"-"` - GroupConstrained *bool `json:"group_constrained"` - Shared *bool `json:"shared"` - TotalMsgCountRoot int64 `json:"total_msg_count_root"` - PolicyID *string `json:"policy_id" db:"-"` -} - -type ChannelWithTeamData struct { - Channel - TeamDisplayName string `json:"team_display_name"` - TeamName string `json:"team_name"` - TeamUpdateAt int64 `json:"team_update_at"` -} - -type ChannelsWithCount struct { - Channels *ChannelListWithTeamData `json:"channels"` - TotalCount int64 `json:"total_count"` -} - -type ChannelPatch struct { - DisplayName *string `json:"display_name"` - Name *string `json:"name"` - Header *string `json:"header"` - Purpose *string `json:"purpose"` - GroupConstrained *bool `json:"group_constrained"` -} - -type ChannelForExport struct { - Channel - TeamName string - SchemeName *string -} - -type DirectChannelForExport struct { - Channel - Members *[]string -} - -type ChannelModeration struct { - Name string `json:"name"` - Roles *ChannelModeratedRoles `json:"roles"` -} - -type ChannelModeratedRoles struct { - Guests *ChannelModeratedRole `json:"guests"` - Members *ChannelModeratedRole `json:"members"` -} - -type ChannelModeratedRole struct { - Value bool `json:"value"` - Enabled bool `json:"enabled"` -} - -type ChannelModerationPatch struct { - Name *string `json:"name"` - Roles *ChannelModeratedRolesPatch `json:"roles"` -} - -type ChannelModeratedRolesPatch struct { - Guests *bool `json:"guests"` - Members *bool `json:"members"` -} - -// ChannelSearchOpts contains options for searching channels. -// -// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records. -// ExcludeDefaultChannels will exclude the configured default channels (ex 'town-square' and 'off-topic'). -// IncludeDeleted will include channel records where DeleteAt != 0. -// ExcludeChannelNames will exclude channels from the results by name. -// Paginate whether to paginate the results. -// Page page requested, if results are paginated. -// PerPage number of results per page, if paginated. -// -type ChannelSearchOpts struct { - NotAssociatedToGroup string - ExcludeDefaultChannels bool - IncludeDeleted bool - Deleted bool - ExcludeChannelNames []string - TeamIds []string - GroupConstrained bool - ExcludeGroupConstrained bool - PolicyID string - ExcludePolicyConstrained bool - IncludePolicyID bool - Public bool - Private bool - Page *int - PerPage *int -} - -type ChannelMemberCountByGroup struct { - GroupId string `db:"-" json:"group_id"` - ChannelMemberCount int64 `db:"-" json:"channel_member_count"` - ChannelMemberTimezonesCount int64 `db:"-" json:"channel_member_timezones_count"` -} - -type ChannelOption func(channel *Channel) - -func WithID(ID string) ChannelOption { - return func(channel *Channel) { - channel.Id = ID - } -} - -func (o *Channel) DeepCopy() *Channel { - copy := *o - if copy.SchemeId != nil { - copy.SchemeId = NewString(*o.SchemeId) - } - return © -} - -func (o *Channel) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *ChannelPatch) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *ChannelsWithCount) ToJson() []byte { - b, _ := json.Marshal(o) - return b -} - -func ChannelsWithCountFromJson(data io.Reader) *ChannelsWithCount { - var o *ChannelsWithCount - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelFromJson(data io.Reader) *Channel { - var o *Channel - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelPatchFromJson(data io.Reader) *ChannelPatch { - var o *ChannelPatch - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelModerationsFromJson(data io.Reader) []*ChannelModeration { - var o []*ChannelModeration - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelModerationsPatchFromJson(data io.Reader) []*ChannelModerationPatch { - var o []*ChannelModerationPatch - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelMemberCountsByGroupFromJson(data io.Reader) []*ChannelMemberCountByGroup { - var o []*ChannelMemberCountByGroup - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *Channel) Etag() string { - return Etag(o.Id, o.UpdateAt) -} - -func (o *Channel) IsValid() *AppError { - if !IsValidId(o.Id) { - return NewAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("Channel.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.DisplayName) > CHANNEL_DISPLAY_NAME_MAX_RUNES { - return NewAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidChannelIdentifier(o.Name) { - return NewAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP) { - return NewAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.Header) > CHANNEL_HEADER_MAX_RUNES { - return NewAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.Purpose) > CHANNEL_PURPOSE_MAX_RUNES { - return NewAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if len(o.CreatorId) > 26 { - return NewAppError("Channel.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "", http.StatusBadRequest) - } - - userIds := strings.Split(o.Name, "__") - if o.Type != CHANNEL_DIRECT && len(userIds) == 2 && IsValidId(userIds[0]) && IsValidId(userIds[1]) { - return NewAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *Channel) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - o.Name = SanitizeUnicode(o.Name) - o.DisplayName = SanitizeUnicode(o.DisplayName) - - o.CreateAt = GetMillis() - o.UpdateAt = o.CreateAt - o.ExtraUpdateAt = 0 -} - -func (o *Channel) PreUpdate() { - o.UpdateAt = GetMillis() - o.Name = SanitizeUnicode(o.Name) - o.DisplayName = SanitizeUnicode(o.DisplayName) -} - -func (o *Channel) IsGroupOrDirect() bool { - return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP -} - -func (o *Channel) IsOpen() bool { - return o.Type == CHANNEL_OPEN -} - -func (o *Channel) Patch(patch *ChannelPatch) { - if patch.DisplayName != nil { - o.DisplayName = *patch.DisplayName - } - - if patch.Name != nil { - o.Name = *patch.Name - } - - if patch.Header != nil { - o.Header = *patch.Header - } - - if patch.Purpose != nil { - o.Purpose = *patch.Purpose - } - - if patch.GroupConstrained != nil { - o.GroupConstrained = patch.GroupConstrained - } -} - -func (o *Channel) MakeNonNil() { - if o.Props == nil { - o.Props = make(map[string]interface{}) - } -} - -func (o *Channel) AddProp(key string, value interface{}) { - o.MakeNonNil() - - o.Props[key] = value -} - -func (o *Channel) IsGroupConstrained() bool { - return o.GroupConstrained != nil && *o.GroupConstrained -} - -func (o *Channel) IsShared() bool { - return o.Shared != nil && *o.Shared -} - -func (o *Channel) GetOtherUserIdForDM(userId string) string { - if o.Type != CHANNEL_DIRECT { - return "" - } - - userIds := strings.Split(o.Name, "__") - - var otherUserId string - - if userIds[0] != userIds[1] { - if userIds[0] == userId { - otherUserId = userIds[1] - } else { - otherUserId = userIds[0] - } - } - - return otherUserId -} - -func GetDMNameFromIds(userId1, userId2 string) string { - if userId1 > userId2 { - return userId2 + "__" + userId1 - } - 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)) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_count.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_count.go deleted file mode 100644 index 6230ab3d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_count.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto/md5" - "encoding/json" - "fmt" - "io" - "sort" - "strconv" -) - -type ChannelCounts struct { - Counts map[string]int64 `json:"counts"` - CountsRoot map[string]int64 `json:"counts_root"` - UpdateTimes map[string]int64 `json:"update_times"` -} - -func (o *ChannelCounts) Etag() string { - // we don't include CountsRoot in ETag calculation, since it's a deriviative - ids := []string{} - for id := range o.Counts { - ids = append(ids, id) - } - sort.Strings(ids) - - str := "" - for _, id := range ids { - str += id + strconv.FormatInt(o.Counts[id], 10) - } - - md5Counts := fmt.Sprintf("%x", md5.Sum([]byte(str))) - - var update int64 = 0 - for _, u := range o.UpdateTimes { - if u > update { - update = u - } - } - - return Etag(md5Counts, update) -} - -func (o *ChannelCounts) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelCountsFromJson(data io.Reader) *ChannelCounts { - var o *ChannelCounts - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_data.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_data.go deleted file mode 100644 index 0a1e0d57..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_data.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ChannelData struct { - Channel *Channel `json:"channel"` - Member *ChannelMember `json:"member"` -} - -func (o *ChannelData) Etag() string { - var mt int64 = 0 - if o.Member != nil { - mt = o.Member.LastUpdateAt - } - - return Etag(o.Channel.Id, o.Channel.UpdateAt, o.Channel.LastPostAt, mt) -} - -func (o *ChannelData) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelDataFromJson(data io.Reader) *ChannelData { - var o *ChannelData - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_list.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_list.go deleted file mode 100644 index 4ba9e5c3..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_list.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ChannelList []*Channel - -func (o *ChannelList) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func (o *ChannelList) Etag() string { - - id := "0" - var t int64 = 0 - var delta int64 = 0 - - for _, v := range *o { - if v.LastPostAt > t { - t = v.LastPostAt - id = v.Id - } - - if v.UpdateAt > t { - t = v.UpdateAt - id = v.Id - } - - } - - return Etag(id, t, delta, len(*o)) -} - -func ChannelListFromJson(data io.Reader) *ChannelList { - var o *ChannelList - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelSliceFromJson(data io.Reader) []*Channel { - var o []*Channel - json.NewDecoder(data).Decode(&o) - return o -} - -type ChannelListWithTeamData []*ChannelWithTeamData - -func (o *ChannelListWithTeamData) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func (o *ChannelListWithTeamData) Etag() string { - - id := "0" - var t int64 = 0 - var delta int64 = 0 - - for _, v := range *o { - if v.LastPostAt > t { - t = v.LastPostAt - id = v.Id - } - - if v.UpdateAt > t { - t = v.UpdateAt - id = v.Id - } - - if v.TeamUpdateAt > t { - t = v.TeamUpdateAt - id = v.Id - } - } - - return Etag(id, t, delta, len(*o)) -} - -func ChannelListWithTeamDataFromJson(data io.Reader) *ChannelListWithTeamData { - var o *ChannelListWithTeamData - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member.go deleted file mode 100644 index bc02881e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "strings" -) - -const ( - CHANNEL_NOTIFY_DEFAULT = "default" - CHANNEL_NOTIFY_ALL = "all" - CHANNEL_NOTIFY_MENTION = "mention" - CHANNEL_NOTIFY_NONE = "none" - CHANNEL_MARK_UNREAD_ALL = "all" - CHANNEL_MARK_UNREAD_MENTION = "mention" - IGNORE_CHANNEL_MENTIONS_DEFAULT = "default" - IGNORE_CHANNEL_MENTIONS_OFF = "off" - IGNORE_CHANNEL_MENTIONS_ON = "on" - IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP = "ignore_channel_mentions" -) - -type ChannelUnread struct { - TeamId string `json:"team_id"` - ChannelId string `json:"channel_id"` - MsgCount int64 `json:"msg_count"` - MentionCount int64 `json:"mention_count"` - MentionCountRoot int64 `json:"mention_count_root"` - MsgCountRoot int64 `json:"msg_count_root"` - NotifyProps StringMap `json:"-"` -} - -type ChannelUnreadAt struct { - TeamId string `json:"team_id"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - MsgCount int64 `json:"msg_count"` - MentionCount int64 `json:"mention_count"` - MentionCountRoot int64 `json:"mention_count_root"` - MsgCountRoot int64 `json:"msg_count_root"` - LastViewedAt int64 `json:"last_viewed_at"` - NotifyProps StringMap `json:"-"` -} - -type ChannelMember struct { - ChannelId string `json:"channel_id"` - UserId string `json:"user_id"` - Roles string `json:"roles"` - LastViewedAt int64 `json:"last_viewed_at"` - MsgCount int64 `json:"msg_count"` - MentionCount int64 `json:"mention_count"` - MentionCountRoot int64 `json:"mention_count_root"` - MsgCountRoot int64 `json:"msg_count_root"` - NotifyProps StringMap `json:"notify_props"` - LastUpdateAt int64 `json:"last_update_at"` - SchemeGuest bool `json:"scheme_guest"` - SchemeUser bool `json:"scheme_user"` - SchemeAdmin bool `json:"scheme_admin"` - ExplicitRoles string `json:"explicit_roles"` -} - -type ChannelMembers []ChannelMember - -type ChannelMemberForExport struct { - ChannelMember - ChannelName string - Username string -} - -func (o *ChannelMembers) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func (o *ChannelUnread) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *ChannelUnreadAt) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelMembersFromJson(data io.Reader) *ChannelMembers { - var o *ChannelMembers - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelUnreadFromJson(data io.Reader) *ChannelUnread { - var o *ChannelUnread - json.NewDecoder(data).Decode(&o) - return o -} - -func ChannelUnreadAtFromJson(data io.Reader) *ChannelUnreadAt { - var o *ChannelUnreadAt - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *ChannelMember) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelMemberFromJson(data io.Reader) *ChannelMember { - var o *ChannelMember - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *ChannelMember) IsValid() *AppError { - - if !IsValidId(o.ChannelId) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.UserId) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - notifyLevel := o.NotifyProps[DESKTOP_NOTIFY_PROP] - if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", nil, "notify_level="+notifyLevel, http.StatusBadRequest) - } - - markUnreadLevel := o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] - if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", nil, "mark_unread_level="+markUnreadLevel, http.StatusBadRequest) - } - - if pushLevel, ok := o.NotifyProps[PUSH_NOTIFY_PROP]; ok { - if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error", nil, "push_notification_level="+pushLevel, http.StatusBadRequest) - } - } - - if sendEmail, ok := o.NotifyProps[EMAIL_NOTIFY_PROP]; ok { - if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error", nil, "push_notification_level="+sendEmail, http.StatusBadRequest) - } - } - - if ignoreChannelMentions, ok := o.NotifyProps[IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; ok { - if len(ignoreChannelMentions) > 40 || !IsIgnoreChannelMentionsValid(ignoreChannelMentions) { - return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.ignore_channel_mentions_value.app_error", nil, "ignore_channel_mentions="+ignoreChannelMentions, http.StatusBadRequest) - } - } - - return nil -} - -func (o *ChannelMember) PreSave() { - o.LastUpdateAt = GetMillis() -} - -func (o *ChannelMember) PreUpdate() { - o.LastUpdateAt = GetMillis() -} - -func (o *ChannelMember) GetRoles() []string { - return strings.Fields(o.Roles) -} - -func (o *ChannelMember) SetChannelMuted(muted bool) { - if o.IsChannelMuted() { - o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] = CHANNEL_MARK_UNREAD_ALL - } else { - o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] = CHANNEL_MARK_UNREAD_MENTION - } -} - -func (o *ChannelMember) IsChannelMuted() bool { - return o.NotifyProps[MARK_UNREAD_NOTIFY_PROP] == CHANNEL_MARK_UNREAD_MENTION -} - -func IsChannelNotifyLevelValid(notifyLevel string) bool { - return notifyLevel == CHANNEL_NOTIFY_DEFAULT || - notifyLevel == CHANNEL_NOTIFY_ALL || - notifyLevel == CHANNEL_NOTIFY_MENTION || - notifyLevel == CHANNEL_NOTIFY_NONE -} - -func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool { - return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION -} - -func IsSendEmailValid(sendEmail string) bool { - return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false" -} - -func IsIgnoreChannelMentionsValid(ignoreChannelMentions string) bool { - return ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_ON || ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_OFF || ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_DEFAULT -} - -func GetDefaultChannelNotifyProps() StringMap { - return StringMap{ - 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, - IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP: IGNORE_CHANNEL_MENTIONS_DEFAULT, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history.go deleted file mode 100644 index b77e0ff9..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -type ChannelMemberHistory struct { - ChannelId string - UserId string - JoinTime int64 - LeaveTime *int64 -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history_result.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history_result.go deleted file mode 100644 index 8f43ca4e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_member_history_result.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -type ChannelMemberHistoryResult struct { - ChannelId string - UserId string - JoinTime int64 - LeaveTime *int64 - - // these two fields are never set in the database - when we SELECT, we join on Users to get them - UserEmail string `db:"Email"` - Username string - IsBot bool - UserDeleteAt int64 -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_mentions.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_mentions.go deleted file mode 100644 index eb14e8ed..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_mentions.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "regexp" - "strings" -) - -var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`) - -func ChannelMentions(message string) []string { - var names []string - - if strings.Contains(message, "~") { - alreadyMentioned := make(map[string]bool) - for _, match := range channelMentionRegexp.FindAllString(message, -1) { - name := match[1:] - if !alreadyMentioned[name] { - names = append(names, name) - alreadyMentioned[name] = true - } - } - } - - return names -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_search.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_search.go deleted file mode 100644 index 51e11736..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_search.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const CHANNEL_SEARCH_DEFAULT_LIMIT = 50 - -type ChannelSearch struct { - Term string `json:"term"` - ExcludeDefaultChannels bool `json:"exclude_default_channels"` - NotAssociatedToGroup string `json:"not_associated_to_group"` - TeamIds []string `json:"team_ids"` - GroupConstrained bool `json:"group_constrained"` - ExcludeGroupConstrained bool `json:"exclude_group_constrained"` - ExcludePolicyConstrained bool `json:"exclude_policy_constrained"` - Public bool `json:"public"` - Private bool `json:"private"` - IncludeDeleted bool `json:"include_deleted"` - Deleted bool `json:"deleted"` - Page *int `json:"page,omitempty"` - PerPage *int `json:"per_page,omitempty"` -} - -// ToJson convert a Channel to a json string -func (c *ChannelSearch) ToJson() string { - b, _ := json.Marshal(c) - return string(b) -} - -// ChannelSearchFromJson will decode the input and return a Channel -func ChannelSearchFromJson(data io.Reader) *ChannelSearch { - var cs *ChannelSearch - json.NewDecoder(data).Decode(&cs) - return cs -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_sidebar.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_sidebar.go deleted file mode 100644 index 35301d1e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_sidebar.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "regexp" -) - -type SidebarCategoryType string -type SidebarCategorySorting string - -const ( - // Each sidebar category has a 'type'. System categories are Channels, Favorites and DMs - // All user-created categories will have type Custom - SidebarCategoryChannels SidebarCategoryType = "channels" - SidebarCategoryDirectMessages SidebarCategoryType = "direct_messages" - SidebarCategoryFavorites SidebarCategoryType = "favorites" - SidebarCategoryCustom SidebarCategoryType = "custom" - // Increment to use when adding/reordering things in the sidebar - MinimalSidebarSortDistance = 10 - // Default Sort Orders for categories - DefaultSidebarSortOrderFavorites = 0 - DefaultSidebarSortOrderChannels = DefaultSidebarSortOrderFavorites + MinimalSidebarSortDistance - DefaultSidebarSortOrderDMs = DefaultSidebarSortOrderChannels + MinimalSidebarSortDistance - // Sorting modes - // default for all categories except DMs (behaves like manual) - SidebarCategorySortDefault SidebarCategorySorting = "" - // sort manually - SidebarCategorySortManual SidebarCategorySorting = "manual" - // sort by recency (default for DMs) - SidebarCategorySortRecent SidebarCategorySorting = "recent" - // sort by display name alphabetically - SidebarCategorySortAlphabetical SidebarCategorySorting = "alpha" -) - -// SidebarCategory represents the corresponding DB table -// SortOrder is never returned to the user and only used for queries -type SidebarCategory struct { - Id string `json:"id"` - UserId string `json:"user_id"` - TeamId string `json:"team_id"` - SortOrder int64 `json:"-"` - Sorting SidebarCategorySorting `json:"sorting"` - Type SidebarCategoryType `json:"type"` - DisplayName string `json:"display_name"` - Muted bool `json:"muted"` - Collapsed bool `json:"collapsed"` -} - -// SidebarCategoryWithChannels combines data from SidebarCategory table with the Channel IDs that belong to that category -type SidebarCategoryWithChannels struct { - SidebarCategory - Channels []string `json:"channel_ids"` -} - -type SidebarCategoryOrder []string - -// OrderedSidebarCategories combines categories, their channel IDs and an array of Category IDs, sorted -type OrderedSidebarCategories struct { - Categories SidebarCategoriesWithChannels `json:"categories"` - Order SidebarCategoryOrder `json:"order"` -} - -type SidebarChannel struct { - ChannelId string `json:"channel_id"` - UserId string `json:"user_id"` - CategoryId string `json:"category_id"` - SortOrder int64 `json:"-"` -} - -type SidebarChannels []*SidebarChannel -type SidebarCategoriesWithChannels []*SidebarCategoryWithChannels - -func SidebarCategoryFromJson(data io.Reader) (*SidebarCategoryWithChannels, error) { - var o *SidebarCategoryWithChannels - err := json.NewDecoder(data).Decode(&o) - return o, err -} - -func SidebarCategoriesFromJson(data io.Reader) ([]*SidebarCategoryWithChannels, error) { - var o []*SidebarCategoryWithChannels - err := json.NewDecoder(data).Decode(&o) - return o, err -} - -func OrderedSidebarCategoriesFromJson(data io.Reader) (*OrderedSidebarCategories, error) { - var o *OrderedSidebarCategories - err := json.NewDecoder(data).Decode(&o) - return o, err -} - -func (o SidebarCategoryWithChannels) ToJson() []byte { - b, _ := json.Marshal(o) - return b -} - -func SidebarCategoriesWithChannelsToJson(o []*SidebarCategoryWithChannels) []byte { - b, err := json.Marshal(o) - if err != nil { - return []byte("[]") - } - return b -} - -func (o OrderedSidebarCategories) ToJson() []byte { - b, err := json.Marshal(o) - if err != nil { - return []byte("[]") - } - return b -} - -var categoryIdPattern = regexp.MustCompile("(favorites|channels|direct_messages)_[a-z0-9]{26}_[a-z0-9]{26}") - -func IsValidCategoryId(s string) bool { - // Category IDs can either be regular IDs - if IsValidId(s) { - return true - } - - // Or default categories can follow the pattern {type}_{userID}_{teamID} - return categoryIdPattern.MatchString(s) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_stats.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_stats.go deleted file mode 100644 index 76f682aa..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_stats.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ChannelStats struct { - ChannelId string `json:"channel_id"` - MemberCount int64 `json:"member_count"` - GuestCount int64 `json:"guest_count"` - PinnedPostCount int64 `json:"pinnedpost_count"` -} - -func (o *ChannelStats) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelStatsFromJson(data io.Reader) *ChannelStats { - var o *ChannelStats - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_view.go b/vendor/github.com/mattermost/mattermost-server/v5/model/channel_view.go deleted file mode 100644 index 2ba74434..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/channel_view.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ChannelView struct { - ChannelId string `json:"channel_id"` - PrevChannelId string `json:"prev_channel_id"` - CollapsedThreadsSupported bool `json:"collapsed_threads_supported"` -} - -func (o *ChannelView) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelViewFromJson(data io.Reader) *ChannelView { - var o *ChannelView - json.NewDecoder(data).Decode(&o) - return o -} - -type ChannelViewResponse struct { - Status string `json:"status"` - LastViewedAtTimes map[string]int64 `json:"last_viewed_at_times"` -} - -func (o *ChannelViewResponse) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ChannelViewResponseFromJson(data io.Reader) *ChannelViewResponse { - var o *ChannelViewResponse - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/client4.go b/vendor/github.com/mattermost/mattermost-server/v5/model/client4.go deleted file mode 100644 index 0b9e713a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/client4.go +++ /dev/null @@ -1,6343 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net" - "net/http" - "net/url" - "strconv" - "strings" - "time" -) - -const ( - HEADER_REQUEST_ID = "X-Request-ID" - HEADER_VERSION_ID = "X-Version-ID" - HEADER_CLUSTER_ID = "X-Cluster-ID" - HEADER_ETAG_SERVER = "ETag" - HEADER_ETAG_CLIENT = "If-None-Match" - HEADER_FORWARDED = "X-Forwarded-For" - HEADER_REAL_IP = "X-Real-IP" - HEADER_FORWARDED_PROTO = "X-Forwarded-Proto" - HEADER_TOKEN = "token" - HEADER_CSRF_TOKEN = "X-CSRF-Token" - HEADER_BEARER = "BEARER" - HEADER_AUTH = "Authorization" - HEADER_CLOUD_TOKEN = "X-Cloud-Token" - HEADER_REMOTECLUSTER_TOKEN = "X-RemoteCluster-Token" - HEADER_REMOTECLUSTER_ID = "X-RemoteCluster-Id" - HEADER_REQUESTED_WITH = "X-Requested-With" - HEADER_REQUESTED_WITH_XML = "XMLHttpRequest" - HEADER_RANGE = "Range" - STATUS = "status" - STATUS_OK = "OK" - STATUS_FAIL = "FAIL" - STATUS_UNHEALTHY = "UNHEALTHY" - STATUS_REMOVE = "REMOVE" - - CLIENT_DIR = "client" - - API_URL_SUFFIX_V1 = "/api/v1" - API_URL_SUFFIX_V4 = "/api/v4" - API_URL_SUFFIX = API_URL_SUFFIX_V4 -) - -type Response struct { - StatusCode int - Error *AppError - RequestId string - Etag string - ServerVersion string - Header http.Header -} - -type Client4 struct { - Url string // The location of the server, for example "http://localhost:8065" - ApiUrl string // The api location of the server, for example "http://localhost:8065/api/v4" - HttpClient *http.Client // The http client - AuthToken string - AuthType string - HttpHeader map[string]string // Headers to be copied over for each request - - // TrueString is the string value sent to the server for true boolean query parameters. - trueString string - - // FalseString is the string value sent to the server for false boolean query parameters. - falseString string -} - -// SetBoolString is a helper method for overriding how true and false query string parameters are -// sent to the server. -// -// This method is only exposed for testing. It is never necessary to configure these values -// in production. -func (c *Client4) SetBoolString(value bool, valueStr string) { - if value { - c.trueString = valueStr - } else { - c.falseString = valueStr - } -} - -// boolString builds the query string parameter for boolean values. -func (c *Client4) boolString(value bool) string { - if value && c.trueString != "" { - return c.trueString - } else if !value && c.falseString != "" { - return c.falseString - } - - if value { - return "true" - } - return "false" -} - -func closeBody(r *http.Response) { - if r.Body != nil { - _, _ = io.Copy(ioutil.Discard, r.Body) - _ = r.Body.Close() - } -} - -// Must is a convenience function used for testing. -func (c *Client4) Must(result interface{}, resp *Response) interface{} { - if resp.Error != nil { - time.Sleep(time.Second) - panic(resp.Error) - } - - return result -} - -func NewAPIv4Client(url string) *Client4 { - url = strings.TrimRight(url, "/") - return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}, "", ""} -} - -func NewAPIv4SocketClient(socketPath string) *Client4 { - tr := &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { - return net.Dial("unix", socketPath) - }, - } - - client := NewAPIv4Client("http://_") - client.HttpClient = &http.Client{Transport: tr} - - return client -} - -func BuildErrorResponse(r *http.Response, err *AppError) *Response { - var statusCode int - var header http.Header - if r != nil { - statusCode = r.StatusCode - header = r.Header - } else { - statusCode = 0 - header = make(http.Header) - } - - return &Response{ - StatusCode: statusCode, - Error: err, - Header: header, - } -} - -func BuildResponse(r *http.Response) *Response { - return &Response{ - StatusCode: r.StatusCode, - RequestId: r.Header.Get(HEADER_REQUEST_ID), - Etag: r.Header.Get(HEADER_ETAG_SERVER), - ServerVersion: r.Header.Get(HEADER_VERSION_ID), - Header: r.Header, - } -} - -func (c *Client4) SetToken(token string) { - c.AuthToken = token - c.AuthType = HEADER_BEARER -} - -// MockSession is deprecated in favour of SetToken -func (c *Client4) MockSession(token string) { - c.SetToken(token) -} - -func (c *Client4) SetOAuthToken(token string) { - c.AuthToken = token - c.AuthType = HEADER_TOKEN -} - -func (c *Client4) ClearOAuthToken() { - c.AuthToken = "" - c.AuthType = HEADER_BEARER -} - -func (c *Client4) GetUsersRoute() string { - return "/users" -} - -func (c *Client4) GetUserRoute(userId string) string { - return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) -} - -func (c *Client4) GetUserThreadsRoute(userID, teamID string) string { - return c.GetUserRoute(userID) + c.GetTeamRoute(teamID) + "/threads" -} - -func (c *Client4) GetUserThreadRoute(userId, teamId, threadId string) string { - return c.GetUserThreadsRoute(userId, teamId) + "/" + threadId -} - -func (c *Client4) GetUserCategoryRoute(userID, teamID string) string { - return c.GetUserRoute(userID) + c.GetTeamRoute(teamID) + "/channels/categories" -} - -func (c *Client4) GetUserAccessTokensRoute() string { - return fmt.Sprintf(c.GetUsersRoute() + "/tokens") -} - -func (c *Client4) GetUserAccessTokenRoute(tokenId string) string { - return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId) -} - -func (c *Client4) GetUserByUsernameRoute(userName string) string { - return fmt.Sprintf(c.GetUsersRoute()+"/username/%v", userName) -} - -func (c *Client4) GetUserByEmailRoute(email string) string { - return fmt.Sprintf(c.GetUsersRoute()+"/email/%v", email) -} - -func (c *Client4) GetBotsRoute() string { - return "/bots" -} - -func (c *Client4) GetBotRoute(botUserId string) string { - return fmt.Sprintf("%s/%s", c.GetBotsRoute(), botUserId) -} - -func (c *Client4) GetTeamsRoute() string { - return "/teams" -} - -func (c *Client4) GetTeamRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId) -} - -func (c *Client4) GetTeamAutoCompleteCommandsRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamsRoute()+"/%v/commands/autocomplete", teamId) -} - -func (c *Client4) GetTeamByNameRoute(teamName string) string { - return fmt.Sprintf(c.GetTeamsRoute()+"/name/%v", teamName) -} - -func (c *Client4) GetTeamMemberRoute(teamId, userId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId)+"/members/%v", userId) -} - -func (c *Client4) GetTeamMembersRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId) + "/members") -} - -func (c *Client4) GetTeamStatsRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId) + "/stats") -} - -func (c *Client4) GetTeamImportRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId) + "/import") -} - -func (c *Client4) GetChannelsRoute() string { - return "/channels" -} - -func (c *Client4) GetChannelsForTeamRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId) + "/channels") -} - -func (c *Client4) GetChannelRoute(channelId string) string { - return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId) -} - -func (c *Client4) GetChannelByNameRoute(channelName, teamId string) string { - return fmt.Sprintf(c.GetTeamRoute(teamId)+"/channels/name/%v", channelName) -} - -func (c *Client4) GetChannelsForTeamForUserRoute(teamId, userId string, includeDeleted bool) string { - route := fmt.Sprintf(c.GetUserRoute(userId) + c.GetTeamRoute(teamId) + "/channels") - if includeDeleted { - query := fmt.Sprintf("?include_deleted=%v", includeDeleted) - return route + query - } - return route -} - -func (c *Client4) GetChannelByNameForTeamNameRoute(channelName, teamName string) string { - return fmt.Sprintf(c.GetTeamByNameRoute(teamName)+"/channels/name/%v", channelName) -} - -func (c *Client4) GetChannelMembersRoute(channelId string) string { - return fmt.Sprintf(c.GetChannelRoute(channelId) + "/members") -} - -func (c *Client4) GetChannelMemberRoute(channelId, userId string) string { - return fmt.Sprintf(c.GetChannelMembersRoute(channelId)+"/%v", userId) -} - -func (c *Client4) GetPostsRoute() string { - return "/posts" -} - -func (c *Client4) GetPostsEphemeralRoute() string { - return "/posts/ephemeral" -} - -func (c *Client4) GetConfigRoute() string { - return "/config" -} - -func (c *Client4) GetLicenseRoute() string { - return "/license" -} - -func (c *Client4) GetPostRoute(postId string) string { - return fmt.Sprintf(c.GetPostsRoute()+"/%v", postId) -} - -func (c *Client4) GetFilesRoute() string { - return "/files" -} - -func (c *Client4) GetFileRoute(fileId string) string { - return fmt.Sprintf(c.GetFilesRoute()+"/%v", fileId) -} - -func (c *Client4) GetUploadsRoute() string { - return "/uploads" -} - -func (c *Client4) GetUploadRoute(uploadId string) string { - return fmt.Sprintf("%s/%s", c.GetUploadsRoute(), uploadId) -} - -func (c *Client4) GetPluginsRoute() string { - return "/plugins" -} - -func (c *Client4) GetPluginRoute(pluginId string) string { - return fmt.Sprintf(c.GetPluginsRoute()+"/%v", pluginId) -} - -func (c *Client4) GetSystemRoute() string { - return "/system" -} - -func (c *Client4) GetCloudRoute() string { - return "/cloud" -} - -func (c *Client4) GetTestEmailRoute() string { - return "/email/test" -} - -func (c *Client4) GetTestSiteURLRoute() string { - return "/site_url/test" -} - -func (c *Client4) GetTestS3Route() string { - return "/file/s3_test" -} - -func (c *Client4) GetDatabaseRoute() string { - return "/database" -} - -func (c *Client4) GetCacheRoute() string { - return "/caches" -} - -func (c *Client4) GetClusterRoute() string { - return "/cluster" -} - -func (c *Client4) GetIncomingWebhooksRoute() string { - return "/hooks/incoming" -} - -func (c *Client4) GetIncomingWebhookRoute(hookID string) string { - return fmt.Sprintf(c.GetIncomingWebhooksRoute()+"/%v", hookID) -} - -func (c *Client4) GetComplianceReportsRoute() string { - return "/compliance/reports" -} - -func (c *Client4) GetComplianceReportRoute(reportId string) string { - return fmt.Sprintf("%s/%s", c.GetComplianceReportsRoute(), reportId) -} - -func (c *Client4) GetComplianceReportDownloadRoute(reportId string) string { - return fmt.Sprintf("%s/%s/download", c.GetComplianceReportsRoute(), reportId) -} - -func (c *Client4) GetOutgoingWebhooksRoute() string { - return "/hooks/outgoing" -} - -func (c *Client4) GetOutgoingWebhookRoute(hookID string) string { - return fmt.Sprintf(c.GetOutgoingWebhooksRoute()+"/%v", hookID) -} - -func (c *Client4) GetPreferencesRoute(userId string) string { - return fmt.Sprintf(c.GetUserRoute(userId) + "/preferences") -} - -func (c *Client4) GetUserStatusRoute(userId string) string { - return fmt.Sprintf(c.GetUserRoute(userId) + "/status") -} - -func (c *Client4) GetUserStatusesRoute() string { - return fmt.Sprintf(c.GetUsersRoute() + "/status") -} - -func (c *Client4) GetSamlRoute() string { - return "/saml" -} - -func (c *Client4) GetLdapRoute() string { - return "/ldap" -} - -func (c *Client4) GetBrandRoute() string { - return "/brand" -} - -func (c *Client4) GetDataRetentionRoute() string { - return "/data_retention" -} - -func (c *Client4) GetDataRetentionPolicyRoute(policyID string) string { - return fmt.Sprintf(c.GetDataRetentionRoute()+"/policies/%v", policyID) -} - -func (c *Client4) GetElasticsearchRoute() string { - return "/elasticsearch" -} - -func (c *Client4) GetBleveRoute() string { - return "/bleve" -} - -func (c *Client4) GetCommandsRoute() string { - return "/commands" -} - -func (c *Client4) GetCommandRoute(commandId string) string { - return fmt.Sprintf(c.GetCommandsRoute()+"/%v", commandId) -} - -func (c *Client4) GetCommandMoveRoute(commandId string) string { - return fmt.Sprintf(c.GetCommandsRoute()+"/%v/move", commandId) -} - -func (c *Client4) GetEmojisRoute() string { - return "/emoji" -} - -func (c *Client4) GetEmojiRoute(emojiId string) string { - return fmt.Sprintf(c.GetEmojisRoute()+"/%v", emojiId) -} - -func (c *Client4) GetEmojiByNameRoute(name string) string { - return fmt.Sprintf(c.GetEmojisRoute()+"/name/%v", name) -} - -func (c *Client4) GetReactionsRoute() string { - return "/reactions" -} - -func (c *Client4) GetOAuthAppsRoute() string { - return "/oauth/apps" -} - -func (c *Client4) GetOAuthAppRoute(appId string) string { - return fmt.Sprintf("/oauth/apps/%v", appId) -} - -func (c *Client4) GetOpenGraphRoute() string { - return "/opengraph" -} - -func (c *Client4) GetJobsRoute() string { - return "/jobs" -} - -func (c *Client4) GetRolesRoute() string { - return "/roles" -} - -func (c *Client4) GetSchemesRoute() string { - return "/schemes" -} - -func (c *Client4) GetSchemeRoute(id string) string { - return c.GetSchemesRoute() + fmt.Sprintf("/%v", id) -} - -func (c *Client4) GetAnalyticsRoute() string { - return "/analytics" -} - -func (c *Client4) GetTimezonesRoute() string { - return fmt.Sprintf(c.GetSystemRoute() + "/timezones") -} - -func (c *Client4) GetChannelSchemeRoute(channelId string) string { - return fmt.Sprintf(c.GetChannelsRoute()+"/%v/scheme", channelId) -} - -func (c *Client4) GetTeamSchemeRoute(teamId string) string { - return fmt.Sprintf(c.GetTeamsRoute()+"/%v/scheme", teamId) -} - -func (c *Client4) GetTotalUsersStatsRoute() string { - return fmt.Sprintf(c.GetUsersRoute() + "/stats") -} - -func (c *Client4) GetRedirectLocationRoute() string { - return "/redirect_location" -} - -func (c *Client4) GetServerBusyRoute() string { - return "/server_busy" -} - -func (c *Client4) GetUserTermsOfServiceRoute(userId string) string { - return c.GetUserRoute(userId) + "/terms_of_service" -} - -func (c *Client4) GetTermsOfServiceRoute() string { - return "/terms_of_service" -} - -func (c *Client4) GetGroupsRoute() string { - return "/groups" -} - -func (c *Client4) GetPublishUserTypingRoute(userId string) string { - return c.GetUserRoute(userId) + "/typing" -} - -func (c *Client4) GetGroupRoute(groupID string) string { - return fmt.Sprintf("%s/%s", c.GetGroupsRoute(), groupID) -} - -func (c *Client4) GetGroupSyncableRoute(groupID, syncableID string, syncableType GroupSyncableType) string { - return fmt.Sprintf("%s/%ss/%s", c.GetGroupRoute(groupID), strings.ToLower(syncableType.String()), syncableID) -} - -func (c *Client4) GetGroupSyncablesRoute(groupID string, syncableType GroupSyncableType) string { - return fmt.Sprintf("%s/%ss", c.GetGroupRoute(groupID), strings.ToLower(syncableType.String())) -} - -func (c *Client4) GetImportsRoute() string { - return "/imports" -} - -func (c *Client4) GetExportsRoute() string { - return "/exports" -} - -func (c *Client4) GetExportRoute(name string) string { - return fmt.Sprintf(c.GetExportsRoute()+"/%v", name) -} - -func (c *Client4) GetRemoteClusterRoute() string { - return "/remotecluster" -} - -func (c *Client4) GetSharedChannelsRoute() string { - return "/sharedchannels" -} - -func (c *Client4) GetPermissionsRoute() string { - return "/permissions" -} - -func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag) -} - -func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "") -} - -func (c *Client4) doApiDeleteBytes(url string, data []byte) (*http.Response, *AppError) { - return c.doApiRequestBytes(http.MethodDelete, c.ApiUrl+url, data, "") -} - -func (c *Client4) doApiPatchBytes(url string, data []byte) (*http.Response, *AppError) { - return c.doApiRequestBytes(http.MethodPatch, c.ApiUrl+url, data, "") -} - -func (c *Client4) doApiPostBytes(url string, data []byte) (*http.Response, *AppError) { - return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "") -} - -func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "") -} - -func (c *Client4) doApiPutBytes(url string, data []byte) (*http.Response, *AppError) { - return c.doApiRequestBytes(http.MethodPut, c.ApiUrl+url, data, "") -} - -func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "") -} - -func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) { - return c.doApiRequestReader(method, url, strings.NewReader(data), map[string]string{HEADER_ETAG_CLIENT: etag}) -} - -func (c *Client4) DoApiRequestWithHeaders(method, url, data string, headers map[string]string) (*http.Response, *AppError) { - return c.doApiRequestReader(method, url, strings.NewReader(data), headers) -} - -func (c *Client4) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, *AppError) { - return c.doApiRequestReader(method, url, bytes.NewReader(data), map[string]string{HEADER_ETAG_CLIENT: etag}) -} - -func (c *Client4) doApiRequestReader(method, url string, data io.Reader, headers map[string]string) (*http.Response, *AppError) { - rq, err := http.NewRequest(method, url, data) - if err != nil { - return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest) - } - - for k, v := range headers { - rq.Header.Set(k, v) - } - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - if c.HttpHeader != nil && len(c.HttpHeader) > 0 { - for k, v := range c.HttpHeader { - rq.Header.Set(k, v) - } - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0) - } - - if rp.StatusCode == 304 { - return rp, nil - } - - if rp.StatusCode >= 300 { - defer closeBody(rp) - return rp, AppErrorFromJson(rp.Body) - } - - return rp, nil -} - -func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*FileUploadResponse, *Response) { - return c.doUploadFile(url, bytes.NewReader(data), contentType, 0) -} - -func (c *Client4) doUploadFile(url string, body io.Reader, contentType string, contentLength int64) (*FileUploadResponse, *Response) { - rq, err := http.NewRequest("POST", c.ApiUrl+url, body) - if err != nil { - return nil, &Response{Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - if contentLength != 0 { - rq.ContentLength = contentLength - } - rq.Header.Set("Content-Type", contentType) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return FileUploadResponseFromJson(rp.Body), BuildResponse(rp) -} - -func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string) (*Emoji, *Response) { - rq, err := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) - if err != nil { - return nil, &Response{Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", contentType) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return EmojiFromJson(rp.Body), BuildResponse(rp) -} - -func (c *Client4) DoUploadImportTeam(url string, data []byte, contentType string) (map[string]string, *Response) { - rq, err := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) - if err != nil { - return nil, &Response{Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", contentType) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return MapFromJson(rp.Body), BuildResponse(rp) -} - -// CheckStatusOK is a convenience function for checking the standard OK response -// from the web service. -func CheckStatusOK(r *http.Response) bool { - m := MapFromJson(r.Body) - defer closeBody(r) - - if m != nil && m[STATUS] == STATUS_OK { - return true - } - - return false -} - -// Authentication Section - -// LoginById authenticates a user by user id and password. -func (c *Client4) LoginById(id string, password string) (*User, *Response) { - m := make(map[string]string) - m["id"] = id - m["password"] = password - return c.login(m) -} - -// Login authenticates a user by login id, which can be username, email or some sort -// of SSO identifier based on server configuration, and a password. -func (c *Client4) Login(loginId string, password string) (*User, *Response) { - m := make(map[string]string) - m["login_id"] = loginId - m["password"] = password - return c.login(m) -} - -// LoginByLdap authenticates a user by LDAP id and password. -func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response) { - m := make(map[string]string) - m["login_id"] = loginId - m["password"] = password - m["ldap_only"] = c.boolString(true) - return c.login(m) -} - -// LoginWithDevice authenticates a user by login id (username, email or some sort -// of SSO identifier based on configuration), password and attaches a device id to -// the session. -func (c *Client4) LoginWithDevice(loginId string, password string, deviceId string) (*User, *Response) { - m := make(map[string]string) - m["login_id"] = loginId - m["password"] = password - m["device_id"] = deviceId - return c.login(m) -} - -// LoginWithMFA logs a user in with a MFA token -func (c *Client4) LoginWithMFA(loginId, password, mfaToken string) (*User, *Response) { - m := make(map[string]string) - m["login_id"] = loginId - m["password"] = password - m["token"] = mfaToken - return c.login(m) -} - -func (c *Client4) login(m map[string]string) (*User, *Response) { - r, err := c.DoApiPost("/users/login", MapToJson(m)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - c.AuthToken = r.Header.Get(HEADER_TOKEN) - c.AuthType = HEADER_BEARER - return UserFromJson(r.Body), BuildResponse(r) -} - -// Logout terminates the current user's session. -func (c *Client4) Logout() (bool, *Response) { - r, err := c.DoApiPost("/users/logout", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - c.AuthToken = "" - c.AuthType = HEADER_BEARER - return CheckStatusOK(r), BuildResponse(r) -} - -// SwitchAccountType changes a user's login type from one type to another. -func (c *Client4) SwitchAccountType(switchRequest *SwitchRequest) (string, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/login/switch", switchRequest.ToJson()) - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["follow_link"], BuildResponse(r) -} - -// User Section - -// CreateUser creates a user in the system based on the provided user struct. -func (c *Client4) CreateUser(user *User) (*User, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute(), user.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// CreateUserWithToken creates a user in the system based on the provided tokenId. -func (c *Client4) CreateUserWithToken(user *User, tokenId string) (*User, *Response) { - if tokenId == "" { - err := NewAppError("MissingHashOrData", "api.user.create_user.missing_token.app_error", nil, "", http.StatusBadRequest) - return nil, &Response{StatusCode: err.StatusCode, Error: err} - } - - query := fmt.Sprintf("?t=%v", tokenId) - r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return UserFromJson(r.Body), BuildResponse(r) -} - -// CreateUserWithInviteId creates a user in the system based on the provided invited id. -func (c *Client4) CreateUserWithInviteId(user *User, inviteId string) (*User, *Response) { - if inviteId == "" { - err := NewAppError("MissingInviteId", "api.user.create_user.missing_invite_id.app_error", nil, "", http.StatusBadRequest) - return nil, &Response{StatusCode: err.StatusCode, Error: err} - } - - query := fmt.Sprintf("?iid=%v", url.QueryEscape(inviteId)) - r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return UserFromJson(r.Body), BuildResponse(r) -} - -// GetMe returns the logged in user. -func (c *Client4) GetMe(etag string) (*User, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(ME), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// GetUser returns a user based on the provided user id string. -func (c *Client4) GetUser(userId, etag string) (*User, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// GetUserByUsername returns a user based on the provided user name string. -func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) { - r, err := c.DoApiGet(c.GetUserByUsernameRoute(userName), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// GetUserByEmail returns a user based on the provided user email string. -func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) { - r, err := c.DoApiGet(c.GetUserByEmailRoute(email), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// AutocompleteUsersInTeam returns the users on a team based on search term. -func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, limit int, etag string) (*UserAutocomplete, *Response) { - query := fmt.Sprintf("?in_team=%v&name=%v&limit=%d", teamId, username, limit) - r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAutocompleteFromJson(r.Body), BuildResponse(r) -} - -// AutocompleteUsersInChannel returns the users in a channel based on search term. -func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, limit int, etag string) (*UserAutocomplete, *Response) { - query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v&limit=%d", teamId, channelId, username, limit) - r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAutocompleteFromJson(r.Body), BuildResponse(r) -} - -// AutocompleteUsers returns the users in the system based on search term. -func (c *Client4) AutocompleteUsers(username string, limit int, etag string) (*UserAutocomplete, *Response) { - query := fmt.Sprintf("?name=%v&limit=%d", username, limit) - r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAutocompleteFromJson(r.Body), BuildResponse(r) -} - -// GetDefaultProfileImage gets the default user's profile image. Must be logged in. -func (c *Client4) GetDefaultProfileImage(userId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetUserRoute(userId)+"/image/default", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetDefaultProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - - return data, BuildResponse(r) -} - -// GetProfileImage gets user's profile image. Must be logged in. -func (c *Client4) GetProfileImage(userId, etag string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetUserRoute(userId)+"/image", etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// GetUsers returns a page of users on the system. Page counting starts at 0. -func (c *Client4) GetUsers(page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersInTeam returns a page of users on a team. Page counting starts at 0. -func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?in_team=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetNewUsersInTeam returns a page of users on a team. Page counting starts at 0. -func (c *Client4) GetNewUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?sort=create_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetRecentlyActiveUsersInTeam returns a page of users on a team. Page counting starts at 0. -func (c *Client4) GetRecentlyActiveUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?sort=last_activity_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetActiveUsersInTeam returns a page of users on a team. Page counting starts at 0. -func (c *Client4) GetActiveUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?active=true&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersNotInTeam returns a page of users who are not in a team. Page counting starts at 0. -func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?not_in_team=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersInChannel returns a page of users in a channel. Page counting starts at 0. -func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersInChannelByStatus returns a page of users in a channel. Page counting starts at 0. Sorted by Status -func (c *Client4) GetUsersInChannelByStatus(channelId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v&sort=status", channelId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersNotInChannel returns a page of users not in a channel. Page counting starts at 0. -func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?in_team=%v¬_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersWithoutTeam returns a page of users on the system that aren't on any teams. Page counting starts at 0. -func (c *Client4) GetUsersWithoutTeam(page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?without_team=1&page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersInGroup returns a page of users in a group. Page counting starts at 0. -func (c *Client4) GetUsersInGroup(groupID string, page int, perPage int, etag string) ([]*User, *Response) { - query := fmt.Sprintf("?in_group=%v&page=%v&per_page=%v", groupID, page, perPage) - r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersByIds returns a list of users based on the provided user ids. -func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/ids", ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersByIds returns a list of users based on the provided user ids. -func (c *Client4) GetUsersByIdsWithOptions(userIds []string, options *UserGetByIdsOptions) ([]*User, *Response) { - v := url.Values{} - if options.Since != 0 { - v.Set("since", fmt.Sprintf("%d", options.Since)) - } - - url := c.GetUsersRoute() + "/ids" - if len(v) > 0 { - url += "?" + v.Encode() - } - - r, err := c.DoApiPost(url, ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersByUsernames returns a list of users based on the provided usernames. -func (c *Client4) GetUsersByUsernames(usernames []string) ([]*User, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/usernames", ArrayToJson(usernames)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// GetUsersByGroupChannelIds returns a map with channel ids as keys -// and a list of users as values based on the provided user ids. -func (c *Client4) GetUsersByGroupChannelIds(groupChannelIds []string) (map[string][]*User, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/group_channels", ArrayToJson(groupChannelIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - usersByChannelId := map[string][]*User{} - json.NewDecoder(r.Body).Decode(&usersByChannelId) - return usersByChannelId, BuildResponse(r) -} - -// SearchUsers returns a list of users based on some search criteria. -func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) { - r, err := c.doApiPostBytes(c.GetUsersRoute()+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserListFromJson(r.Body), BuildResponse(r) -} - -// UpdateUser updates a user in the system based on the provided user struct. -func (c *Client4) UpdateUser(user *User) (*User, *Response) { - r, err := c.DoApiPut(c.GetUserRoute(user.Id), user.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// PatchUser partially updates a user in the system. Any missing fields are not updated. -func (c *Client4) PatchUser(userId string, patch *UserPatch) (*User, *Response) { - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/patch", patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// UpdateUserAuth updates a user AuthData (uthData, authService and password) in the system. -func (c *Client4) UpdateUserAuth(userId string, userAuth *UserAuth) (*UserAuth, *Response) { - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/auth", userAuth.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAuthFromJson(r.Body), BuildResponse(r) -} - -// UpdateUserMfa activates multi-factor authentication for a user if activate -// is true and a valid code is provided. If activate is false, then code is not -// required and multi-factor authentication is disabled for the user. -func (c *Client4) UpdateUserMfa(userId, code string, activate bool) (bool, *Response) { - requestBody := make(map[string]interface{}) - requestBody["activate"] = activate - requestBody["code"] = code - - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/mfa", StringInterfaceToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// CheckUserMfa checks whether a user has MFA active on their account or not based on the -// provided login id. -// Deprecated: Clients should use Login method and check for MFA Error -func (c *Client4) CheckUserMfa(loginId string) (bool, *Response) { - requestBody := make(map[string]interface{}) - requestBody["login_id"] = loginId - r, err := c.DoApiPost(c.GetUsersRoute()+"/mfa", StringInterfaceToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - - data := StringInterfaceFromJson(r.Body) - mfaRequired, ok := data["mfa_required"].(bool) - if !ok { - return false, BuildResponse(r) - } - return mfaRequired, BuildResponse(r) -} - -// GenerateMfaSecret will generate a new MFA secret for a user and return it as a string and -// as a base64 encoded image QR code. -func (c *Client4) GenerateMfaSecret(userId string) (*MfaSecret, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/mfa/generate", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MfaSecretFromJson(r.Body), BuildResponse(r) -} - -// UpdateUserPassword updates a user's password. Must be logged in as the user or be a system administrator. -func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string) (bool, *Response) { - requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword} - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateUserHashedPassword updates a user's password with an already-hashed password. Must be a system administrator. -func (c *Client4) UpdateUserHashedPassword(userId, newHashedPassword string) (bool, *Response) { - requestBody := map[string]string{"already_hashed": "true", "new_password": newHashedPassword} - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PromoteGuestToUser convert a guest into a regular user -func (c *Client4) PromoteGuestToUser(guestId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/promote", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DemoteUserToGuest convert a regular user into a guest -func (c *Client4) DemoteUserToGuest(guestId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/demote", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles. -func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) { - requestBody := map[string]string{"roles": roles} - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/roles", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateUserActive updates status of a user whether active or not. -func (c *Client4) UpdateUserActive(userId string, active bool) (bool, *Response) { - requestBody := make(map[string]interface{}) - requestBody["active"] = active - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/active", StringInterfaceToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return CheckStatusOK(r), BuildResponse(r) -} - -// DeleteUser deactivates a user in the system based on the provided user id string. -func (c *Client4) DeleteUser(userId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetUserRoute(userId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PermanentDeleteUser deletes a user in the system based on the provided user id string. -func (c *Client4) PermanentDeleteUser(userId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetUserRoute(userId) + "?permanent=" + c.boolString(true)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// ConvertUserToBot converts a user to a bot user. -func (c *Client4) ConvertUserToBot(userId string) (*Bot, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/convert_to_bot", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// ConvertBotToUser converts a bot user to a user. -func (c *Client4) ConvertBotToUser(userId string, userPatch *UserPatch, setSystemAdmin bool) (*User, *Response) { - var query string - if setSystemAdmin { - query = "?set_system_admin=true" - } - r, err := c.DoApiPost(c.GetBotRoute(userId)+"/convert_to_user"+query, userPatch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// PermanentDeleteAll permanently deletes all users in the system. This is a local only endpoint -func (c *Client4) PermanentDeleteAllUsers() (bool, *Response) { - r, err := c.DoApiDelete(c.GetUsersRoute()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// SendPasswordResetEmail will send a link for password resetting to a user with the -// provided email. -func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) { - requestBody := map[string]string{"email": email} - r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset/send", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// ResetPassword uses a recovery code to update reset a user's password. -func (c *Client4) ResetPassword(token, newPassword string) (bool, *Response) { - requestBody := map[string]string{"token": token, "new_password": newPassword} - r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetSessions returns a list of sessions based on the provided user id string. -func (c *Client4) GetSessions(userId, etag string) ([]*Session, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/sessions", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SessionsFromJson(r.Body), BuildResponse(r) -} - -// RevokeSession revokes a user session based on the provided user id and session id strings. -func (c *Client4) RevokeSession(userId, sessionId string) (bool, *Response) { - requestBody := map[string]string{"session_id": sessionId} - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// RevokeAllSessions revokes all sessions for the provided user id string. -func (c *Client4) RevokeAllSessions(userId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke/all", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// RevokeAllSessions revokes all sessions for all the users. -func (c *Client4) RevokeSessionsFromAllUsers() (bool, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/sessions/revoke/all", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// AttachDeviceId attaches a mobile device ID to the current session. -func (c *Client4) AttachDeviceId(deviceId string) (bool, *Response) { - requestBody := map[string]string{"device_id": deviceId} - r, err := c.DoApiPut(c.GetUsersRoute()+"/sessions/device", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetTeamsUnreadForUser will return an array with TeamUnread objects that contain the amount -// of unread messages and mentions the current user has for the teams it belongs to. -// An optional team ID can be set to exclude that team from the results. -// An optional boolean can be set to include collapsed thread unreads. Must be authenticated. -func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string, includeCollapsedThreads bool) ([]*TeamUnread, *Response) { - query := url.Values{} - - if teamIdToExclude != "" { - query.Set("exclude_team", teamIdToExclude) - } - - if includeCollapsedThreads { - query.Set("include_collapsed_threads", "true") - } - - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread?"+query.Encode(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamsUnreadFromJson(r.Body), BuildResponse(r) -} - -// GetUserAudits returns a list of audit based on the provided user id string. -func (c *Client4) GetUserAudits(userId string, page int, perPage int, etag string) (Audits, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/audits"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return AuditsFromJson(r.Body), BuildResponse(r) -} - -// VerifyUserEmail will verify a user's email using the supplied token. -func (c *Client4) VerifyUserEmail(token string) (bool, *Response) { - requestBody := map[string]string{"token": token} - r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// VerifyUserEmailWithoutToken will verify a user's email by its Id. (Requires manage system role) -func (c *Client4) VerifyUserEmailWithoutToken(userId string) (*User, *Response) { - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/email/verify/member", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserFromJson(r.Body), BuildResponse(r) -} - -// SendVerificationEmail will send an email to the user with the provided email address, if -// that user exists. The email will contain a link that can be used to verify the user's -// email address. -func (c *Client4) SendVerificationEmail(email string) (bool, *Response) { - requestBody := map[string]string{"email": email} - r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify/send", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// SetDefaultProfileImage resets the profile image to a default generated one. -func (c *Client4) SetDefaultProfileImage(userId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetUserRoute(userId) + "/image") - if err != nil { - return false, BuildErrorResponse(r, err) - } - return CheckStatusOK(r), BuildResponse(r) -} - -// SetProfileImage sets profile image of the user. -func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("image", "profile.png") - if err != nil { - return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err = writer.Close(); err != nil { - return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetUserRoute(userId)+"/image", bytes.NewReader(body.Bytes())) - if err != nil { - return false, &Response{Error: NewAppError("SetProfileImage", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetUserRoute(userId)+"/image", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return CheckStatusOK(rp), BuildResponse(rp) -} - -// CreateUserAccessToken will generate a user access token that can be used in place -// of a session token to access the REST API. Must have the 'create_user_access_token' -// permission and if generating for another user, must have the 'edit_other_users' -// permission. A non-blank description is required. -func (c *Client4) CreateUserAccessToken(userId, description string) (*UserAccessToken, *Response) { - requestBody := map[string]string{"description": description} - r, err := c.DoApiPost(c.GetUserRoute(userId)+"/tokens", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAccessTokenFromJson(r.Body), BuildResponse(r) -} - -// GetUserAccessTokens will get a page of access tokens' id, description, is_active -// and the user_id in the system. The actual token will not be returned. Must have -// the 'manage_system' permission. -func (c *Client4) GetUserAccessTokens(page int, perPage int) ([]*UserAccessToken, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUserAccessTokensRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAccessTokenListFromJson(r.Body), BuildResponse(r) -} - -// GetUserAccessToken will get a user access tokens' id, description, is_active -// and the user_id of the user it is for. The actual token will not be returned. -// Must have the 'read_user_access_token' permission and if getting for another -// user, must have the 'edit_other_users' permission. -func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) { - r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAccessTokenFromJson(r.Body), BuildResponse(r) -} - -// GetUserAccessTokensForUser will get a paged list of user access tokens showing id, -// description and user_id for each. The actual tokens will not be returned. Must have -// the 'read_user_access_token' permission and if getting for another user, must have the -// 'edit_other_users' permission. -func (c *Client4) GetUserAccessTokensForUser(userId string, page, perPage int) ([]*UserAccessToken, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/tokens"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAccessTokenListFromJson(r.Body), BuildResponse(r) -} - -// RevokeUserAccessToken will revoke a user access token by id. Must have the -// 'revoke_user_access_token' permission and if revoking for another user, must have the -// 'edit_other_users' permission. -func (c *Client4) RevokeUserAccessToken(tokenId string) (bool, *Response) { - requestBody := map[string]string{"token_id": tokenId} - r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/revoke", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// SearchUserAccessTokens returns user access tokens matching the provided search term. -func (c *Client4) SearchUserAccessTokens(search *UserAccessTokenSearch) ([]*UserAccessToken, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserAccessTokenListFromJson(r.Body), BuildResponse(r) -} - -// DisableUserAccessToken will disable a user access token by id. Must have the -// 'revoke_user_access_token' permission and if disabling for another user, must have the -// 'edit_other_users' permission. -func (c *Client4) DisableUserAccessToken(tokenId string) (bool, *Response) { - requestBody := map[string]string{"token_id": tokenId} - r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/disable", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// EnableUserAccessToken will enable a user access token by id. Must have the -// 'create_user_access_token' permission and if enabling for another user, must have the -// 'edit_other_users' permission. -func (c *Client4) EnableUserAccessToken(tokenId string) (bool, *Response) { - requestBody := map[string]string{"token_id": tokenId} - r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/enable", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Bots section - -// CreateBot creates a bot in the system based on the provided bot struct. -func (c *Client4) CreateBot(bot *Bot) (*Bot, *Response) { - r, err := c.doApiPostBytes(c.GetBotsRoute(), bot.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// PatchBot partially updates a bot. Any missing fields are not updated. -func (c *Client4) PatchBot(userId string, patch *BotPatch) (*Bot, *Response) { - r, err := c.doApiPutBytes(c.GetBotRoute(userId), patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// GetBot fetches the given, undeleted bot. -func (c *Client4) GetBot(userId string, etag string) (*Bot, *Response) { - r, err := c.DoApiGet(c.GetBotRoute(userId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// GetBot fetches the given bot, even if it is deleted. -func (c *Client4) GetBotIncludeDeleted(userId string, etag string) (*Bot, *Response) { - r, err := c.DoApiGet(c.GetBotRoute(userId)+"?include_deleted="+c.boolString(true), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// GetBots fetches the given page of bots, excluding deleted. -func (c *Client4) GetBots(page, perPage int, etag string) ([]*Bot, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetBotsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotListFromJson(r.Body), BuildResponse(r) -} - -// GetBotsIncludeDeleted fetches the given page of bots, including deleted. -func (c *Client4) GetBotsIncludeDeleted(page, perPage int, etag string) ([]*Bot, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted="+c.boolString(true), page, perPage) - r, err := c.DoApiGet(c.GetBotsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotListFromJson(r.Body), BuildResponse(r) -} - -// GetBotsOrphaned fetches the given page of bots, only including orphanded bots. -func (c *Client4) GetBotsOrphaned(page, perPage int, etag string) ([]*Bot, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&only_orphaned="+c.boolString(true), page, perPage) - r, err := c.DoApiGet(c.GetBotsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotListFromJson(r.Body), BuildResponse(r) -} - -// DisableBot disables the given bot in the system. -func (c *Client4) DisableBot(botUserId string) (*Bot, *Response) { - r, err := c.doApiPostBytes(c.GetBotRoute(botUserId)+"/disable", nil) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// EnableBot disables the given bot in the system. -func (c *Client4) EnableBot(botUserId string) (*Bot, *Response) { - r, err := c.doApiPostBytes(c.GetBotRoute(botUserId)+"/enable", nil) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// AssignBot assigns the given bot to the given user -func (c *Client4) AssignBot(botUserId, newOwnerId string) (*Bot, *Response) { - r, err := c.doApiPostBytes(c.GetBotRoute(botUserId)+"/assign/"+newOwnerId, nil) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return BotFromJson(r.Body), BuildResponse(r) -} - -// SetBotIconImage sets LHS bot icon image. -func (c *Client4) SetBotIconImage(botUserId string, data []byte) (bool, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("image", "icon.svg") - if err != nil { - return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err = writer.Close(); err != nil { - return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetBotRoute(botUserId)+"/icon", bytes.NewReader(body.Bytes())) - if err != nil { - return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetBotRoute(botUserId)+"/icon", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return CheckStatusOK(rp), BuildResponse(rp) -} - -// GetBotIconImage gets LHS bot icon image. Must be logged in. -func (c *Client4) GetBotIconImage(botUserId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetBotRoute(botUserId)+"/icon", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetBotIconImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// DeleteBotIconImage deletes LHS bot icon image. Must be logged in. -func (c *Client4) DeleteBotIconImage(botUserId string) (bool, *Response) { - r, appErr := c.DoApiDelete(c.GetBotRoute(botUserId) + "/icon") - if appErr != nil { - return false, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Team Section - -// CreateTeam creates a team in the system based on the provided team struct. -func (c *Client4) CreateTeam(team *Team) (*Team, *Response) { - r, err := c.DoApiPost(c.GetTeamsRoute(), team.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// GetTeam returns a team based on the provided team id string. -func (c *Client4) GetTeam(teamId, etag string) (*Team, *Response) { - r, err := c.DoApiGet(c.GetTeamRoute(teamId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// GetAllTeams returns all teams based on permissions. -func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamListFromJson(r.Body), BuildResponse(r) -} - -// GetAllTeamsWithTotalCount returns all teams based on permissions. -func (c *Client4) GetAllTeamsWithTotalCount(etag string, page int, perPage int) ([]*Team, int64, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage) - r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag) - if err != nil { - return nil, 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - teamsListWithCount := TeamsWithCountFromJson(r.Body) - return teamsListWithCount.Teams, teamsListWithCount.TotalCount, BuildResponse(r) -} - -// GetAllTeamsExcludePolicyConstrained returns all teams which are not part of a data retention policy. -// Must be a system administrator. -func (c *Client4) GetAllTeamsExcludePolicyConstrained(etag string, page int, perPage int) ([]*Team, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&exclude_policy_constrained=%v", page, perPage, true) - r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamListFromJson(r.Body), BuildResponse(r) -} - -// GetTeamByName returns a team based on the provided team name string. -func (c *Client4) GetTeamByName(name, etag string) (*Team, *Response) { - r, err := c.DoApiGet(c.GetTeamByNameRoute(name), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// SearchTeams returns teams matching the provided search term. -func (c *Client4) SearchTeams(search *TeamSearch) ([]*Team, *Response) { - r, err := c.DoApiPost(c.GetTeamsRoute()+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamListFromJson(r.Body), BuildResponse(r) -} - -// SearchTeamsPaged returns a page of teams and the total count matching the provided search term. -func (c *Client4) SearchTeamsPaged(search *TeamSearch) ([]*Team, int64, *Response) { - if search.Page == nil { - search.Page = NewInt(0) - } - if search.PerPage == nil { - search.PerPage = NewInt(100) - } - r, err := c.DoApiPost(c.GetTeamsRoute()+"/search", search.ToJson()) - if err != nil { - return nil, 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - twc := TeamsWithCountFromJson(r.Body) - return twc.Teams, twc.TotalCount, BuildResponse(r) -} - -// TeamExists returns true or false if the team exist or not. -func (c *Client4) TeamExists(name, etag string) (bool, *Response) { - r, err := c.DoApiGet(c.GetTeamByNameRoute(name)+"/exists", etag) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapBoolFromJson(r.Body)["exists"], BuildResponse(r) -} - -// GetTeamsForUser returns a list of teams a user is on. Must be logged in as the user -// or be a system administrator. -func (c *Client4) GetTeamsForUser(userId, etag string) ([]*Team, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamListFromJson(r.Body), BuildResponse(r) -} - -// GetTeamMember returns a team member based on the provided team and user id strings. -func (c *Client4) GetTeamMember(teamId, userId, etag string) (*TeamMember, *Response) { - r, err := c.DoApiGet(c.GetTeamMemberRoute(teamId, userId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMemberFromJson(r.Body), BuildResponse(r) -} - -// UpdateTeamMemberRoles will update the roles on a team for a user. -func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool, *Response) { - requestBody := map[string]string{"roles": newRoles} - r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/roles", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateTeamMemberSchemeRoles will update the scheme-derived roles on a team for a user. -func (c *Client4) UpdateTeamMemberSchemeRoles(teamId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) { - r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/schemeRoles", schemeRoles.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateTeam will update a team. -func (c *Client4) UpdateTeam(team *Team) (*Team, *Response) { - r, err := c.DoApiPut(c.GetTeamRoute(team.Id), team.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// PatchTeam partially updates a team. Any missing fields are not updated. -func (c *Client4) PatchTeam(teamId string, patch *TeamPatch) (*Team, *Response) { - r, err := c.DoApiPut(c.GetTeamRoute(teamId)+"/patch", patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// RestoreTeam restores a previously deleted team. -func (c *Client4) RestoreTeam(teamId string) (*Team, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/restore", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// RegenerateTeamInviteId requests a new invite ID to be generated. -func (c *Client4) RegenerateTeamInviteId(teamId string) (*Team, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/regenerate_invite_id", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// SoftDeleteTeam deletes the team softly (archive only, not permanent delete). -func (c *Client4) SoftDeleteTeam(teamId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetTeamRoute(teamId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PermanentDeleteTeam deletes the team, should only be used when needed for -// compliance and the like. -func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=" + c.boolString(true)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateTeamPrivacy modifies the team type (model.TEAM_OPEN <--> model.TEAM_INVITE) and sets -// the corresponding AllowOpenInvite appropriately. -func (c *Client4) UpdateTeamPrivacy(teamId string, privacy string) (*Team, *Response) { - requestBody := map[string]string{"privacy": privacy} - r, err := c.DoApiPut(c.GetTeamRoute(teamId)+"/privacy", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// GetTeamMembers returns team members based on the provided team id string. -func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag string) ([]*TeamMember, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersFromJson(r.Body), BuildResponse(r) -} - -// GetTeamMembersWithoutDeletedUsers returns team members based on the provided team id string. Additional parameters of sort and exclude_deleted_users accepted as well -// Could not add it to above function due to it be a breaking change. -func (c *Client4) GetTeamMembersSortAndWithoutDeletedUsers(teamId string, page int, perPage int, sort string, exclude_deleted_users bool, etag string) ([]*TeamMember, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&sort=%v&exclude_deleted_users=%v", page, perPage, sort, exclude_deleted_users) - r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersFromJson(r.Body), BuildResponse(r) -} - -// GetTeamMembersForUser returns the team members for a user. -func (c *Client4) GetTeamMembersForUser(userId string, etag string) ([]*TeamMember, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/members", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersFromJson(r.Body), BuildResponse(r) -} - -// GetTeamMembersByIds will return an array of team members based on the -// team id and a list of user ids provided. Must be authenticated. -func (c *Client4) GetTeamMembersByIds(teamId string, userIds []string) ([]*TeamMember, *Response) { - r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersFromJson(r.Body), BuildResponse(r) -} - -// AddTeamMember adds user to a team and return a team member. -func (c *Client4) AddTeamMember(teamId, userId string) (*TeamMember, *Response) { - member := &TeamMember{TeamId: teamId, UserId: userId} - r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId), member.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMemberFromJson(r.Body), BuildResponse(r) -} - -// AddTeamMemberFromInvite adds a user to a team and return a team member using an invite id -// or an invite token/data pair. -func (c *Client4) AddTeamMemberFromInvite(token, inviteId string) (*TeamMember, *Response) { - var query string - - if inviteId != "" { - query += fmt.Sprintf("?invite_id=%v", inviteId) - } - - if token != "" { - query += fmt.Sprintf("?token=%v", token) - } - - r, err := c.DoApiPost(c.GetTeamsRoute()+"/members/invite"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMemberFromJson(r.Body), BuildResponse(r) -} - -// AddTeamMembers adds a number of users to a team and returns the team members. -func (c *Client4) AddTeamMembers(teamId string, userIds []string) ([]*TeamMember, *Response) { - var members []*TeamMember - for _, userId := range userIds { - member := &TeamMember{TeamId: teamId, UserId: userId} - members = append(members, member) - } - - r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch", TeamMembersToJson(members)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersFromJson(r.Body), BuildResponse(r) -} - -// AddTeamMembers adds a number of users to a team and returns the team members. -func (c *Client4) AddTeamMembersGracefully(teamId string, userIds []string) ([]*TeamMemberWithError, *Response) { - var members []*TeamMember - for _, userId := range userIds { - member := &TeamMember{TeamId: teamId, UserId: userId} - members = append(members, member) - } - - r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful="+c.boolString(true), TeamMembersToJson(members)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamMembersWithErrorFromJson(r.Body), BuildResponse(r) -} - -// RemoveTeamMember will remove a user from a team. -func (c *Client4) RemoveTeamMember(teamId, userId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetTeamMemberRoute(teamId, userId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetTeamStats returns a team stats based on the team id string. -// Must be authenticated. -func (c *Client4) GetTeamStats(teamId, etag string) (*TeamStats, *Response) { - r, err := c.DoApiGet(c.GetTeamStatsRoute(teamId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamStatsFromJson(r.Body), BuildResponse(r) -} - -// GetTotalUsersStats returns a total system user stats. -// Must be authenticated. -func (c *Client4) GetTotalUsersStats(etag string) (*UsersStats, *Response) { - r, err := c.DoApiGet(c.GetTotalUsersStatsRoute(), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UsersStatsFromJson(r.Body), BuildResponse(r) -} - -// GetTeamUnread will return a TeamUnread object that contains the amount of -// unread messages and mentions the user has for the specified team. -// Must be authenticated. -func (c *Client4) GetTeamUnread(teamId, userId string) (*TeamUnread, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/unread", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamUnreadFromJson(r.Body), BuildResponse(r) -} - -// ImportTeam will import an exported team from other app into a existing team. -func (c *Client4) ImportTeam(data []byte, filesize int, importFrom, filename, teamId string) (map[string]string, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("file", filename) - if err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - part, err = writer.CreateFormField("filesize") - if err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, strings.NewReader(strconv.Itoa(filesize))); err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - part, err = writer.CreateFormField("importFrom") - if err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err := io.Copy(part, strings.NewReader(importFrom)); err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err := writer.Close(); err != nil { - return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - return c.DoUploadImportTeam(c.GetTeamImportRoute(teamId), body.Bytes(), writer.FormDataContentType()) -} - -// InviteUsersToTeam invite users by email to the team. -func (c *Client4) InviteUsersToTeam(teamId string, userEmails []string) (bool, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email", ArrayToJson(userEmails)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// InviteGuestsToTeam invite guest by email to some channels in a team. -func (c *Client4) InviteGuestsToTeam(teamId string, userEmails []string, channels []string, message string) (bool, *Response) { - guestsInvite := GuestsInvite{ - Emails: userEmails, - Channels: channels, - Message: message, - } - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email", guestsInvite.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// InviteUsersToTeam invite users by email to the team. -func (c *Client4) InviteUsersToTeamGracefully(teamId string, userEmails []string) ([]*EmailInviteWithError, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email?graceful="+c.boolString(true), ArrayToJson(userEmails)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmailInviteWithErrorFromJson(r.Body), BuildResponse(r) -} - -// InviteGuestsToTeam invite guest by email to some channels in a team. -func (c *Client4) InviteGuestsToTeamGracefully(teamId string, userEmails []string, channels []string, message string) ([]*EmailInviteWithError, *Response) { - guestsInvite := GuestsInvite{ - Emails: userEmails, - Channels: channels, - Message: message, - } - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email?graceful="+c.boolString(true), guestsInvite.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmailInviteWithErrorFromJson(r.Body), BuildResponse(r) -} - -// InvalidateEmailInvites will invalidate active email invitations that have not been accepted by the user. -func (c *Client4) InvalidateEmailInvites() (bool, *Response) { - r, err := c.DoApiDelete(c.GetTeamsRoute() + "/invites/email") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetTeamInviteInfo returns a team object from an invite id containing sanitized information. -func (c *Client4) GetTeamInviteInfo(inviteId string) (*Team, *Response) { - r, err := c.DoApiGet(c.GetTeamsRoute()+"/invite/"+inviteId, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamFromJson(r.Body), BuildResponse(r) -} - -// SetTeamIcon sets team icon of the team. -func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("image", "teamIcon.png") - if err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err = writer.Close(); err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetTeamRoute(teamId)+"/image", bytes.NewReader(body.Bytes())) - if err != nil { - return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - // set to http.StatusForbidden(403) - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetTeamRoute(teamId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return CheckStatusOK(rp), BuildResponse(rp) -} - -// GetTeamIcon gets the team icon of the team. -func (c *Client4) GetTeamIcon(teamId, etag string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetTeamRoute(teamId)+"/image", etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetTeamIcon", "model.client.get_team_icon.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// RemoveTeamIcon updates LastTeamIconUpdate to 0 which indicates team icon is removed. -func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "/image") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Channel Section - -// GetAllChannels get all the channels. Must be a system administrator. -func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) { - return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{}) -} - -// GetAllChannelsIncludeDeleted get all the channels. Must be a system administrator. -func (c *Client4) GetAllChannelsIncludeDeleted(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) { - return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{IncludeDeleted: true}) -} - -// GetAllChannelsExcludePolicyConstrained gets all channels which are not part of a data retention policy. -// Must be a system administrator. -func (c *Client4) GetAllChannelsExcludePolicyConstrained(page, perPage int, etag string) (*ChannelListWithTeamData, *Response) { - return c.getAllChannels(page, perPage, etag, ChannelSearchOpts{ExcludePolicyConstrained: true}) -} - -func (c *Client4) getAllChannels(page int, perPage int, etag string, opts ChannelSearchOpts) (*ChannelListWithTeamData, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=%v&exclude_policy_constrained=%v", - page, perPage, opts.IncludeDeleted, opts.ExcludePolicyConstrained) - r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelListWithTeamDataFromJson(r.Body), BuildResponse(r) -} - -// GetAllChannelsWithCount get all the channels including the total count. Must be a system administrator. -func (c *Client4) GetAllChannelsWithCount(page int, perPage int, etag string) (*ChannelListWithTeamData, int64, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage) - r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag) - if err != nil { - return nil, 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - cwc := ChannelsWithCountFromJson(r.Body) - return cwc.Channels, cwc.TotalCount, BuildResponse(r) -} - -// CreateChannel creates a channel based on the provided channel struct. -func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// UpdateChannel updates a channel based on the provided channel struct. -func (c *Client4) UpdateChannel(channel *Channel) (*Channel, *Response) { - r, err := c.DoApiPut(c.GetChannelRoute(channel.Id), channel.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// PatchChannel partially updates a channel. Any missing fields are not updated. -func (c *Client4) PatchChannel(channelId string, patch *ChannelPatch) (*Channel, *Response) { - r, err := c.DoApiPut(c.GetChannelRoute(channelId)+"/patch", patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// ConvertChannelToPrivate converts public to private channel. -func (c *Client4) ConvertChannelToPrivate(channelId string) (*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/convert", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// UpdateChannelPrivacy updates channel privacy -func (c *Client4) UpdateChannelPrivacy(channelId string, privacy string) (*Channel, *Response) { - requestBody := map[string]string{"privacy": privacy} - r, err := c.DoApiPut(c.GetChannelRoute(channelId)+"/privacy", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// RestoreChannel restores a previously deleted channel. Any missing fields are not updated. -func (c *Client4) RestoreChannel(channelId string) (*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/restore", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// CreateDirectChannel creates a direct message channel based on the two user -// ids provided. -func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Response) { - requestBody := []string{userId1, userId2} - r, err := c.DoApiPost(c.GetChannelsRoute()+"/direct", ArrayToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// CreateGroupChannel creates a group message channel based on userIds provided. -func (c *Client4) CreateGroupChannel(userIds []string) (*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsRoute()+"/group", ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannel returns a channel based on the provided channel id string. -func (c *Client4) GetChannel(channelId, etag string) (*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelStats returns statistics for a channel. -func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/stats", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelStatsFromJson(r.Body), BuildResponse(r) -} - -// GetChannelMembersTimezones gets a list of timezones for a channel. -func (c *Client4) GetChannelMembersTimezones(channelId string) ([]string, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/timezones", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -// GetPinnedPosts gets a list of pinned posts. -func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPrivateChannelsForTeam returns a list of private channels based on the provided team id string. -func (c *Client4) GetPrivateChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) { - query := fmt.Sprintf("/private?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// GetPublicChannelsForTeam returns a list of public channels based on the provided team id string. -func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// GetDeletedChannelsForTeam returns a list of public channels based on the provided team id string. -func (c *Client4) GetDeletedChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) { - query := fmt.Sprintf("/deleted?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// GetPublicChannelsByIdsForTeam returns a list of public channels based on provided team id string. -func (c *Client4) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) ([]*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/ids", ArrayToJson(channelIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// GetChannelsForTeamForUser returns a list channels of on a team for a user. -func (c *Client4) GetChannelsForTeamForUser(teamId, userId string, includeDeleted bool, etag string) ([]*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelsForTeamForUserRoute(teamId, userId, includeDeleted), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// GetChannelsForTeamAndUserWithLastDeleteAt returns a list channels of a team for a user, additionally filtered with lastDeleteAt. This does not have any effect if includeDeleted is set to false. -func (c *Client4) GetChannelsForTeamAndUserWithLastDeleteAt(teamId, userId string, includeDeleted bool, lastDeleteAt int, etag string) ([]*Channel, *Response) { - route := fmt.Sprintf(c.GetUserRoute(userId) + c.GetTeamRoute(teamId) + "/channels") - route += fmt.Sprintf("?include_deleted=%v&last_delete_at=%d", includeDeleted, lastDeleteAt) - r, err := c.DoApiGet(route, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// SearchChannels returns the channels on a team matching the provided search term. -func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) ([]*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// SearchArchivedChannels returns the archived channels on a team matching the provided search term. -func (c *Client4) SearchArchivedChannels(teamId string, search *ChannelSearch) ([]*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search_archived", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// SearchAllChannels search in all the channels. Must be a system administrator. -func (c *Client4) SearchAllChannels(search *ChannelSearch) (*ChannelListWithTeamData, *Response) { - r, err := c.DoApiPost(c.GetChannelsRoute()+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelListWithTeamDataFromJson(r.Body), BuildResponse(r) -} - -// SearchAllChannelsPaged searches all the channels and returns the results paged with the total count. -func (c *Client4) SearchAllChannelsPaged(search *ChannelSearch) (*ChannelsWithCount, *Response) { - r, err := c.DoApiPost(c.GetChannelsRoute()+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelsWithCountFromJson(r.Body), BuildResponse(r) -} - -// SearchGroupChannels returns the group channels of the user whose members' usernames match the search term. -func (c *Client4) SearchGroupChannels(search *ChannelSearch) ([]*Channel, *Response) { - r, err := c.DoApiPost(c.GetChannelsRoute()+"/group/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelSliceFromJson(r.Body), BuildResponse(r) -} - -// DeleteChannel deletes channel based on the provided channel id string. -func (c *Client4) DeleteChannel(channelId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetChannelRoute(channelId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PermanentDeleteChannel deletes a channel based on the provided channel id string. -func (c *Client4) PermanentDeleteChannel(channelId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetChannelRoute(channelId) + "?permanent=" + c.boolString(true)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// MoveChannel moves the channel to the destination team. -func (c *Client4) MoveChannel(channelId, teamId string, force bool) (*Channel, *Response) { - requestBody := map[string]interface{}{ - "team_id": teamId, - "force": force, - } - r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/move", StringInterfaceToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelByName returns a channel based on the provided channel name and team id strings. -func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelByNameIncludeDeleted returns a channel based on the provided channel name and team id strings. Other then GetChannelByName it will also return deleted channels. -func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted="+c.boolString(true), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings. -func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelByNameForTeamNameIncludeDeleted returns a channel based on the provided channel name and team name strings. Other then GetChannelByNameForTeamName it will also return deleted channels. -func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) { - r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted="+c.boolString(true), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelFromJson(r.Body), BuildResponse(r) -} - -// GetChannelMembers gets a page of channel members. -func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetChannelMembersRoute(channelId)+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMembersFromJson(r.Body), BuildResponse(r) -} - -// GetChannelMembersByIds gets the channel members in a channel for a list of user ids. -func (c *Client4) GetChannelMembersByIds(channelId string, userIds []string) (*ChannelMembers, *Response) { - r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"/ids", ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMembersFromJson(r.Body), BuildResponse(r) -} - -// GetChannelMember gets a channel member. -func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMember, *Response) { - r, err := c.DoApiGet(c.GetChannelMemberRoute(channelId, userId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMemberFromJson(r.Body), BuildResponse(r) -} - -// GetChannelMembersForUser gets all the channel members for a user on a team. -func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*ChannelMembers, *Response) { - r, err := c.DoApiGet(fmt.Sprintf(c.GetUserRoute(userId)+"/teams/%v/channels/members", teamId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMembersFromJson(r.Body), BuildResponse(r) -} - -// ViewChannel performs a view action for a user. Synonymous with switching channels or marking channels as read by a user. -func (c *Client4) ViewChannel(userId string, view *ChannelView) (*ChannelViewResponse, *Response) { - url := fmt.Sprintf(c.GetChannelsRoute()+"/members/%v/view", userId) - r, err := c.DoApiPost(url, view.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelViewResponseFromJson(r.Body), BuildResponse(r) -} - -// GetChannelUnread will return a ChannelUnread object that contains the number of -// unread messages and mentions for a user. -func (c *Client4) GetChannelUnread(channelId, userId string) (*ChannelUnread, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/unread", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelUnreadFromJson(r.Body), BuildResponse(r) -} - -// UpdateChannelRoles will update the roles on a channel for a user. -func (c *Client4) UpdateChannelRoles(channelId, userId, roles string) (bool, *Response) { - requestBody := map[string]string{"roles": roles} - r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/roles", MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateChannelMemberSchemeRoles will update the scheme-derived roles on a channel for a user. -func (c *Client4) UpdateChannelMemberSchemeRoles(channelId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) { - r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/schemeRoles", schemeRoles.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateChannelNotifyProps will update the notification properties on a channel for a user. -func (c *Client4) UpdateChannelNotifyProps(channelId, userId string, props map[string]string) (bool, *Response) { - r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/notify_props", MapToJson(props)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// AddChannelMember adds user to channel and return a channel member. -func (c *Client4) AddChannelMember(channelId, userId string) (*ChannelMember, *Response) { - requestBody := map[string]string{"user_id": userId} - r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMemberFromJson(r.Body), BuildResponse(r) -} - -// AddChannelMemberWithRootId adds user to channel and return a channel member. Post add to channel message has the postRootId. -func (c *Client4) AddChannelMemberWithRootId(channelId, userId, postRootId string) (*ChannelMember, *Response) { - requestBody := map[string]string{"user_id": userId, "post_root_id": postRootId} - r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMemberFromJson(r.Body), BuildResponse(r) -} - -// RemoveUserFromChannel will delete the channel member object for a user, effectively removing the user from a channel. -func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetChannelMemberRoute(channelId, userId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// AutocompleteChannelsForTeam will return an ordered list of channels autocomplete suggestions. -func (c *Client4) AutocompleteChannelsForTeam(teamId, name string) (*ChannelList, *Response) { - query := fmt.Sprintf("?name=%v", name) - r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+"/autocomplete"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelListFromJson(r.Body), BuildResponse(r) -} - -// AutocompleteChannelsForTeamForSearch will return an ordered list of your channels autocomplete suggestions. -func (c *Client4) AutocompleteChannelsForTeamForSearch(teamId, name string) (*ChannelList, *Response) { - query := fmt.Sprintf("?name=%v", name) - r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+"/search_autocomplete"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelListFromJson(r.Body), BuildResponse(r) -} - -// Post Section - -// CreatePost creates a post based on the provided post struct. -func (c *Client4) CreatePost(post *Post) (*Post, *Response) { - r, err := c.DoApiPost(c.GetPostsRoute(), post.ToUnsanitizedJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostFromJson(r.Body), BuildResponse(r) -} - -// CreatePostEphemeral creates a ephemeral post based on the provided post struct which is send to the given user id. -func (c *Client4) CreatePostEphemeral(post *PostEphemeral) (*Post, *Response) { - r, err := c.DoApiPost(c.GetPostsEphemeralRoute(), post.ToUnsanitizedJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostFromJson(r.Body), BuildResponse(r) -} - -// UpdatePost updates a post based on the provided post struct. -func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) { - r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToUnsanitizedJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostFromJson(r.Body), BuildResponse(r) -} - -// PatchPost partially updates a post. Any missing fields are not updated. -func (c *Client4) PatchPost(postId string, patch *PostPatch) (*Post, *Response) { - r, err := c.DoApiPut(c.GetPostRoute(postId)+"/patch", patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostFromJson(r.Body), BuildResponse(r) -} - -// SetPostUnread marks channel where post belongs as unread on the time of the provided post. -func (c *Client4) SetPostUnread(userId string, postId string, collapsedThreadsSupported bool) *Response { - b, _ := json.Marshal(map[string]bool{"collapsed_threads_supported": collapsedThreadsSupported}) - r, err := c.DoApiPost(c.GetUserRoute(userId)+c.GetPostRoute(postId)+"/set_unread", string(b)) - if err != nil { - return BuildErrorResponse(r, err) - } - defer closeBody(r) - return BuildResponse(r) -} - -// PinPost pin a post based on provided post id string. -func (c *Client4) PinPost(postId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetPostRoute(postId)+"/pin", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UnpinPost unpin a post based on provided post id string. -func (c *Client4) UnpinPost(postId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetPostRoute(postId)+"/unpin", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetPost gets a single post. -func (c *Client4) GetPost(postId string, etag string) (*Post, *Response) { - r, err := c.DoApiGet(c.GetPostRoute(postId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostFromJson(r.Body), BuildResponse(r) -} - -// DeletePost deletes a post from the provided post id string. -func (c *Client4) DeletePost(postId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetPostRoute(postId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetPostThread gets a post with all the other posts in the same thread. -func (c *Client4) GetPostThread(postId string, etag string, collapsedThreads bool) (*PostList, *Response) { - url := c.GetPostRoute(postId) + "/thread" - if collapsedThreads { - url += "?collapsedThreads=true" - } - r, err := c.DoApiGet(url, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPostsForChannel gets a page of posts with an array for ordering for a channel. -func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - if collapsedThreads { - query += "&collapsedThreads=true" - } - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetFlaggedPostsForUser returns flagged posts of a user based on user id string. -func (c *Client4) GetFlaggedPostsForUser(userId string, page int, perPage int) (*PostList, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetFlaggedPostsForUserInTeam returns flagged posts in team of a user based on user id string. -func (c *Client4) GetFlaggedPostsForUserInTeam(userId string, teamId string, page int, perPage int) (*PostList, *Response) { - if !IsValidId(teamId) { - return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInTeam", "model.client.get_flagged_posts_in_team.missing_parameter.app_error", nil, "", http.StatusBadRequest)} - } - - query := fmt.Sprintf("?team_id=%v&page=%v&per_page=%v", teamId, page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetFlaggedPostsForUserInChannel returns flagged posts in channel of a user based on user id string. -func (c *Client4) GetFlaggedPostsForUserInChannel(userId string, channelId string, page int, perPage int) (*PostList, *Response) { - if !IsValidId(channelId) { - return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInChannel", "model.client.get_flagged_posts_in_channel.missing_parameter.app_error", nil, "", http.StatusBadRequest)} - } - - query := fmt.Sprintf("?channel_id=%v&page=%v&per_page=%v", channelId, page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPostsSince gets posts created after a specified time as Unix time in milliseconds. -func (c *Client4) GetPostsSince(channelId string, time int64, collapsedThreads bool) (*PostList, *Response) { - query := fmt.Sprintf("?since=%v", time) - if collapsedThreads { - query += "&collapsedThreads=true" - } - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPostsAfter gets a page of posts that were posted after the post provided. -func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId) - if collapsedThreads { - query += "&collapsedThreads=true" - } - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPostsBefore gets a page of posts that were posted before the post provided. -func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string, collapsedThreads bool) (*PostList, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId) - if collapsedThreads { - query += "&collapsedThreads=true" - } - r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// GetPostsAroundLastUnread gets a list of posts around last unread post by a user in a channel. -func (c *Client4) GetPostsAroundLastUnread(userId, channelId string, limitBefore, limitAfter int, collapsedThreads bool) (*PostList, *Response) { - query := fmt.Sprintf("?limit_before=%v&limit_after=%v", limitBefore, limitAfter) - if collapsedThreads { - query += "&collapsedThreads=true" - } - r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/posts/unread"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// SearchFiles returns any posts with matching terms string. -func (c *Client4) SearchFiles(teamId string, terms string, isOrSearch bool) (*FileInfoList, *Response) { - params := SearchParameter{ - Terms: &terms, - IsOrSearch: &isOrSearch, - } - return c.SearchFilesWithParams(teamId, ¶ms) -} - -// SearchFilesWithParams returns any posts with matching terms string. -func (c *Client4) SearchFilesWithParams(teamId string, params *SearchParameter) (*FileInfoList, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/files/search", params.SearchParameterToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return FileInfoListFromJson(r.Body), BuildResponse(r) -} - -// SearchPosts returns any posts with matching terms string. -func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*PostList, *Response) { - params := SearchParameter{ - Terms: &terms, - IsOrSearch: &isOrSearch, - } - return c.SearchPostsWithParams(teamId, ¶ms) -} - -// SearchPostsWithParams returns any posts with matching terms string. -func (c *Client4) SearchPostsWithParams(teamId string, params *SearchParameter) (*PostList, *Response) { - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", params.SearchParameterToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostListFromJson(r.Body), BuildResponse(r) -} - -// SearchPostsWithMatches returns any posts with matching terms string, including. -func (c *Client4) SearchPostsWithMatches(teamId string, terms string, isOrSearch bool) (*PostSearchResults, *Response) { - requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch} - r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", StringInterfaceToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PostSearchResultsFromJson(r.Body), BuildResponse(r) -} - -// DoPostAction performs a post action. -func (c *Client4) DoPostAction(postId, actionId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetPostRoute(postId)+"/actions/"+actionId, "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DoPostActionWithCookie performs a post action with extra arguments -func (c *Client4) DoPostActionWithCookie(postId, actionId, selected, cookieStr string) (bool, *Response) { - var body []byte - if selected != "" || cookieStr != "" { - body, _ = json.Marshal(DoPostActionRequest{ - SelectedOption: selected, - Cookie: cookieStr, - }) - } - r, err := c.DoApiPost(c.GetPostRoute(postId)+"/actions/"+actionId, string(body)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// OpenInteractiveDialog sends a WebSocket event to a user's clients to -// open interactive dialogs, based on the provided trigger ID and other -// provided data. Used with interactive message buttons, menus and -// slash commands. -func (c *Client4) OpenInteractiveDialog(request OpenDialogRequest) (bool, *Response) { - b, _ := json.Marshal(request) - r, err := c.DoApiPost("/actions/dialogs/open", string(b)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// SubmitInteractiveDialog will submit the provided dialog data to the integration -// configured by the URL. Used with the interactive dialogs integration feature. -func (c *Client4) SubmitInteractiveDialog(request SubmitDialogRequest) (*SubmitDialogResponse, *Response) { - b, _ := json.Marshal(request) - r, err := c.DoApiPost("/actions/dialogs/submit", string(b)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - var resp SubmitDialogResponse - json.NewDecoder(r.Body).Decode(&resp) - return &resp, BuildResponse(r) -} - -// UploadFile will upload a file to a channel using a multipart request, to be later attached to a post. -// This method is functionally equivalent to Client4.UploadFileAsRequestBody. -func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormField("channel_id") - if err != nil { - return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, err = io.Copy(part, strings.NewReader(channelId)) - if err != nil { - return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - part, err = writer.CreateFormFile("files", filename) - if err != nil { - return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - _, err = io.Copy(part, bytes.NewBuffer(data)) - if err != nil { - return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - err = writer.Close() - if err != nil { - return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - return c.DoUploadFile(c.GetFilesRoute(), body.Bytes(), writer.FormDataContentType()) -} - -// UploadFileAsRequestBody will upload a file to a channel as the body of a request, to be later attached -// to a post. This method is functionally equivalent to Client4.UploadFile. -func (c *Client4) UploadFileAsRequestBody(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) { - return c.DoUploadFile(c.GetFilesRoute()+fmt.Sprintf("?channel_id=%v&filename=%v", url.QueryEscape(channelId), url.QueryEscape(filename)), data, http.DetectContentType(data)) -} - -// GetFile gets the bytes for a file by id. -func (c *Client4) GetFile(fileId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// DownloadFile gets the bytes for a file by id, optionally adding headers to force the browser to download it. -func (c *Client4) DownloadFile(fileId string, download bool) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("?download=%v", download), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("DownloadFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// GetFileThumbnail gets the bytes for a file by id. -func (c *Client4) GetFileThumbnail(fileId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId)+"/thumbnail", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// DownloadFileThumbnail gets the bytes for a file by id, optionally adding headers to force the browser to download it. -func (c *Client4) DownloadFileThumbnail(fileId string, download bool) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/thumbnail?download=%v", download), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("DownloadFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// GetFileLink gets the public link of a file by id. -func (c *Client4) GetFileLink(fileId string) (string, *Response) { - r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/link", "") - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["link"], BuildResponse(r) -} - -// GetFilePreview gets the bytes for a file by id. -func (c *Client4) GetFilePreview(fileId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId)+"/preview", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// DownloadFilePreview gets the bytes for a file by id. -func (c *Client4) DownloadFilePreview(fileId string, download bool) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/preview?download=%v", download), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("DownloadFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// GetFileInfo gets all the file info objects. -func (c *Client4) GetFileInfo(fileId string) (*FileInfo, *Response) { - r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/info", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return FileInfoFromJson(r.Body), BuildResponse(r) -} - -// GetFileInfosForPost gets all the file info objects attached to a post. -func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo, *Response) { - r, err := c.DoApiGet(c.GetPostRoute(postId)+"/files/info", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return FileInfosFromJson(r.Body), BuildResponse(r) -} - -// General/System Section - -// GenerateSupportPacket downloads the generated support packet -func (c *Client4) GenerateSupportPacket() ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetSystemRoute()+"/support_packet", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_job_result_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above. -func (c *Client4) GetPing() (string, *Response) { - r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", "") - if r != nil && r.StatusCode == 500 { - defer r.Body.Close() - return STATUS_UNHEALTHY, BuildErrorResponse(r, err) - } - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["status"], BuildResponse(r) -} - -// GetPingWithServerStatus will return ok if several basic server health checks -// all pass successfully. -func (c *Client4) GetPingWithServerStatus() (string, *Response) { - r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status="+c.boolString(true), "") - if r != nil && r.StatusCode == 500 { - defer r.Body.Close() - return STATUS_UNHEALTHY, BuildErrorResponse(r, err) - } - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["status"], BuildResponse(r) -} - -// GetPingWithFullServerStatus will return the full status if several basic server -// health checks all pass successfully. -func (c *Client4) GetPingWithFullServerStatus() (map[string]string, *Response) { - r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status="+c.boolString(true), "") - if r != nil && r.StatusCode == 500 { - defer r.Body.Close() - return map[string]string{"status": STATUS_UNHEALTHY}, BuildErrorResponse(r, err) - } - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body), BuildResponse(r) -} - -// TestEmail will attempt to connect to the configured SMTP server. -func (c *Client4) TestEmail(config *Config) (bool, *Response) { - r, err := c.DoApiPost(c.GetTestEmailRoute(), config.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// TestSiteURL will test the validity of a site URL. -func (c *Client4) TestSiteURL(siteURL string) (bool, *Response) { - requestBody := make(map[string]string) - requestBody["site_url"] = siteURL - r, err := c.DoApiPost(c.GetTestSiteURLRoute(), MapToJson(requestBody)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// TestS3Connection will attempt to connect to the AWS S3. -func (c *Client4) TestS3Connection(config *Config) (bool, *Response) { - r, err := c.DoApiPost(c.GetTestS3Route(), config.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetConfig will retrieve the server config with some sanitized items. -func (c *Client4) GetConfig() (*Config, *Response) { - r, err := c.DoApiGet(c.GetConfigRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ConfigFromJson(r.Body), BuildResponse(r) -} - -// ReloadConfig will reload the server configuration. -func (c *Client4) ReloadConfig() (bool, *Response) { - r, err := c.DoApiPost(c.GetConfigRoute()+"/reload", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetOldClientConfig will retrieve the parts of the server configuration needed by the -// client, formatted in the old format. -func (c *Client4) GetOldClientConfig(etag string) (map[string]string, *Response) { - r, err := c.DoApiGet(c.GetConfigRoute()+"/client?format=old", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body), BuildResponse(r) -} - -// GetEnvironmentConfig will retrieve a map mirroring the server configuration where fields -// are set to true if the corresponding config setting is set through an environment variable. -// Settings that haven't been set through environment variables will be missing from the map. -func (c *Client4) GetEnvironmentConfig() (map[string]interface{}, *Response) { - r, err := c.DoApiGet(c.GetConfigRoute()+"/environment", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return StringInterfaceFromJson(r.Body), BuildResponse(r) -} - -// GetOldClientLicense will retrieve the parts of the server license needed by the -// client, formatted in the old format. -func (c *Client4) GetOldClientLicense(etag string) (map[string]string, *Response) { - r, err := c.DoApiGet(c.GetLicenseRoute()+"/client?format=old", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body), BuildResponse(r) -} - -// DatabaseRecycle will recycle the connections. Discard current connection and get new one. -func (c *Client4) DatabaseRecycle() (bool, *Response) { - r, err := c.DoApiPost(c.GetDatabaseRoute()+"/recycle", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// InvalidateCaches will purge the cache and can affect the performance while is cleaning. -func (c *Client4) InvalidateCaches() (bool, *Response) { - r, err := c.DoApiPost(c.GetCacheRoute()+"/invalidate", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateConfig will update the server configuration. -func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) { - r, err := c.DoApiPut(c.GetConfigRoute(), config.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ConfigFromJson(r.Body), BuildResponse(r) -} - -// MigrateConfig will migrate existing config to the new one. -func (c *Client4) MigrateConfig(from, to string) (bool, *Response) { - m := make(map[string]string, 2) - m["from"] = from - m["to"] = to - r, err := c.DoApiPost(c.GetConfigRoute()+"/migrate", MapToJson(m)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return true, BuildResponse(r) -} - -// UploadLicenseFile will add a license file to the system. -func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("license", "test-license.mattermost-license") - if err != nil { - return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err = writer.Close(); err != nil { - return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetLicenseRoute(), bytes.NewReader(body.Bytes())) - if err != nil { - return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetLicenseRoute(), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return CheckStatusOK(rp), BuildResponse(rp) -} - -// RemoveLicenseFile will remove the server license it exists. Note that this will -// disable all enterprise features. -func (c *Client4) RemoveLicenseFile() (bool, *Response) { - r, err := c.DoApiDelete(c.GetLicenseRoute()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetAnalyticsOld will retrieve analytics using the old format. New format is not -// available but the "/analytics" endpoint is reserved for it. The "name" argument is optional -// and defaults to "standard". The "teamId" argument is optional and will limit results -// to a specific team. -func (c *Client4) GetAnalyticsOld(name, teamId string) (AnalyticsRows, *Response) { - query := fmt.Sprintf("?name=%v&team_id=%v", name, teamId) - r, err := c.DoApiGet(c.GetAnalyticsRoute()+"/old"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return AnalyticsRowsFromJson(r.Body), BuildResponse(r) -} - -// Webhooks Section - -// CreateIncomingWebhook creates an incoming webhook for a channel. -func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { - r, err := c.DoApiPost(c.GetIncomingWebhooksRoute(), hook.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return IncomingWebhookFromJson(r.Body), BuildResponse(r) -} - -// UpdateIncomingWebhook updates an incoming webhook for a channel. -func (c *Client4) UpdateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { - r, err := c.DoApiPut(c.GetIncomingWebhookRoute(hook.Id), hook.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return IncomingWebhookFromJson(r.Body), BuildResponse(r) -} - -// GetIncomingWebhooks returns a page of incoming webhooks on the system. Page counting starts at 0. -func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return IncomingWebhookListFromJson(r.Body), BuildResponse(r) -} - -// GetIncomingWebhooksForTeam returns a page of incoming webhooks for a team. Page counting starts at 0. -func (c *Client4) GetIncomingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) - r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return IncomingWebhookListFromJson(r.Body), BuildResponse(r) -} - -// GetIncomingWebhook returns an Incoming webhook given the hook ID. -func (c *Client4) GetIncomingWebhook(hookID string, etag string) (*IncomingWebhook, *Response) { - r, err := c.DoApiGet(c.GetIncomingWebhookRoute(hookID), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return IncomingWebhookFromJson(r.Body), BuildResponse(r) -} - -// DeleteIncomingWebhook deletes and Incoming Webhook given the hook ID. -func (c *Client4) DeleteIncomingWebhook(hookID string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetIncomingWebhookRoute(hookID)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// CreateOutgoingWebhook creates an outgoing webhook for a team or channel. -func (c *Client4) CreateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) { - r, err := c.DoApiPost(c.GetOutgoingWebhooksRoute(), hook.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookFromJson(r.Body), BuildResponse(r) -} - -// UpdateOutgoingWebhook creates an outgoing webhook for a team or channel. -func (c *Client4) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) { - r, err := c.DoApiPut(c.GetOutgoingWebhookRoute(hook.Id), hook.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookFromJson(r.Body), BuildResponse(r) -} - -// GetOutgoingWebhooks returns a page of outgoing webhooks on the system. Page counting starts at 0. -func (c *Client4) GetOutgoingWebhooks(page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) -} - -// GetOutgoingWebhook outgoing webhooks on the system requested by Hook Id. -func (c *Client4) GetOutgoingWebhook(hookId string) (*OutgoingWebhook, *Response) { - r, err := c.DoApiGet(c.GetOutgoingWebhookRoute(hookId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookFromJson(r.Body), BuildResponse(r) -} - -// GetOutgoingWebhooksForChannel returns a page of outgoing webhooks for a channel. Page counting starts at 0. -func (c *Client4) GetOutgoingWebhooksForChannel(channelId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&channel_id=%v", page, perPage, channelId) - r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) -} - -// GetOutgoingWebhooksForTeam returns a page of outgoing webhooks for a team. Page counting starts at 0. -func (c *Client4) GetOutgoingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) - r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) -} - -// RegenOutgoingHookToken regenerate the outgoing webhook token. -func (c *Client4) RegenOutgoingHookToken(hookId string) (*OutgoingWebhook, *Response) { - r, err := c.DoApiPost(c.GetOutgoingWebhookRoute(hookId)+"/regen_token", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OutgoingWebhookFromJson(r.Body), BuildResponse(r) -} - -// DeleteOutgoingWebhook delete the outgoing webhook on the system requested by Hook Id. -func (c *Client4) DeleteOutgoingWebhook(hookId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetOutgoingWebhookRoute(hookId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Preferences Section - -// GetPreferences returns the user's preferences. -func (c *Client4) GetPreferences(userId string) (Preferences, *Response) { - r, err := c.DoApiGet(c.GetPreferencesRoute(userId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - preferences, _ := PreferencesFromJson(r.Body) - return preferences, BuildResponse(r) -} - -// UpdatePreferences saves the user's preferences. -func (c *Client4) UpdatePreferences(userId string, preferences *Preferences) (bool, *Response) { - r, err := c.DoApiPut(c.GetPreferencesRoute(userId), preferences.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return true, BuildResponse(r) -} - -// DeletePreferences deletes the user's preferences. -func (c *Client4) DeletePreferences(userId string, preferences *Preferences) (bool, *Response) { - r, err := c.DoApiPost(c.GetPreferencesRoute(userId)+"/delete", preferences.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return true, BuildResponse(r) -} - -// GetPreferencesByCategory returns the user's preferences from the provided category string. -func (c *Client4) GetPreferencesByCategory(userId string, category string) (Preferences, *Response) { - url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s", category) - r, err := c.DoApiGet(url, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - preferences, _ := PreferencesFromJson(r.Body) - return preferences, BuildResponse(r) -} - -// GetPreferenceByCategoryAndName returns the user's preferences from the provided category and preference name string. -func (c *Client4) GetPreferenceByCategoryAndName(userId string, category string, preferenceName string) (*Preference, *Response) { - url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s/name/%v", category, preferenceName) - r, err := c.DoApiGet(url, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PreferenceFromJson(r.Body), BuildResponse(r) -} - -// SAML Section - -// GetSamlMetadata returns metadata for the SAML configuration. -func (c *Client4) GetSamlMetadata() (string, *Response) { - r, err := c.DoApiGet(c.GetSamlRoute()+"/metadata", "") - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - buf := new(bytes.Buffer) - _, _ = buf.ReadFrom(r.Body) - return buf.String(), BuildResponse(r) -} - -func fileToMultipart(data []byte, filename string) ([]byte, *multipart.Writer, error) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("certificate", filename) - if err != nil { - return nil, nil, err - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return nil, nil, err - } - - if err := writer.Close(); err != nil { - return nil, nil, err - } - - return body.Bytes(), writer, nil -} - -// UploadSamlIdpCertificate will upload an IDP certificate for SAML and set the config to use it. -// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk. -func (c *Client4) UploadSamlIdpCertificate(data []byte, filename string) (bool, *Response) { - body, writer, err := fileToMultipart(data, filename) - if err != nil { - return false, &Response{Error: NewAppError("UploadSamlIdpCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/idp", body, writer.FormDataContentType()) - return resp.Error == nil, resp -} - -// UploadSamlPublicCertificate will upload a public certificate for SAML and set the config to use it. -// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk. -func (c *Client4) UploadSamlPublicCertificate(data []byte, filename string) (bool, *Response) { - body, writer, err := fileToMultipart(data, filename) - if err != nil { - return false, &Response{Error: NewAppError("UploadSamlPublicCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/public", body, writer.FormDataContentType()) - return resp.Error == nil, resp -} - -// UploadSamlPrivateCertificate will upload a private key for SAML and set the config to use it. -// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk. -func (c *Client4) UploadSamlPrivateCertificate(data []byte, filename string) (bool, *Response) { - body, writer, err := fileToMultipart(data, filename) - if err != nil { - return false, &Response{Error: NewAppError("UploadSamlPrivateCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/private", body, writer.FormDataContentType()) - return resp.Error == nil, resp -} - -// DeleteSamlIdpCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. -func (c *Client4) DeleteSamlIdpCertificate() (bool, *Response) { - r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/idp") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DeleteSamlPublicCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. -func (c *Client4) DeleteSamlPublicCertificate() (bool, *Response) { - r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/public") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DeleteSamlPrivateCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. -func (c *Client4) DeleteSamlPrivateCertificate() (bool, *Response) { - r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/private") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetSamlCertificateStatus returns metadata for the SAML configuration. -func (c *Client4) GetSamlCertificateStatus() (*SamlCertificateStatus, *Response) { - r, err := c.DoApiGet(c.GetSamlRoute()+"/certificate/status", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SamlCertificateStatusFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetSamlMetadataFromIdp(samlMetadataURL string) (*SamlMetadataResponse, *Response) { - requestBody := make(map[string]string) - requestBody["saml_metadata_url"] = samlMetadataURL - r, err := c.DoApiPost(c.GetSamlRoute()+"/metadatafromidp", MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - - defer closeBody(r) - return SamlMetadataResponseFromJson(r.Body), BuildResponse(r) -} - -// ResetSamlAuthDataToEmail resets the AuthData field of SAML users to their Email. -func (c *Client4) ResetSamlAuthDataToEmail(includeDeleted bool, dryRun bool, userIDs []string) (int64, *Response) { - params := map[string]interface{}{ - "include_deleted": includeDeleted, - "dry_run": dryRun, - "user_ids": userIDs, - } - b, _ := json.Marshal(params) - r, err := c.doApiPostBytes(c.GetSamlRoute()+"/reset_auth_data", b) - if err != nil { - return 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - respBody := map[string]int64{} - jsonErr := json.NewDecoder(r.Body).Decode(&respBody) - if jsonErr != nil { - appErr := NewAppError("Api4.ResetSamlAuthDataToEmail", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return 0, BuildErrorResponse(r, appErr) - } - return respBody["num_affected"], BuildResponse(r) -} - -// Compliance Section - -// CreateComplianceReport creates an incoming webhook for a channel. -func (c *Client4) CreateComplianceReport(report *Compliance) (*Compliance, *Response) { - r, err := c.DoApiPost(c.GetComplianceReportsRoute(), report.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ComplianceFromJson(r.Body), BuildResponse(r) -} - -// GetComplianceReports returns list of compliance reports. -func (c *Client4) GetComplianceReports(page, perPage int) (Compliances, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetComplianceReportsRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CompliancesFromJson(r.Body), BuildResponse(r) -} - -// GetComplianceReport returns a compliance report. -func (c *Client4) GetComplianceReport(reportId string) (*Compliance, *Response) { - r, err := c.DoApiGet(c.GetComplianceReportRoute(reportId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ComplianceFromJson(r.Body), BuildResponse(r) -} - -// DownloadComplianceReport returns a full compliance report as a file. -func (c *Client4) DownloadComplianceReport(reportId string) ([]byte, *Response) { - rq, err := http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportDownloadRoute(reportId), nil) - if err != nil { - return nil, &Response{Error: NewAppError("DownloadComplianceReport", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, &Response{Error: NewAppError("DownloadComplianceReport", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - data, err := ioutil.ReadAll(rp.Body) - if err != nil { - return nil, BuildErrorResponse(rp, NewAppError("DownloadComplianceReport", "model.client.read_file.app_error", nil, err.Error(), rp.StatusCode)) - } - - return data, BuildResponse(rp) -} - -// Cluster Section - -// GetClusterStatus returns the status of all the configured cluster nodes. -func (c *Client4) GetClusterStatus() ([]*ClusterInfo, *Response) { - r, err := c.DoApiGet(c.GetClusterRoute()+"/status", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ClusterInfosFromJson(r.Body), BuildResponse(r) -} - -// LDAP Section - -// SyncLdap will force a sync with the configured LDAP server. -// If includeRemovedMembers is true, then group members who left or were removed from a -// synced team/channel will be re-joined; otherwise, they will be excluded. -func (c *Client4) SyncLdap(includeRemovedMembers bool) (bool, *Response) { - reqBody, _ := json.Marshal(map[string]interface{}{ - "include_removed_members": includeRemovedMembers, - }) - r, err := c.doApiPostBytes(c.GetLdapRoute()+"/sync", reqBody) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// TestLdap will attempt to connect to the configured LDAP server and return OK if configured -// correctly. -func (c *Client4) TestLdap() (bool, *Response) { - r, err := c.DoApiPost(c.GetLdapRoute()+"/test", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetLdapGroups retrieves the immediate child groups of the given parent group. -func (c *Client4) GetLdapGroups() ([]*Group, *Response) { - path := fmt.Sprintf("%s/groups", c.GetLdapRoute()) - - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - responseData := struct { - Count int `json:"count"` - Groups []*Group `json:"groups"` - }{} - if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil { - appErr := NewAppError("Api4.GetLdapGroups", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return nil, BuildErrorResponse(r, appErr) - } - for i := range responseData.Groups { - responseData.Groups[i].DisplayName = *responseData.Groups[i].Name - } - - return responseData.Groups, BuildResponse(r) -} - -// LinkLdapGroup creates or undeletes a Mattermost group and associates it to the given LDAP group DN. -func (c *Client4) LinkLdapGroup(dn string) (*Group, *Response) { - path := fmt.Sprintf("%s/groups/%s/link", c.GetLdapRoute(), dn) - - r, appErr := c.DoApiPost(path, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return GroupFromJson(r.Body), BuildResponse(r) -} - -// UnlinkLdapGroup deletes the Mattermost group associated with the given LDAP group DN. -func (c *Client4) UnlinkLdapGroup(dn string) (*Group, *Response) { - path := fmt.Sprintf("%s/groups/%s/link", c.GetLdapRoute(), dn) - - r, appErr := c.DoApiDelete(path) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return GroupFromJson(r.Body), BuildResponse(r) -} - -// MigrateIdLdap migrates the LDAP enabled users to given attribute -func (c *Client4) MigrateIdLdap(toAttribute string) (bool, *Response) { - r, err := c.DoApiPost(c.GetLdapRoute()+"/migrateid", MapToJson(map[string]string{ - "toAttribute": toAttribute, - })) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetGroupsByChannel retrieves the Mattermost Groups associated with a given channel -func (c *Client4) GetGroupsByChannel(channelId string, opts GroupSearchOpts) ([]*GroupWithSchemeAdmin, int, *Response) { - path := fmt.Sprintf("%s/groups?q=%v&include_member_count=%v&filter_allow_reference=%v", c.GetChannelRoute(channelId), opts.Q, opts.IncludeMemberCount, opts.FilterAllowReference) - if opts.PageOpts != nil { - path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage) - } - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, 0, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - responseData := struct { - Groups []*GroupWithSchemeAdmin `json:"groups"` - Count int `json:"total_group_count"` - }{} - if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil { - appErr := NewAppError("Api4.GetGroupsByChannel", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return nil, 0, BuildErrorResponse(r, appErr) - } - - return responseData.Groups, responseData.Count, BuildResponse(r) -} - -// GetGroupsByTeam retrieves the Mattermost Groups associated with a given team -func (c *Client4) GetGroupsByTeam(teamId string, opts GroupSearchOpts) ([]*GroupWithSchemeAdmin, int, *Response) { - path := fmt.Sprintf("%s/groups?q=%v&include_member_count=%v&filter_allow_reference=%v", c.GetTeamRoute(teamId), opts.Q, opts.IncludeMemberCount, opts.FilterAllowReference) - if opts.PageOpts != nil { - path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage) - } - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, 0, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - responseData := struct { - Groups []*GroupWithSchemeAdmin `json:"groups"` - Count int `json:"total_group_count"` - }{} - if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil { - appErr := NewAppError("Api4.GetGroupsByTeam", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return nil, 0, BuildErrorResponse(r, appErr) - } - - return responseData.Groups, responseData.Count, BuildResponse(r) -} - -// GetGroupsAssociatedToChannelsByTeam retrieves the Mattermost Groups associated with channels in a given team -func (c *Client4) GetGroupsAssociatedToChannelsByTeam(teamId string, opts GroupSearchOpts) (map[string][]*GroupWithSchemeAdmin, *Response) { - path := fmt.Sprintf("%s/groups_by_channels?q=%v&filter_allow_reference=%v", c.GetTeamRoute(teamId), opts.Q, opts.FilterAllowReference) - if opts.PageOpts != nil { - path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage) - } - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - responseData := struct { - GroupsAssociatedToChannels map[string][]*GroupWithSchemeAdmin `json:"groups"` - }{} - if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil { - appErr := NewAppError("Api4.GetGroupsAssociatedToChannelsByTeam", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return nil, BuildErrorResponse(r, appErr) - } - - return responseData.GroupsAssociatedToChannels, BuildResponse(r) -} - -// GetGroups retrieves Mattermost Groups -func (c *Client4) GetGroups(opts GroupSearchOpts) ([]*Group, *Response) { - path := fmt.Sprintf( - "%s?include_member_count=%v¬_associated_to_team=%v¬_associated_to_channel=%v&filter_allow_reference=%v&q=%v&filter_parent_team_permitted=%v", - c.GetGroupsRoute(), - opts.IncludeMemberCount, - opts.NotAssociatedToTeam, - opts.NotAssociatedToChannel, - opts.FilterAllowReference, - opts.Q, - opts.FilterParentTeamPermitted, - ) - if opts.Since > 0 { - path = fmt.Sprintf("%s&since=%v", path, opts.Since) - } - if opts.PageOpts != nil { - path = fmt.Sprintf("%s&page=%v&per_page=%v", path, opts.PageOpts.Page, opts.PageOpts.PerPage) - } - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return GroupsFromJson(r.Body), BuildResponse(r) -} - -// GetGroupsByUserId retrieves Mattermost Groups for a user -func (c *Client4) GetGroupsByUserId(userId string) ([]*Group, *Response) { - path := fmt.Sprintf( - "%s/%v/groups", - c.GetUsersRoute(), - userId, - ) - - r, appErr := c.DoApiGet(path, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupsFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) MigrateAuthToLdap(fromAuthService string, matchField string, force bool) (bool, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/migrate_auth/ldap", StringInterfaceToJson(map[string]interface{}{ - "from": fromAuthService, - "force": force, - "match_field": matchField, - })) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -func (c *Client4) MigrateAuthToSaml(fromAuthService string, usersMap map[string]string, auto bool) (bool, *Response) { - r, err := c.DoApiPost(c.GetUsersRoute()+"/migrate_auth/saml", StringInterfaceToJson(map[string]interface{}{ - "from": fromAuthService, - "auto": auto, - "matches": usersMap, - })) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UploadLdapPublicCertificate will upload a public certificate for LDAP and set the config to use it. -func (c *Client4) UploadLdapPublicCertificate(data []byte) (bool, *Response) { - body, writer, err := fileToMultipart(data, LDAP_PUBLIC_CERTIFICATE_NAME) - if err != nil { - return false, &Response{Error: NewAppError("UploadLdapPublicCertificate", "model.client.upload_ldap_cert.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, resp := c.DoUploadFile(c.GetLdapRoute()+"/certificate/public", body, writer.FormDataContentType()) - return resp.Error == nil, resp -} - -// UploadLdapPrivateCertificate will upload a private key for LDAP and set the config to use it. -func (c *Client4) UploadLdapPrivateCertificate(data []byte) (bool, *Response) { - body, writer, err := fileToMultipart(data, LDAP_PRIVATE_KEY_NAME) - if err != nil { - return false, &Response{Error: NewAppError("UploadLdapPrivateCertificate", "model.client.upload_Ldap_cert.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - _, resp := c.DoUploadFile(c.GetLdapRoute()+"/certificate/private", body, writer.FormDataContentType()) - return resp.Error == nil, resp -} - -// DeleteLdapPublicCertificate deletes the LDAP IDP certificate from the server and updates the config to not use it and disable LDAP. -func (c *Client4) DeleteLdapPublicCertificate() (bool, *Response) { - r, err := c.DoApiDelete(c.GetLdapRoute() + "/certificate/public") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DeleteLDAPPrivateCertificate deletes the LDAP IDP certificate from the server and updates the config to not use it and disable LDAP. -func (c *Client4) DeleteLdapPrivateCertificate() (bool, *Response) { - r, err := c.DoApiDelete(c.GetLdapRoute() + "/certificate/private") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Audits Section - -// GetAudits returns a list of audits for the whole system. -func (c *Client4) GetAudits(page int, perPage int, etag string) (Audits, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet("/audits"+query, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return AuditsFromJson(r.Body), BuildResponse(r) -} - -// Brand Section - -// GetBrandImage retrieves the previously uploaded brand image. -func (c *Client4) GetBrandImage() ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetBrandRoute()+"/image", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - if r.StatusCode >= 300 { - return nil, BuildErrorResponse(r, AppErrorFromJson(r.Body)) - } - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetBrandImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - - return data, BuildResponse(r) -} - -// DeleteBrandImage deletes the brand image for the system. -func (c *Client4) DeleteBrandImage() *Response { - r, err := c.DoApiDelete(c.GetBrandRoute() + "/image") - if err != nil { - return BuildErrorResponse(r, err) - } - return BuildResponse(r) -} - -// UploadBrandImage sets the brand image for the system. -func (c *Client4) UploadBrandImage(data []byte) (bool, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("image", "brand.png") - if err != nil { - return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { - return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - if err = writer.Close(); err != nil { - return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetBrandRoute()+"/image", bytes.NewReader(body.Bytes())) - if err != nil { - return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetBrandRoute()+"/image", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return CheckStatusOK(rp), BuildResponse(rp) -} - -// Logs Section - -// GetLogs page of logs as a string array. -func (c *Client4) GetLogs(page, perPage int) ([]string, *Response) { - query := fmt.Sprintf("?page=%v&logs_per_page=%v", page, perPage) - r, err := c.DoApiGet("/logs"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -// PostLog is a convenience Web Service call so clients can log messages into -// the server-side logs. For example we typically log javascript error messages -// into the server-side. It returns the log message if the logging was successful. -func (c *Client4) PostLog(message map[string]string) (map[string]string, *Response) { - r, err := c.DoApiPost("/logs", MapToJson(message)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body), BuildResponse(r) -} - -// OAuth Section - -// CreateOAuthApp will register a new OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. -func (c *Client4) CreateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) { - r, err := c.DoApiPost(c.GetOAuthAppsRoute(), app.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppFromJson(r.Body), BuildResponse(r) -} - -// UpdateOAuthApp updates a page of registered OAuth 2.0 client applications with Mattermost acting as an OAuth 2.0 service provider. -func (c *Client4) UpdateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) { - r, err := c.DoApiPut(c.GetOAuthAppRoute(app.Id), app.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppFromJson(r.Body), BuildResponse(r) -} - -// GetOAuthApps gets a page of registered OAuth 2.0 client applications with Mattermost acting as an OAuth 2.0 service provider. -func (c *Client4) GetOAuthApps(page, perPage int) ([]*OAuthApp, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetOAuthAppsRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppListFromJson(r.Body), BuildResponse(r) -} - -// GetOAuthApp gets a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. -func (c *Client4) GetOAuthApp(appId string) (*OAuthApp, *Response) { - r, err := c.DoApiGet(c.GetOAuthAppRoute(appId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppFromJson(r.Body), BuildResponse(r) -} - -// GetOAuthAppInfo gets a sanitized version of a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. -func (c *Client4) GetOAuthAppInfo(appId string) (*OAuthApp, *Response) { - r, err := c.DoApiGet(c.GetOAuthAppRoute(appId)+"/info", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppFromJson(r.Body), BuildResponse(r) -} - -// DeleteOAuthApp deletes a registered OAuth 2.0 client application. -func (c *Client4) DeleteOAuthApp(appId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetOAuthAppRoute(appId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// RegenerateOAuthAppSecret regenerates the client secret for a registered OAuth 2.0 client application. -func (c *Client4) RegenerateOAuthAppSecret(appId string) (*OAuthApp, *Response) { - r, err := c.DoApiPost(c.GetOAuthAppRoute(appId)+"/regen_secret", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppFromJson(r.Body), BuildResponse(r) -} - -// GetAuthorizedOAuthAppsForUser gets a page of OAuth 2.0 client applications the user has authorized to use access their account. -func (c *Client4) GetAuthorizedOAuthAppsForUser(userId string, page, perPage int) ([]*OAuthApp, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/oauth/apps/authorized"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return OAuthAppListFromJson(r.Body), BuildResponse(r) -} - -// AuthorizeOAuthApp will authorize an OAuth 2.0 client application to access a user's account and provide a redirect link to follow. -func (c *Client4) AuthorizeOAuthApp(authRequest *AuthorizeRequest) (string, *Response) { - r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/authorize", authRequest.ToJson(), "") - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["redirect"], BuildResponse(r) -} - -// DeauthorizeOAuthApp will deauthorize an OAuth 2.0 client application from accessing a user's account. -func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) { - requestData := map[string]string{"client_id": appId} - r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/deauthorize", MapToJson(requestData), "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetOAuthAccessToken is a test helper function for the OAuth access token endpoint. -func (c *Client4) GetOAuthAccessToken(data url.Values) (*AccessResponse, *Response) { - rq, err := http.NewRequest(http.MethodPost, c.Url+"/oauth/access_token", strings.NewReader(data.Encode())) - if err != nil { - return nil, &Response{Error: NewAppError(c.Url+"/oauth/access_token", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.Url+"/oauth/access_token", "model.client.connecting.app_error", nil, err.Error(), 403)} - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return AccessResponseFromJson(rp.Body), BuildResponse(rp) -} - -// Elasticsearch Section - -// TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured. -// correctly. -func (c *Client4) TestElasticsearch() (bool, *Response) { - r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/test", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PurgeElasticsearchIndexes immediately deletes all Elasticsearch indexes. -func (c *Client4) PurgeElasticsearchIndexes() (bool, *Response) { - r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/purge_indexes", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Bleve Section - -// PurgeBleveIndexes immediately deletes all Bleve indexes. -func (c *Client4) PurgeBleveIndexes() (bool, *Response) { - r, err := c.DoApiPost(c.GetBleveRoute()+"/purge_indexes", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// Data Retention Section - -// GetDataRetentionPolicy will get the current global data retention policy details. -func (c *Client4) GetDataRetentionPolicy() (*GlobalRetentionPolicy, *Response) { - r, err := c.DoApiGet(c.GetDataRetentionRoute()+"/policy", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return GlobalRetentionPolicyFromJson(r.Body), BuildResponse(r) -} - -// GetDataRetentionPolicyByID will get the details for the granular data retention policy with the specified ID. -func (c *Client4) GetDataRetentionPolicyByID(policyID string) (*RetentionPolicyWithTeamAndChannelCounts, *Response) { - r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - policy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPolicyByID", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return policy, BuildResponse(r) -} - -// GetDataRetentionPoliciesCount will get the total number of granular data retention policies. -func (c *Client4) GetDataRetentionPoliciesCount() (int64, *Response) { - type CountBody struct { - TotalCount int64 `json:"total_count"` - } - r, appErr := c.DoApiGet(c.GetDataRetentionRoute()+"/policies_count", "") - if appErr != nil { - return 0, BuildErrorResponse(r, appErr) - } - var countObj CountBody - jsonErr := json.NewDecoder(r.Body).Decode(&countObj) - if jsonErr != nil { - return 0, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPoliciesCount", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return countObj.TotalCount, BuildResponse(r) -} - -// GetDataRetentionPolicies will get the current granular data retention policies' details. -func (c *Client4) GetDataRetentionPolicies(page, perPage int) (*RetentionPolicyWithTeamAndChannelCountsList, *Response) { - query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage) - r, appErr := c.DoApiGet(c.GetDataRetentionRoute()+"/policies"+query, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - policies, err := RetentionPolicyWithTeamAndChannelCountsListFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetDataRetentionPolicies", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return policies, BuildResponse(r) -} - -// CreateDataRetentionPolicy will create a new granular data retention policy which will be applied to -// the specified teams and channels. The Id field of `policy` must be empty. -func (c *Client4) CreateDataRetentionPolicy(policy *RetentionPolicyWithTeamAndChannelIDs) (*RetentionPolicyWithTeamAndChannelCounts, *Response) { - r, appErr := c.doApiPostBytes(c.GetDataRetentionRoute()+"/policies", policy.ToJson()) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - newPolicy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.CreateDataRetentionPolicy", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return newPolicy, BuildResponse(r) -} - -// DeleteDataRetentionPolicy will delete the granular data retention policy with the specified ID. -func (c *Client4) DeleteDataRetentionPolicy(policyID string) *Response { - r, appErr := c.DoApiDelete(c.GetDataRetentionPolicyRoute(policyID)) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -// PatchDataRetentionPolicy will patch the granular data retention policy with the specified ID. -// The Id field of `patch` must be non-empty. -func (c *Client4) PatchDataRetentionPolicy(patch *RetentionPolicyWithTeamAndChannelIDs) (*RetentionPolicyWithTeamAndChannelCounts, *Response) { - r, appErr := c.doApiPatchBytes(c.GetDataRetentionPolicyRoute(patch.ID), patch.ToJson()) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - policy, err := RetentionPolicyWithTeamAndChannelCountsFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.PatchDataRetentionPolicy", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return policy, BuildResponse(r) -} - -// GetTeamsForRetentionPolicy will get the teams to which the specified policy is currently applied. -func (c *Client4) GetTeamsForRetentionPolicy(policyID string, page, perPage int) (*TeamsWithCount, *Response) { - query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage) - r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID)+"/teams"+query, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var teams *TeamsWithCount - jsonErr := json.NewDecoder(r.Body).Decode(&teams) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetTeamsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return teams, BuildResponse(r) -} - -// SearchTeamsForRetentionPolicy will search the teams to which the specified policy is currently applied. -func (c *Client4) SearchTeamsForRetentionPolicy(policyID string, term string) ([]*Team, *Response) { - body, _ := json.Marshal(map[string]interface{}{"term": term}) - r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams/search", body) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var teams []*Team - jsonErr := json.NewDecoder(r.Body).Decode(&teams) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.SearchTeamsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return teams, BuildResponse(r) -} - -// AddTeamsToRetentionPolicy will add the specified teams to the granular data retention policy -// with the specified ID. -func (c *Client4) AddTeamsToRetentionPolicy(policyID string, teamIDs []string) *Response { - body, _ := json.Marshal(teamIDs) - r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams", body) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -// RemoveTeamsFromRetentionPolicy will remove the specified teams from the granular data retention policy -// with the specified ID. -func (c *Client4) RemoveTeamsFromRetentionPolicy(policyID string, teamIDs []string) *Response { - body, _ := json.Marshal(teamIDs) - r, appErr := c.doApiDeleteBytes(c.GetDataRetentionPolicyRoute(policyID)+"/teams", body) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -// GetChannelsForRetentionPolicy will get the channels to which the specified policy is currently applied. -func (c *Client4) GetChannelsForRetentionPolicy(policyID string, page, perPage int) (*ChannelsWithCount, *Response) { - query := fmt.Sprintf("?page=%d&per_page=%d", page, perPage) - r, appErr := c.DoApiGet(c.GetDataRetentionPolicyRoute(policyID)+"/channels"+query, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var channels *ChannelsWithCount - jsonErr := json.NewDecoder(r.Body).Decode(&channels) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetChannelsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return channels, BuildResponse(r) -} - -// SearchChannelsForRetentionPolicy will search the channels to which the specified policy is currently applied. -func (c *Client4) SearchChannelsForRetentionPolicy(policyID string, term string) (ChannelListWithTeamData, *Response) { - body, _ := json.Marshal(map[string]interface{}{"term": term}) - r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels/search", body) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var channels ChannelListWithTeamData - jsonErr := json.NewDecoder(r.Body).Decode(&channels) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.SearchChannelsForRetentionPolicy", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return channels, BuildResponse(r) -} - -// AddChannelsToRetentionPolicy will add the specified channels to the granular data retention policy -// with the specified ID. -func (c *Client4) AddChannelsToRetentionPolicy(policyID string, channelIDs []string) *Response { - body, _ := json.Marshal(channelIDs) - r, appErr := c.doApiPostBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels", body) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -// RemoveChannelsFromRetentionPolicy will remove the specified channels from the granular data retention policy -// with the specified ID. -func (c *Client4) RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *Response { - body, _ := json.Marshal(channelIDs) - r, appErr := c.doApiDeleteBytes(c.GetDataRetentionPolicyRoute(policyID)+"/channels", body) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -// GetTeamPoliciesForUser will get the data retention policies for the teams to which a user belongs. -func (c *Client4) GetTeamPoliciesForUser(userID string, offset, limit int) (*RetentionPolicyForTeamList, *Response) { - r, appErr := c.DoApiGet(c.GetUserRoute(userID)+"/data_retention/team_policies", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var teams RetentionPolicyForTeamList - jsonErr := json.NewDecoder(r.Body).Decode(&teams) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetTeamPoliciesForUser", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return &teams, BuildResponse(r) -} - -// GetChannelPoliciesForUser will get the data retention policies for the channels to which a user belongs. -func (c *Client4) GetChannelPoliciesForUser(userID string, offset, limit int) (*RetentionPolicyForChannelList, *Response) { - r, appErr := c.DoApiGet(c.GetUserRoute(userID)+"/data_retention/channel_policies", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - var channels RetentionPolicyForChannelList - jsonErr := json.NewDecoder(r.Body).Decode(&channels) - if jsonErr != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetChannelPoliciesForUser", "model.utils.decode_json.app_error", nil, jsonErr.Error(), r.StatusCode)) - } - return &channels, BuildResponse(r) -} - -// Commands Section - -// CreateCommand will create a new command if the user have the right permissions. -func (c *Client4) CreateCommand(cmd *Command) (*Command, *Response) { - r, err := c.DoApiPost(c.GetCommandsRoute(), cmd.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CommandFromJson(r.Body), BuildResponse(r) -} - -// UpdateCommand updates a command based on the provided Command struct. -func (c *Client4) UpdateCommand(cmd *Command) (*Command, *Response) { - r, err := c.DoApiPut(c.GetCommandRoute(cmd.Id), cmd.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CommandFromJson(r.Body), BuildResponse(r) -} - -// MoveCommand moves a command to a different team. -func (c *Client4) MoveCommand(teamId string, commandId string) (bool, *Response) { - cmr := CommandMoveRequest{TeamId: teamId} - r, err := c.DoApiPut(c.GetCommandMoveRoute(commandId), cmr.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DeleteCommand deletes a command based on the provided command id string. -func (c *Client4) DeleteCommand(commandId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetCommandRoute(commandId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// ListCommands will retrieve a list of commands available in the team. -func (c *Client4) ListCommands(teamId string, customOnly bool) ([]*Command, *Response) { - query := fmt.Sprintf("?team_id=%v&custom_only=%v", teamId, customOnly) - r, err := c.DoApiGet(c.GetCommandsRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CommandListFromJson(r.Body), BuildResponse(r) -} - -// ListCommandAutocompleteSuggestions will retrieve a list of suggestions for a userInput. -func (c *Client4) ListCommandAutocompleteSuggestions(userInput, teamId string) ([]AutocompleteSuggestion, *Response) { - query := fmt.Sprintf("/commands/autocomplete_suggestions?user_input=%v", userInput) - r, err := c.DoApiGet(c.GetTeamRoute(teamId)+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return AutocompleteSuggestionsFromJSON(r.Body), BuildResponse(r) -} - -// GetCommandById will retrieve a command by id. -func (c *Client4) GetCommandById(cmdId string) (*Command, *Response) { - url := fmt.Sprintf("%s/%s", c.GetCommandsRoute(), cmdId) - r, err := c.DoApiGet(url, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CommandFromJson(r.Body), BuildResponse(r) -} - -// ExecuteCommand executes a given slash command. -func (c *Client4) ExecuteCommand(channelId, command string) (*CommandResponse, *Response) { - commandArgs := &CommandArgs{ - ChannelId: channelId, - Command: command, - } - r, err := c.DoApiPost(c.GetCommandsRoute()+"/execute", commandArgs.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - response, _ := CommandResponseFromJson(r.Body) - return response, BuildResponse(r) -} - -// ExecuteCommandWithTeam executes a given slash command against the specified team. -// Use this when executing slash commands in a DM/GM, since the team id cannot be inferred in that case. -func (c *Client4) ExecuteCommandWithTeam(channelId, teamId, command string) (*CommandResponse, *Response) { - commandArgs := &CommandArgs{ - ChannelId: channelId, - TeamId: teamId, - Command: command, - } - r, err := c.DoApiPost(c.GetCommandsRoute()+"/execute", commandArgs.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - response, _ := CommandResponseFromJson(r.Body) - return response, BuildResponse(r) -} - -// ListAutocompleteCommands will retrieve a list of commands available in the team. -func (c *Client4) ListAutocompleteCommands(teamId string) ([]*Command, *Response) { - r, err := c.DoApiGet(c.GetTeamAutoCompleteCommandsRoute(teamId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CommandListFromJson(r.Body), BuildResponse(r) -} - -// RegenCommandToken will create a new token if the user have the right permissions. -func (c *Client4) RegenCommandToken(commandId string) (string, *Response) { - r, err := c.DoApiPut(c.GetCommandRoute(commandId)+"/regen_token", "") - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["token"], BuildResponse(r) -} - -// Status Section - -// GetUserStatus returns a user based on the provided user id string. -func (c *Client4) GetUserStatus(userId, etag string) (*Status, *Response) { - r, err := c.DoApiGet(c.GetUserStatusRoute(userId), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return StatusFromJson(r.Body), BuildResponse(r) -} - -// GetUsersStatusesByIds returns a list of users status based on the provided user ids. -func (c *Client4) GetUsersStatusesByIds(userIds []string) ([]*Status, *Response) { - r, err := c.DoApiPost(c.GetUserStatusesRoute()+"/ids", ArrayToJson(userIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return StatusListFromJson(r.Body), BuildResponse(r) -} - -// UpdateUserStatus sets a user's status based on the provided user id string. -func (c *Client4) UpdateUserStatus(userId string, userStatus *Status) (*Status, *Response) { - r, err := c.DoApiPut(c.GetUserStatusRoute(userId), userStatus.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return StatusFromJson(r.Body), BuildResponse(r) -} - -// Emoji Section - -// CreateEmoji will save an emoji to the server if the current user has permission -// to do so. If successful, the provided emoji will be returned with its Id field -// filled in. Otherwise, an error will be returned. -func (c *Client4) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoji, *Response) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - part, err := writer.CreateFormFile("image", filename) - if err != nil { - return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)} - } - - if _, err := io.Copy(part, bytes.NewBuffer(image)); err != nil { - return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)} - } - - if err := writer.WriteField("emoji", emoji.ToJson()); err != nil { - return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.emoji.app_error", nil, err.Error(), 0)} - } - - if err := writer.Close(); err != nil { - return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.writer.app_error", nil, err.Error(), 0)} - } - - return c.DoEmojiUploadFile(c.GetEmojisRoute(), body.Bytes(), writer.FormDataContentType()) -} - -// GetEmojiList returns a page of custom emoji on the system. -func (c *Client4) GetEmojiList(page, perPage int) ([]*Emoji, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) - r, err := c.DoApiGet(c.GetEmojisRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiListFromJson(r.Body), BuildResponse(r) -} - -// GetSortedEmojiList returns a page of custom emoji on the system sorted based on the sort -// parameter, blank for no sorting and "name" to sort by emoji names. -func (c *Client4) GetSortedEmojiList(page, perPage int, sort string) ([]*Emoji, *Response) { - query := fmt.Sprintf("?page=%v&per_page=%v&sort=%v", page, perPage, sort) - r, err := c.DoApiGet(c.GetEmojisRoute()+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiListFromJson(r.Body), BuildResponse(r) -} - -// DeleteEmoji delete an custom emoji on the provided emoji id string. -func (c *Client4) DeleteEmoji(emojiId string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetEmojiRoute(emojiId)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetEmoji returns a custom emoji based on the emojiId string. -func (c *Client4) GetEmoji(emojiId string) (*Emoji, *Response) { - r, err := c.DoApiGet(c.GetEmojiRoute(emojiId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiFromJson(r.Body), BuildResponse(r) -} - -// GetEmojiByName returns a custom emoji based on the name string. -func (c *Client4) GetEmojiByName(name string) (*Emoji, *Response) { - r, err := c.DoApiGet(c.GetEmojiByNameRoute(name), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiFromJson(r.Body), BuildResponse(r) -} - -// GetEmojiImage returns the emoji image. -func (c *Client4) GetEmojiImage(emojiId string) ([]byte, *Response) { - r, apErr := c.DoApiGet(c.GetEmojiRoute(emojiId)+"/image", "") - if apErr != nil { - return nil, BuildErrorResponse(r, apErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetEmojiImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) - } - - return data, BuildResponse(r) -} - -// SearchEmoji returns a list of emoji matching some search criteria. -func (c *Client4) SearchEmoji(search *EmojiSearch) ([]*Emoji, *Response) { - r, err := c.DoApiPost(c.GetEmojisRoute()+"/search", search.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiListFromJson(r.Body), BuildResponse(r) -} - -// AutocompleteEmoji returns a list of emoji starting with or matching name. -func (c *Client4) AutocompleteEmoji(name string, etag string) ([]*Emoji, *Response) { - query := fmt.Sprintf("?name=%v", name) - r, err := c.DoApiGet(c.GetEmojisRoute()+"/autocomplete"+query, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return EmojiListFromJson(r.Body), BuildResponse(r) -} - -// Reaction Section - -// SaveReaction saves an emoji reaction for a post. Returns the saved reaction if successful, otherwise an error will be returned. -func (c *Client4) SaveReaction(reaction *Reaction) (*Reaction, *Response) { - r, err := c.DoApiPost(c.GetReactionsRoute(), reaction.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ReactionFromJson(r.Body), BuildResponse(r) -} - -// GetReactions returns a list of reactions to a post. -func (c *Client4) GetReactions(postId string) ([]*Reaction, *Response) { - r, err := c.DoApiGet(c.GetPostRoute(postId)+"/reactions", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ReactionsFromJson(r.Body), BuildResponse(r) -} - -// DeleteReaction deletes reaction of a user in a post. -func (c *Client4) DeleteReaction(reaction *Reaction) (bool, *Response) { - r, err := c.DoApiDelete(c.GetUserRoute(reaction.UserId) + c.GetPostRoute(reaction.PostId) + fmt.Sprintf("/reactions/%v", reaction.EmojiName)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// FetchBulkReactions returns a map of postIds and corresponding reactions -func (c *Client4) GetBulkReactions(postIds []string) (map[string][]*Reaction, *Response) { - r, err := c.DoApiPost(c.GetPostsRoute()+"/ids/reactions", ArrayToJson(postIds)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapPostIdToReactionsFromJson(r.Body), BuildResponse(r) -} - -// Timezone Section - -// GetSupportedTimezone returns a page of supported timezones on the system. -func (c *Client4) GetSupportedTimezone() ([]string, *Response) { - r, err := c.DoApiGet(c.GetTimezonesRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - var timezones []string - json.NewDecoder(r.Body).Decode(&timezones) - return timezones, BuildResponse(r) -} - -// Open Graph Metadata Section - -// OpenGraph return the open graph metadata for a particular url if the site have the metadata. -func (c *Client4) OpenGraph(url string) (map[string]string, *Response) { - requestBody := make(map[string]string) - requestBody["url"] = url - - r, err := c.DoApiPost(c.GetOpenGraphRoute(), MapToJson(requestBody)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body), BuildResponse(r) -} - -// Jobs Section - -// GetJob gets a single job. -func (c *Client4) GetJob(id string) (*Job, *Response) { - r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/%v", id), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return JobFromJson(r.Body), BuildResponse(r) -} - -// GetJobs gets all jobs, sorted with the job that was created most recently first. -func (c *Client4) GetJobs(page int, perPage int) ([]*Job, *Response) { - r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("?page=%v&per_page=%v", page, perPage), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return JobsFromJson(r.Body), BuildResponse(r) -} - -// GetJobsByType gets all jobs of a given type, sorted with the job that was created most recently first. -func (c *Client4) GetJobsByType(jobType string, page int, perPage int) ([]*Job, *Response) { - r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/type/%v?page=%v&per_page=%v", jobType, page, perPage), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return JobsFromJson(r.Body), BuildResponse(r) -} - -// CreateJob creates a job based on the provided job struct. -func (c *Client4) CreateJob(job *Job) (*Job, *Response) { - r, err := c.DoApiPost(c.GetJobsRoute(), job.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return JobFromJson(r.Body), BuildResponse(r) -} - -// CancelJob requests the cancellation of the job with the provided Id. -func (c *Client4) CancelJob(jobId string) (bool, *Response) { - r, err := c.DoApiPost(c.GetJobsRoute()+fmt.Sprintf("/%v/cancel", jobId), "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DownloadJob downloads the results of the job -func (c *Client4) DownloadJob(jobId string) ([]byte, *Response) { - r, appErr := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/%v/download", jobId), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - data, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_job_result_file.app_error", nil, err.Error(), r.StatusCode)) - } - return data, BuildResponse(r) -} - -// Roles Section - -// GetRole gets a single role by ID. -func (c *Client4) GetRole(id string) (*Role, *Response) { - r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/%v", id), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return RoleFromJson(r.Body), BuildResponse(r) -} - -// GetRoleByName gets a single role by Name. -func (c *Client4) GetRoleByName(name string) (*Role, *Response) { - r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/name/%v", name), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return RoleFromJson(r.Body), BuildResponse(r) -} - -// GetRolesByNames returns a list of roles based on the provided role names. -func (c *Client4) GetRolesByNames(roleNames []string) ([]*Role, *Response) { - r, err := c.DoApiPost(c.GetRolesRoute()+"/names", ArrayToJson(roleNames)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return RoleListFromJson(r.Body), BuildResponse(r) -} - -// PatchRole partially updates a role in the system. Any missing fields are not updated. -func (c *Client4) PatchRole(roleId string, patch *RolePatch) (*Role, *Response) { - r, err := c.DoApiPut(c.GetRolesRoute()+fmt.Sprintf("/%v/patch", roleId), patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return RoleFromJson(r.Body), BuildResponse(r) -} - -// Schemes Section - -// CreateScheme creates a new Scheme. -func (c *Client4) CreateScheme(scheme *Scheme) (*Scheme, *Response) { - r, err := c.DoApiPost(c.GetSchemesRoute(), scheme.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SchemeFromJson(r.Body), BuildResponse(r) -} - -// GetScheme gets a single scheme by ID. -func (c *Client4) GetScheme(id string) (*Scheme, *Response) { - r, err := c.DoApiGet(c.GetSchemeRoute(id), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SchemeFromJson(r.Body), BuildResponse(r) -} - -// GetSchemes gets all schemes, sorted with the most recently created first, optionally filtered by scope. -func (c *Client4) GetSchemes(scope string, page int, perPage int) ([]*Scheme, *Response) { - r, err := c.DoApiGet(c.GetSchemesRoute()+fmt.Sprintf("?scope=%v&page=%v&per_page=%v", scope, page, perPage), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SchemesFromJson(r.Body), BuildResponse(r) -} - -// DeleteScheme deletes a single scheme by ID. -func (c *Client4) DeleteScheme(id string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetSchemeRoute(id)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// PatchScheme partially updates a scheme in the system. Any missing fields are not updated. -func (c *Client4) PatchScheme(id string, patch *SchemePatch) (*Scheme, *Response) { - r, err := c.DoApiPut(c.GetSchemeRoute(id)+"/patch", patch.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return SchemeFromJson(r.Body), BuildResponse(r) -} - -// GetTeamsForScheme gets the teams using this scheme, sorted alphabetically by display name. -func (c *Client4) GetTeamsForScheme(schemeId string, page int, perPage int) ([]*Team, *Response) { - r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/teams?page=%v&per_page=%v", page, perPage), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TeamListFromJson(r.Body), BuildResponse(r) -} - -// GetChannelsForScheme gets the channels using this scheme, sorted alphabetically by display name. -func (c *Client4) GetChannelsForScheme(schemeId string, page int, perPage int) (ChannelList, *Response) { - r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/channels?page=%v&per_page=%v", page, perPage), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return *ChannelListFromJson(r.Body), BuildResponse(r) -} - -// Plugin Section - -// UploadPlugin takes an io.Reader stream pointing to the contents of a .tar.gz plugin. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) UploadPlugin(file io.Reader) (*Manifest, *Response) { - return c.uploadPlugin(file, false) -} - -func (c *Client4) UploadPluginForced(file io.Reader) (*Manifest, *Response) { - return c.uploadPlugin(file, true) -} - -func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response) { - body := new(bytes.Buffer) - writer := multipart.NewWriter(body) - - if force { - err := writer.WriteField("force", c.boolString(true)) - if err != nil { - return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)} - } - } - - part, err := writer.CreateFormFile("plugin", "plugin.tar.gz") - if err != nil { - return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)} - } - - if _, err = io.Copy(part, file); err != nil { - return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)} - } - - if err = writer.Close(); err != nil { - return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)} - } - - rq, err := http.NewRequest("POST", c.ApiUrl+c.GetPluginsRoute(), body) - if err != nil { - return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} - } - rq.Header.Set("Content-Type", writer.FormDataContentType()) - - if c.AuthToken != "" { - rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) - } - - rp, err := c.HttpClient.Do(rq) - if err != nil || rp == nil { - return nil, BuildErrorResponse(rp, NewAppError("UploadPlugin", "model.client.connecting.app_error", nil, err.Error(), 0)) - } - defer closeBody(rp) - - if rp.StatusCode >= 300 { - return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) - } - - return ManifestFromJson(rp.Body), BuildResponse(rp) -} - -func (c *Client4) InstallPluginFromUrl(downloadUrl string, force bool) (*Manifest, *Response) { - forceStr := c.boolString(force) - - url := fmt.Sprintf("%s?plugin_download_url=%s&force=%s", c.GetPluginsRoute()+"/install_from_url", url.QueryEscape(downloadUrl), forceStr) - r, err := c.DoApiPost(url, "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ManifestFromJson(r.Body), BuildResponse(r) -} - -// InstallMarketplacePlugin will install marketplace plugin. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) InstallMarketplacePlugin(request *InstallMarketplacePluginRequest) (*Manifest, *Response) { - json, err := request.ToJson() - if err != nil { - return nil, &Response{Error: NewAppError("InstallMarketplacePlugin", "model.client.plugin_request_to_json.app_error", nil, err.Error(), http.StatusBadRequest)} - } - r, appErr := c.DoApiPost(c.GetPluginsRoute()+"/marketplace", json) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return ManifestFromJson(r.Body), BuildResponse(r) -} - -// GetPlugins will return a list of plugin manifests for currently active plugins. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) GetPlugins() (*PluginsResponse, *Response) { - r, err := c.DoApiGet(c.GetPluginsRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PluginsResponseFromJson(r.Body), BuildResponse(r) -} - -// GetPluginStatuses will return the plugins installed on any server in the cluster, for reporting -// to the administrator via the system console. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) GetPluginStatuses() (PluginStatuses, *Response) { - r, err := c.DoApiGet(c.GetPluginsRoute()+"/statuses", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return PluginStatusesFromJson(r.Body), BuildResponse(r) -} - -// RemovePlugin will disable and delete a plugin. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) RemovePlugin(id string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetPluginRoute(id)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetWebappPlugins will return a list of plugins that the webapp should download. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) GetWebappPlugins() ([]*Manifest, *Response) { - r, err := c.DoApiGet(c.GetPluginsRoute()+"/webapp", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ManifestListFromJson(r.Body), BuildResponse(r) -} - -// EnablePlugin will enable an plugin installed. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) EnablePlugin(id string) (bool, *Response) { - r, err := c.DoApiPost(c.GetPluginRoute(id)+"/enable", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// DisablePlugin will disable an enabled plugin. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) DisablePlugin(id string) (bool, *Response) { - r, err := c.DoApiPost(c.GetPluginRoute(id)+"/disable", "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetMarketplacePlugins will return a list of plugins that an admin can install. -// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE. -func (c *Client4) GetMarketplacePlugins(filter *MarketplacePluginFilter) ([]*MarketplacePlugin, *Response) { - route := c.GetPluginsRoute() + "/marketplace" - u, parseErr := url.Parse(route) - if parseErr != nil { - return nil, &Response{Error: NewAppError("GetMarketplacePlugins", "model.client.parse_plugins.app_error", nil, parseErr.Error(), http.StatusBadRequest)} - } - - filter.ApplyToURL(u) - - r, err := c.DoApiGet(u.String(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - plugins, readerErr := MarketplacePluginsFromReader(r.Body) - if readerErr != nil { - return nil, BuildErrorResponse(r, NewAppError(route, "model.client.parse_plugins.app_error", nil, err.Error(), http.StatusBadRequest)) - } - - return plugins, BuildResponse(r) -} - -// UpdateChannelScheme will update a channel's scheme. -func (c *Client4) UpdateChannelScheme(channelId, schemeId string) (bool, *Response) { - sip := &SchemeIDPatch{SchemeID: &schemeId} - r, err := c.DoApiPut(c.GetChannelSchemeRoute(channelId), sip.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// UpdateTeamScheme will update a team's scheme. -func (c *Client4) UpdateTeamScheme(teamId, schemeId string) (bool, *Response) { - sip := &SchemeIDPatch{SchemeID: &schemeId} - r, err := c.DoApiPut(c.GetTeamSchemeRoute(teamId), sip.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetRedirectLocation retrieves the value of the 'Location' header of an HTTP response for a given URL. -func (c *Client4) GetRedirectLocation(urlParam, etag string) (string, *Response) { - url := fmt.Sprintf("%s?url=%s", c.GetRedirectLocationRoute(), url.QueryEscape(urlParam)) - r, err := c.DoApiGet(url, etag) - if err != nil { - return "", BuildErrorResponse(r, err) - } - defer closeBody(r) - return MapFromJson(r.Body)["location"], BuildResponse(r) -} - -// SetServerBusy will mark the server as busy, which disables non-critical services for `secs` seconds. -func (c *Client4) SetServerBusy(secs int) (bool, *Response) { - url := fmt.Sprintf("%s?seconds=%d", c.GetServerBusyRoute(), secs) - r, err := c.DoApiPost(url, "") - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// ClearServerBusy will mark the server as not busy. -func (c *Client4) ClearServerBusy() (bool, *Response) { - r, err := c.DoApiDelete(c.GetServerBusyRoute()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetServerBusy returns the current ServerBusyState including the time when a server marked busy -// will automatically have the flag cleared. -func (c *Client4) GetServerBusy() (*ServerBusyState, *Response) { - r, err := c.DoApiGet(c.GetServerBusyRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - sbs := ServerBusyStateFromJson(r.Body) - return sbs, BuildResponse(r) -} - -// GetServerBusyExpires returns the time when a server marked busy -// will automatically have the flag cleared. -// -// Deprecated: Use GetServerBusy instead. -func (c *Client4) GetServerBusyExpires() (*time.Time, *Response) { - r, err := c.DoApiGet(c.GetServerBusyRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - sbs := ServerBusyStateFromJson(r.Body) - expires := time.Unix(sbs.Expires, 0) - return &expires, BuildResponse(r) -} - -// RegisterTermsOfServiceAction saves action performed by a user against a specific terms of service. -func (c *Client4) RegisterTermsOfServiceAction(userId, termsOfServiceId string, accepted bool) (*bool, *Response) { - url := c.GetUserTermsOfServiceRoute(userId) - data := map[string]interface{}{"termsOfServiceId": termsOfServiceId, "accepted": accepted} - r, err := c.DoApiPost(url, StringInterfaceToJson(data)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return NewBool(CheckStatusOK(r)), BuildResponse(r) -} - -// GetTermsOfService fetches the latest terms of service -func (c *Client4) GetTermsOfService(etag string) (*TermsOfService, *Response) { - url := c.GetTermsOfServiceRoute() - r, err := c.DoApiGet(url, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TermsOfServiceFromJson(r.Body), BuildResponse(r) -} - -// GetUserTermsOfService fetches user's latest terms of service action if the latest action was for acceptance. -func (c *Client4) GetUserTermsOfService(userId, etag string) (*UserTermsOfService, *Response) { - url := c.GetUserTermsOfServiceRoute(userId) - r, err := c.DoApiGet(url, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UserTermsOfServiceFromJson(r.Body), BuildResponse(r) -} - -// CreateTermsOfService creates new terms of service. -func (c *Client4) CreateTermsOfService(text, userId string) (*TermsOfService, *Response) { - url := c.GetTermsOfServiceRoute() - data := map[string]interface{}{"text": text} - r, err := c.DoApiPost(url, StringInterfaceToJson(data)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return TermsOfServiceFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetGroup(groupID, etag string) (*Group, *Response) { - r, appErr := c.DoApiGet(c.GetGroupRoute(groupID), etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) PatchGroup(groupID string, patch *GroupPatch) (*Group, *Response) { - payload, _ := json.Marshal(patch) - r, appErr := c.DoApiPut(c.GetGroupRoute(groupID)+"/patch", string(payload)) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) LinkGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, patch *GroupSyncablePatch) (*GroupSyncable, *Response) { - payload, _ := json.Marshal(patch) - url := fmt.Sprintf("%s/link", c.GetGroupSyncableRoute(groupID, syncableID, syncableType)) - r, appErr := c.DoApiPost(url, string(payload)) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupSyncableFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) UnlinkGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType) *Response { - url := fmt.Sprintf("%s/link", c.GetGroupSyncableRoute(groupID, syncableID, syncableType)) - r, appErr := c.DoApiDelete(url) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return BuildResponse(r) -} - -func (c *Client4) GetGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, etag string) (*GroupSyncable, *Response) { - r, appErr := c.DoApiGet(c.GetGroupSyncableRoute(groupID, syncableID, syncableType), etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupSyncableFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetGroupSyncables(groupID string, syncableType GroupSyncableType, etag string) ([]*GroupSyncable, *Response) { - r, appErr := c.DoApiGet(c.GetGroupSyncablesRoute(groupID, syncableType), etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupSyncablesFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) PatchGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, patch *GroupSyncablePatch) (*GroupSyncable, *Response) { - payload, _ := json.Marshal(patch) - r, appErr := c.DoApiPut(c.GetGroupSyncableRoute(groupID, syncableID, syncableType)+"/patch", string(payload)) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupSyncableFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int, etag string) ([]*UserWithGroups, int64, *Response) { - groupIDStr := strings.Join(groupIDs, ",") - query := fmt.Sprintf("?group_ids=%s&page=%d&per_page=%d", groupIDStr, page, perPage) - r, err := c.DoApiGet(c.GetTeamRoute(teamID)+"/members_minus_group_members"+query, etag) - if err != nil { - return nil, 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - ugc := UsersWithGroupsAndCountFromJson(r.Body) - return ugc.Users, ugc.Count, BuildResponse(r) -} - -func (c *Client4) ChannelMembersMinusGroupMembers(channelID string, groupIDs []string, page, perPage int, etag string) ([]*UserWithGroups, int64, *Response) { - groupIDStr := strings.Join(groupIDs, ",") - query := fmt.Sprintf("?group_ids=%s&page=%d&per_page=%d", groupIDStr, page, perPage) - r, err := c.DoApiGet(c.GetChannelRoute(channelID)+"/members_minus_group_members"+query, etag) - if err != nil { - return nil, 0, BuildErrorResponse(r, err) - } - defer closeBody(r) - ugc := UsersWithGroupsAndCountFromJson(r.Body) - return ugc.Users, ugc.Count, BuildResponse(r) -} - -func (c *Client4) PatchConfig(config *Config) (*Config, *Response) { - r, err := c.DoApiPut(c.GetConfigRoute()+"/patch", config.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ConfigFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetChannelModerations(channelID string, etag string) ([]*ChannelModeration, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelID)+"/moderations", etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelModerationsFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) PatchChannelModerations(channelID string, patch []*ChannelModerationPatch) ([]*ChannelModeration, *Response) { - payload, _ := json.Marshal(patch) - r, err := c.DoApiPut(c.GetChannelRoute(channelID)+"/moderations/patch", string(payload)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelModerationsFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetKnownUsers() ([]string, *Response) { - r, err := c.DoApiGet(c.GetUsersRoute()+"/known", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - var userIds []string - json.NewDecoder(r.Body).Decode(&userIds) - return userIds, BuildResponse(r) -} - -// PublishUserTyping publishes a user is typing websocket event based on the provided TypingRequest. -func (c *Client4) PublishUserTyping(userID string, typingRequest TypingRequest) (bool, *Response) { - r, err := c.DoApiPost(c.GetPublishUserTypingRoute(userID), typingRequest.ToJson()) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -func (c *Client4) GetChannelMemberCountsByGroup(channelID string, includeTimezones bool, etag string) ([]*ChannelMemberCountByGroup, *Response) { - r, err := c.DoApiGet(c.GetChannelRoute(channelID)+"/member_counts_by_group?include_timezones="+strconv.FormatBool(includeTimezones), etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ChannelMemberCountsByGroupFromJson(r.Body), BuildResponse(r) -} - -// RequestTrialLicense will request a trial license and install it in the server -func (c *Client4) RequestTrialLicense(users int) (bool, *Response) { - b, _ := json.Marshal(map[string]interface{}{"users": users, "terms_accepted": true}) - r, err := c.DoApiPost("/trial-license", string(b)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -// GetGroupStats retrieves stats for a Mattermost Group -func (c *Client4) GetGroupStats(groupID string) (*GroupStats, *Response) { - r, appErr := c.DoApiGet(c.GetGroupRoute(groupID)+"/stats", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - return GroupStatsFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetSidebarCategoriesForTeamForUser(userID, teamID, etag string) (*OrderedSidebarCategories, *Response) { - route := c.GetUserCategoryRoute(userID, teamID) - r, appErr := c.DoApiGet(route, etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - cat, err := OrderedSidebarCategoriesFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.GetSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return cat, BuildResponse(r) -} - -func (c *Client4) CreateSidebarCategoryForTeamForUser(userID, teamID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) { - payload, _ := json.Marshal(category) - route := c.GetUserCategoryRoute(userID, teamID) - r, appErr := c.doApiPostBytes(route, payload) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - cat, err := SidebarCategoryFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.CreateSidebarCategoryForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - return cat, BuildResponse(r) -} - -func (c *Client4) UpdateSidebarCategoriesForTeamForUser(userID, teamID string, categories []*SidebarCategoryWithChannels) ([]*SidebarCategoryWithChannels, *Response) { - payload, _ := json.Marshal(categories) - route := c.GetUserCategoryRoute(userID, teamID) - - r, appErr := c.doApiPutBytes(route, payload) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - categories, err := SidebarCategoriesFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - - return categories, BuildResponse(r) -} - -func (c *Client4) GetSidebarCategoryOrderForTeamForUser(userID, teamID, etag string) ([]string, *Response) { - route := c.GetUserCategoryRoute(userID, teamID) + "/order" - r, err := c.DoApiGet(route, etag) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) UpdateSidebarCategoryOrderForTeamForUser(userID, teamID string, order []string) ([]string, *Response) { - payload, _ := json.Marshal(order) - route := c.GetUserCategoryRoute(userID, teamID) + "/order" - r, err := c.doApiPutBytes(route, payload) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) GetSidebarCategoryForTeamForUser(userID, teamID, categoryID, etag string) (*SidebarCategoryWithChannels, *Response) { - route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID - r, appErr := c.DoApiGet(route, etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - cat, err := SidebarCategoryFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - - return cat, BuildResponse(r) -} - -func (c *Client4) UpdateSidebarCategoryForTeamForUser(userID, teamID, categoryID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) { - payload, _ := json.Marshal(category) - route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID - r, appErr := c.doApiPutBytes(route, payload) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - cat, err := SidebarCategoryFromJson(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode)) - } - - return cat, BuildResponse(r) -} - -// CheckIntegrity performs a database integrity check. -func (c *Client4) CheckIntegrity() ([]IntegrityCheckResult, *Response) { - r, err := c.DoApiPost("/integrity", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - var results []IntegrityCheckResult - if err := json.NewDecoder(r.Body).Decode(&results); err != nil { - appErr := NewAppError("Api4.CheckIntegrity", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError) - return nil, BuildErrorResponse(r, appErr) - } - return results, BuildResponse(r) -} - -func (c *Client4) GetNotices(lastViewed int64, teamId string, client NoticeClientType, clientVersion, locale, etag string) (NoticeMessages, *Response) { - url := fmt.Sprintf("/system/notices/%s?lastViewed=%d&client=%s&clientVersion=%s&locale=%s", teamId, lastViewed, client, clientVersion, locale) - r, appErr := c.DoApiGet(url, etag) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - notices, err := UnmarshalProductNoticeMessages(r.Body) - if err != nil { - return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} - } - return notices, BuildResponse(r) -} - -func (c *Client4) MarkNoticesViewed(ids []string) *Response { - r, err := c.DoApiPut("/system/notices/view", ArrayToJson(ids)) - if err != nil { - return BuildErrorResponse(r, err) - } - defer closeBody(r) - return BuildResponse(r) -} - -// CreateUpload creates a new upload session. -func (c *Client4) CreateUpload(us *UploadSession) (*UploadSession, *Response) { - r, err := c.DoApiPost(c.GetUploadsRoute(), us.ToJson()) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UploadSessionFromJson(r.Body), BuildResponse(r) -} - -// GetUpload returns the upload session for the specified uploadId. -func (c *Client4) GetUpload(uploadId string) (*UploadSession, *Response) { - r, err := c.DoApiGet(c.GetUploadRoute(uploadId), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UploadSessionFromJson(r.Body), BuildResponse(r) -} - -// GetUploadsForUser returns the upload sessions created by the specified -// userId. -func (c *Client4) GetUploadsForUser(userId string) ([]*UploadSession, *Response) { - r, err := c.DoApiGet(c.GetUserRoute(userId)+"/uploads", "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return UploadSessionsFromJson(r.Body), BuildResponse(r) -} - -// UploadData performs an upload. On success it returns -// a FileInfo object. -func (c *Client4) UploadData(uploadId string, data io.Reader) (*FileInfo, *Response) { - url := c.GetUploadRoute(uploadId) - r, err := c.doApiRequestReader("POST", c.ApiUrl+url, data, nil) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return FileInfoFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) UpdatePassword(userId, currentPassword, newPassword string) *Response { - requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword} - r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)) - if err != nil { - return BuildErrorResponse(r, err) - } - defer closeBody(r) - return BuildResponse(r) -} - -// Cloud Section - -func (c *Client4) GetCloudProducts() ([]*Product, *Response) { - r, appErr := c.DoApiGet(c.GetCloudRoute()+"/products", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var cloudProducts []*Product - json.NewDecoder(r.Body).Decode(&cloudProducts) - - return cloudProducts, BuildResponse(r) -} - -func (c *Client4) CreateCustomerPayment() (*StripeSetupIntent, *Response) { - r, appErr := c.DoApiPost(c.GetCloudRoute()+"/payment", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var setupIntent *StripeSetupIntent - json.NewDecoder(r.Body).Decode(&setupIntent) - - return setupIntent, BuildResponse(r) -} - -func (c *Client4) ConfirmCustomerPayment(confirmRequest *ConfirmPaymentMethodRequest) *Response { - json, _ := json.Marshal(confirmRequest) - - r, appErr := c.doApiPostBytes(c.GetCloudRoute()+"/payment/confirm", json) - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return BuildResponse(r) -} - -func (c *Client4) GetCloudCustomer() (*CloudCustomer, *Response) { - r, appErr := c.DoApiGet(c.GetCloudRoute()+"/customer", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var cloudCustomer *CloudCustomer - json.NewDecoder(r.Body).Decode(&cloudCustomer) - - return cloudCustomer, BuildResponse(r) -} - -func (c *Client4) GetSubscription() (*Subscription, *Response) { - r, appErr := c.DoApiGet(c.GetCloudRoute()+"/subscription", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var subscription *Subscription - json.NewDecoder(r.Body).Decode(&subscription) - - return subscription, BuildResponse(r) -} - -func (c *Client4) GetSubscriptionStats() (*SubscriptionStats, *Response) { - r, appErr := c.DoApiGet(c.GetCloudRoute()+"/subscription/stats", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var stats *SubscriptionStats - json.NewDecoder(r.Body).Decode(&stats) - return stats, BuildResponse(r) -} - -func (c *Client4) GetInvoicesForSubscription() ([]*Invoice, *Response) { - r, appErr := c.DoApiGet(c.GetCloudRoute()+"/subscription/invoices", "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var invoices []*Invoice - json.NewDecoder(r.Body).Decode(&invoices) - - return invoices, BuildResponse(r) -} - -func (c *Client4) UpdateCloudCustomer(customerInfo *CloudCustomerInfo) (*CloudCustomer, *Response) { - customerBytes, _ := json.Marshal(customerInfo) - - r, appErr := c.doApiPutBytes(c.GetCloudRoute()+"/customer", customerBytes) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var customer *CloudCustomer - json.NewDecoder(r.Body).Decode(&customer) - - return customer, BuildResponse(r) -} - -func (c *Client4) UpdateCloudCustomerAddress(address *Address) (*CloudCustomer, *Response) { - addressBytes, _ := json.Marshal(address) - - r, appErr := c.doApiPutBytes(c.GetCloudRoute()+"/customer/address", addressBytes) - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var customer *CloudCustomer - json.NewDecoder(r.Body).Decode(&customer) - - return customer, BuildResponse(r) -} - -func (c *Client4) ListImports() ([]string, *Response) { - r, err := c.DoApiGet(c.GetImportsRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) ListExports() ([]string, *Response) { - r, err := c.DoApiGet(c.GetExportsRoute(), "") - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - return ArrayFromJson(r.Body), BuildResponse(r) -} - -func (c *Client4) DeleteExport(name string) (bool, *Response) { - r, err := c.DoApiDelete(c.GetExportRoute(name)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - return CheckStatusOK(r), BuildResponse(r) -} - -func (c *Client4) DownloadExport(name string, wr io.Writer, offset int64) (int64, *Response) { - var headers map[string]string - if offset > 0 { - headers = map[string]string{ - HEADER_RANGE: fmt.Sprintf("bytes=%d-", offset), - } - } - r, appErr := c.DoApiRequestWithHeaders(http.MethodGet, c.ApiUrl+c.GetExportRoute(name), "", headers) - if appErr != nil { - return 0, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - n, err := io.Copy(wr, r.Body) - if err != nil { - return n, BuildErrorResponse(r, NewAppError("DownloadExport", "model.client.copy.app_error", nil, err.Error(), r.StatusCode)) - } - return n, BuildResponse(r) -} - -func (c *Client4) GetUserThreads(userId, teamId string, options GetUserThreadsOpts) (*Threads, *Response) { - v := url.Values{} - if options.Since != 0 { - v.Set("since", fmt.Sprintf("%d", options.Since)) - } - if options.Before != "" { - v.Set("before", options.Before) - } - if options.After != "" { - v.Set("after", options.After) - } - if options.PageSize != 0 { - v.Set("pageSize", fmt.Sprintf("%d", options.PageSize)) - } - if options.Extended { - v.Set("extended", "true") - } - if options.Deleted { - v.Set("deleted", "true") - } - if options.Unread { - v.Set("unread", "true") - } - url := c.GetUserThreadsRoute(userId, teamId) - if len(v) > 0 { - url += "?" + v.Encode() - } - - r, appErr := c.DoApiGet(url, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var threads Threads - json.NewDecoder(r.Body).Decode(&threads) - - return &threads, BuildResponse(r) -} - -func (c *Client4) GetUserThread(userId, teamId, threadId string, extended bool) (*ThreadResponse, *Response) { - url := c.GetUserThreadRoute(userId, teamId, threadId) - if extended { - url += "?extended=true" - } - r, appErr := c.DoApiGet(url, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var thread ThreadResponse - json.NewDecoder(r.Body).Decode(&thread) - - return &thread, BuildResponse(r) -} - -func (c *Client4) UpdateThreadsReadForUser(userId, teamId string) *Response { - r, appErr := c.DoApiPut(fmt.Sprintf("%s/read", c.GetUserThreadsRoute(userId, teamId)), "") - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return BuildResponse(r) -} - -func (c *Client4) UpdateThreadReadForUser(userId, teamId, threadId string, timestamp int64) (*ThreadResponse, *Response) { - r, appErr := c.DoApiPut(fmt.Sprintf("%s/read/%d", c.GetUserThreadRoute(userId, teamId, threadId), timestamp), "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - var thread ThreadResponse - json.NewDecoder(r.Body).Decode(&thread) - - return &thread, BuildResponse(r) -} - -func (c *Client4) UpdateThreadFollowForUser(userId, teamId, threadId string, state bool) *Response { - var appErr *AppError - var r *http.Response - if state { - r, appErr = c.DoApiPut(c.GetUserThreadRoute(userId, teamId, threadId)+"/following", "") - } else { - r, appErr = c.DoApiDelete(c.GetUserThreadRoute(userId, teamId, threadId) + "/following") - } - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return BuildResponse(r) -} - -func (c *Client4) SendAdminUpgradeRequestEmail() *Response { - r, appErr := c.DoApiPost(c.GetCloudRoute()+"/subscription/limitreached/invite", "") - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return BuildResponse(r) -} - -func (c *Client4) SendAdminUpgradeRequestEmailOnJoin() *Response { - r, appErr := c.DoApiPost(c.GetCloudRoute()+"/subscription/limitreached/join", "") - if appErr != nil { - return BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - return BuildResponse(r) -} - -func (c *Client4) GetAllSharedChannels(teamID string, page, perPage int) ([]*SharedChannel, *Response) { - url := fmt.Sprintf("%s/%s?page=%d&per_page=%d", c.GetSharedChannelsRoute(), teamID, page, perPage) - r, appErr := c.DoApiGet(url, "") - if appErr != nil { - return nil, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var channels []*SharedChannel - json.NewDecoder(r.Body).Decode(&channels) - - return channels, BuildResponse(r) -} - -func (c *Client4) GetRemoteClusterInfo(remoteID string) (RemoteClusterInfo, *Response) { - url := fmt.Sprintf("%s/remote_info/%s", c.GetSharedChannelsRoute(), remoteID) - r, appErr := c.DoApiGet(url, "") - if appErr != nil { - return RemoteClusterInfo{}, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - var rci RemoteClusterInfo - json.NewDecoder(r.Body).Decode(&rci) - - return rci, BuildResponse(r) -} - -func (c *Client4) GetAncillaryPermissions(subsectionPermissions []string) ([]string, *Response) { - var returnedPermissions []string - url := fmt.Sprintf("%s/ancillary?subsection_permissions=%s", c.GetPermissionsRoute(), strings.Join(subsectionPermissions, ",")) - r, appErr := c.DoApiGet(url, "") - if appErr != nil { - return returnedPermissions, BuildErrorResponse(r, appErr) - } - defer closeBody(r) - - json.NewDecoder(r.Body).Decode(&returnedPermissions) - return returnedPermissions, BuildResponse(r) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/cloud.go b/vendor/github.com/mattermost/mattermost-server/v5/model/cloud.go deleted file mode 100644 index ffd85a2a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/cloud.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import "strings" - -const ( - EventTypeFailedPayment = "failed-payment" - EventTypeFailedPaymentNoCard = "failed-payment-no-card" - EventTypeSendAdminWelcomeEmail = "send-admin-welcome-email" - EventTypeTrialWillEnd = "trial-will-end" - EventTypeTrialEnded = "trial-ended" - JoinLimitation = "join" - InviteLimitation = "invite" -) - -var MockCWS string - -type BillingScheme string - -const ( - BillingSchemePerSeat = BillingScheme("per_seat") - BillingSchemeFlatFee = BillingScheme("flat_fee") -) - -type RecurringInterval string - -const ( - RecurringIntervalYearly = RecurringInterval("year") - RecurringIntervalMonthly = RecurringInterval("month") -) - -type SubscriptionFamily string - -const ( - SubscriptionFamilyCloud = SubscriptionFamily("cloud") - SubscriptionFamilyOnPrem = SubscriptionFamily("on-prem") -) - -// Product model represents a product on the cloud system. -type Product struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - PricePerSeat float64 `json:"price_per_seat"` - AddOns []*AddOn `json:"add_ons"` - SKU string `json:"sku"` - PriceID string `json:"price_id"` - Family SubscriptionFamily `json:"product_family"` - RecurringInterval RecurringInterval `json:"recurring_interval"` - BillingScheme BillingScheme `json:"billing_scheme"` -} - -// AddOn represents an addon to a product. -type AddOn struct { - ID string `json:"id"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - PricePerSeat float64 `json:"price_per_seat"` -} - -// StripeSetupIntent represents the SetupIntent model from Stripe for updating payment methods. -type StripeSetupIntent struct { - ID string `json:"id"` - ClientSecret string `json:"client_secret"` -} - -// ConfirmPaymentMethodRequest contains the fields for the customer payment update API. -type ConfirmPaymentMethodRequest struct { - StripeSetupIntentID string `json:"stripe_setup_intent_id"` -} - -// Customer model represents a customer on the system. -type CloudCustomer struct { - CloudCustomerInfo - ID string `json:"id"` - CreatorID string `json:"creator_id"` - CreateAt int64 `json:"create_at"` - BillingAddress *Address `json:"billing_address"` - CompanyAddress *Address `json:"company_address"` - PaymentMethod *PaymentMethod `json:"payment_method"` -} - -// CloudCustomerInfo represents editable info of a customer. -type CloudCustomerInfo struct { - Name string `json:"name"` - Email string `json:"email,omitempty"` - ContactFirstName string `json:"contact_first_name,omitempty"` - ContactLastName string `json:"contact_last_name,omitempty"` - NumEmployees int `json:"num_employees"` -} - -// Address model represents a customer's address. -type Address struct { - City string `json:"city"` - Country string `json:"country"` - Line1 string `json:"line1"` - Line2 string `json:"line2"` - PostalCode string `json:"postal_code"` - State string `json:"state"` -} - -// PaymentMethod represents methods of payment for a customer. -type PaymentMethod struct { - Type string `json:"type"` - LastFour int `json:"last_four"` - ExpMonth int `json:"exp_month"` - ExpYear int `json:"exp_year"` - CardBrand string `json:"card_brand"` - Name string `json:"name"` -} - -// Subscription model represents a subscription on the system. -type Subscription struct { - ID string `json:"id"` - CustomerID string `json:"customer_id"` - ProductID string `json:"product_id"` - AddOns []string `json:"add_ons"` - StartAt int64 `json:"start_at"` - EndAt int64 `json:"end_at"` - CreateAt int64 `json:"create_at"` - Seats int `json:"seats"` - Status string `json:"status"` - DNS string `json:"dns"` - IsPaidTier string `json:"is_paid_tier"` - LastInvoice *Invoice `json:"last_invoice"` - IsFreeTrial string `json:"is_free_trial"` - TrialEndAt int64 `json:"trial_end_at"` -} - -// GetWorkSpaceNameFromDNS returns the work space name. For example from test.mattermost.cloud.com, it returns test -func (s *Subscription) GetWorkSpaceNameFromDNS() string { - return strings.Split(s.DNS, ".")[0] -} - -// Invoice model represents a cloud invoice -type Invoice struct { - ID string `json:"id"` - Number string `json:"number"` - CreateAt int64 `json:"create_at"` - Total int64 `json:"total"` - Tax int64 `json:"tax"` - Status string `json:"status"` - Description string `json:"description"` - PeriodStart int64 `json:"period_start"` - PeriodEnd int64 `json:"period_end"` - SubscriptionID string `json:"subscription_id"` - Items []*InvoiceLineItem `json:"line_items"` -} - -// InvoiceLineItem model represents a cloud invoice lineitem tied to an invoice. -type InvoiceLineItem struct { - PriceID string `json:"price_id"` - Total int64 `json:"total"` - Quantity int64 `json:"quantity"` - PricePerUnit int64 `json:"price_per_unit"` - Description string `json:"description"` - Type string `json:"type"` - Metadata map[string]interface{} `json:"metadata"` -} - -type CWSWebhookPayload struct { - Event string `json:"event"` - FailedPayment *FailedPayment `json:"failed_payment"` - CloudWorkspaceOwner *CloudWorkspaceOwner `json:"cloud_workspace_owner"` - SubscriptionTrialEndUnixTimeStamp int64 `json:"trial_end_time_stamp"` -} - -type FailedPayment struct { - CardBrand string `json:"card_brand"` - LastFour int `json:"last_four"` - FailureMessage string `json:"failure_message"` -} - -// CloudWorkspaceOwner is part of the CWS Webhook payload that contains information about the user that created the workspace from the CWS -type CloudWorkspaceOwner struct { - UserName string `json:"username"` -} -type SubscriptionStats struct { - RemainingSeats int `json:"remaining_seats"` - IsPaidTier string `json:"is_paid_tier"` - IsFreeTrial string `json:"is_free_trial"` -} - -type SubscriptionChange struct { - ProductID string `json:"product_id"` -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_discovery.go b/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_discovery.go deleted file mode 100644 index 758e4980..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_discovery.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "os" -) - -const ( - CDS_OFFLINE_AFTER_MILLIS = 1000 * 60 * 30 // 30 minutes - CDS_TYPE_APP = "mattermost_app" -) - -type ClusterDiscovery struct { - Id string `json:"id"` - Type string `json:"type"` - ClusterName string `json:"cluster_name"` - Hostname string `json:"hostname"` - GossipPort int32 `json:"gossip_port"` - Port int32 `json:"port"` - CreateAt int64 `json:"create_at"` - LastPingAt int64 `json:"last_ping_at"` -} - -func (o *ClusterDiscovery) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - if o.CreateAt == 0 { - o.CreateAt = GetMillis() - o.LastPingAt = o.CreateAt - } -} - -func (o *ClusterDiscovery) AutoFillHostname() { - // attempt to set the hostname from the OS - if o.Hostname == "" { - if hn, err := os.Hostname(); err == nil { - o.Hostname = hn - } - } -} - -func (o *ClusterDiscovery) AutoFillIpAddress(iface string, ipAddress string) { - // attempt to set the hostname to the first non-local IP address - if o.Hostname == "" { - if ipAddress != "" { - o.Hostname = ipAddress - } else { - o.Hostname = GetServerIpAddress(iface) - } - } -} - -func (o *ClusterDiscovery) IsEqual(in *ClusterDiscovery) bool { - if in == nil { - return false - } - - if o.Type != in.Type { - return false - } - - if o.ClusterName != in.ClusterName { - return false - } - - if o.Hostname != in.Hostname { - return false - } - - return true -} - -func FilterClusterDiscovery(vs []*ClusterDiscovery, f func(*ClusterDiscovery) bool) []*ClusterDiscovery { - copy := make([]*ClusterDiscovery, 0) - for _, v := range vs { - if f(v) { - copy = append(copy, v) - } - } - - return copy -} - -func (o *ClusterDiscovery) IsValid() *AppError { - if !IsValidId(o.Id) { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if o.ClusterName == "" { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.name.app_error", nil, "", http.StatusBadRequest) - } - - if o.Type == "" { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.type.app_error", nil, "", http.StatusBadRequest) - } - - if o.Hostname == "" { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.hostname.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - if o.LastPingAt == 0 { - return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.last_ping_at.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *ClusterDiscovery) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } - - return string(b) -} - -func ClusterDiscoveryFromJson(data io.Reader) *ClusterDiscovery { - decoder := json.NewDecoder(data) - var me ClusterDiscovery - err := decoder.Decode(&me) - if err == nil { - return &me - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_info.go b/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_info.go deleted file mode 100644 index fc15d38c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_info.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ClusterInfo struct { - Id string `json:"id"` - Version string `json:"version"` - ConfigHash string `json:"config_hash"` - IpAddress string `json:"ipaddress"` - Hostname string `json:"hostname"` -} - -func (ci *ClusterInfo) ToJson() string { - b, _ := json.Marshal(ci) - return string(b) -} - -func ClusterInfoFromJson(data io.Reader) *ClusterInfo { - var ci *ClusterInfo - json.NewDecoder(data).Decode(&ci) - return ci -} - -func ClusterInfosToJson(objmap []*ClusterInfo) string { - b, _ := json.Marshal(objmap) - return string(b) -} - -func ClusterInfosFromJson(data io.Reader) []*ClusterInfo { - decoder := json.NewDecoder(data) - - var objmap []*ClusterInfo - if err := decoder.Decode(&objmap); err != nil { - return make([]*ClusterInfo, 0) - } - return objmap -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_message.go b/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_message.go deleted file mode 100644 index bd73ba8d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_message.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const ( - CLUSTER_EVENT_PUBLISH = "publish" - CLUSTER_EVENT_UPDATE_STATUS = "update_status" - CLUSTER_EVENT_INVALIDATE_ALL_CACHES = "inv_all_caches" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS = "inv_reactions" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOK = "inv_webhook" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_POSTS = "inv_channel_posts" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBERS_NOTIFY_PROPS = "inv_channel_members_notify_props" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBERS = "inv_channel_members" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_BY_NAME = "inv_channel_name" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL = "inv_channel" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_GUEST_COUNT = "inv_channel_guest_count" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER_TEAMS = "inv_user_teams" - CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLE_PERMISSIONS = "inv_role_permissions" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_BY_IDS = "inv_profile_ids" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_IN_CHANNEL = "inv_profile_in_channel" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_FILE_INFOS = "inv_file_infos" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOKS = "inv_webhooks" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID = "inv_emojis_by_id" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME = "inv_emojis_id_by_name" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_PINNEDPOSTS_COUNTS = "inv_channel_pinnedposts_counts" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS = "inv_channel_member_counts" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POSTS = "inv_last_posts" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POST_TIME = "inv_last_post_time" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TEAMS = "inv_teams" - CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_ALL_USERS = "inv_all_user_sessions" - CLUSTER_EVENT_INSTALL_PLUGIN = "install_plugin" - CLUSTER_EVENT_REMOVE_PLUGIN = "remove_plugin" - CLUSTER_EVENT_PLUGIN_EVENT = "plugin_event" - CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE = "inv_terms_of_service" - CLUSTER_EVENT_BUSY_STATE_CHANGED = "busy_state_change" - - // Gossip communication - CLUSTER_GOSSIP_EVENT_REQUEST_GET_LOGS = "gossip_request_get_logs" - CLUSTER_GOSSIP_EVENT_RESPONSE_GET_LOGS = "gossip_response_get_logs" - CLUSTER_GOSSIP_EVENT_REQUEST_GET_CLUSTER_STATS = "gossip_request_cluster_stats" - CLUSTER_GOSSIP_EVENT_RESPONSE_GET_CLUSTER_STATS = "gossip_response_cluster_stats" - CLUSTER_GOSSIP_EVENT_REQUEST_GET_PLUGIN_STATUSES = "gossip_request_plugin_statuses" - CLUSTER_GOSSIP_EVENT_RESPONSE_GET_PLUGIN_STATUSES = "gossip_response_plugin_statuses" - CLUSTER_GOSSIP_EVENT_REQUEST_SAVE_CONFIG = "gossip_request_save_config" - CLUSTER_GOSSIP_EVENT_RESPONSE_SAVE_CONFIG = "gossip_response_save_config" - - // SendTypes for ClusterMessage. - CLUSTER_SEND_BEST_EFFORT = "best_effort" - CLUSTER_SEND_RELIABLE = "reliable" -) - -type ClusterMessage struct { - Event string `json:"event"` - SendType string `json:"-"` - WaitForAllToSend bool `json:"-"` - Data string `json:"data,omitempty"` - Props map[string]string `json:"props,omitempty"` -} - -func (o *ClusterMessage) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ClusterMessageFromJson(data io.Reader) *ClusterMessage { - var o *ClusterMessage - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_stats.go b/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_stats.go deleted file mode 100644 index 9e8c630c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/cluster_stats.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type ClusterStats struct { - Id string `json:"id"` - TotalWebsocketConnections int `json:"total_websocket_connections"` - TotalReadDbConnections int `json:"total_read_db_connections"` - TotalMasterDbConnections int `json:"total_master_db_connections"` -} - -func (cs *ClusterStats) ToJson() string { - b, _ := json.Marshal(cs) - return string(b) -} - -func ClusterStatsFromJson(data io.Reader) *ClusterStats { - var cs *ClusterStats - json.NewDecoder(data).Decode(&cs) - return cs -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command.go deleted file mode 100644 index 59a4eee4..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "strings" -) - -const ( - COMMAND_METHOD_POST = "P" - COMMAND_METHOD_GET = "G" - MIN_TRIGGER_LENGTH = 1 - MAX_TRIGGER_LENGTH = 128 -) - -type Command struct { - Id string `json:"id"` - Token string `json:"token"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - CreatorId string `json:"creator_id"` - TeamId string `json:"team_id"` - Trigger string `json:"trigger"` - Method string `json:"method"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - AutoComplete bool `json:"auto_complete"` - AutoCompleteDesc string `json:"auto_complete_desc"` - AutoCompleteHint string `json:"auto_complete_hint"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - URL string `json:"url"` - // PluginId records the id of the plugin that created this Command. If it is blank, the Command - // was not created by a plugin. - PluginId string `json:"plugin_id"` - AutocompleteData *AutocompleteData `db:"-" json:"autocomplete_data,omitempty"` - // AutocompleteIconData is a base64 encoded svg - AutocompleteIconData string `db:"-" json:"autocomplete_icon_data,omitempty"` -} - -func (o *Command) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func CommandFromJson(data io.Reader) *Command { - var o *Command - json.NewDecoder(data).Decode(&o) - return o -} - -func CommandListToJson(l []*Command) string { - b, _ := json.Marshal(l) - return string(b) -} - -func CommandListFromJson(data io.Reader) []*Command { - var o []*Command - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *Command) IsValid() *AppError { - - if !IsValidId(o.Id) { - return NewAppError("Command.IsValid", "model.command.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Token) != 26 { - return NewAppError("Command.IsValid", "model.command.is_valid.token.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("Command.IsValid", "model.command.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("Command.IsValid", "model.command.is_valid.update_at.app_error", nil, "", http.StatusBadRequest) - } - - // If the CreatorId is blank, this should be a command created by a plugin. - if o.CreatorId == "" && !IsValidPluginId(o.PluginId) { - return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "", http.StatusBadRequest) - } - - // If the PluginId is blank, this should be a command associated with a userId. - if o.PluginId == "" && !IsValidId(o.CreatorId) { - return NewAppError("Command.IsValid", "model.command.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreatorId != "" && o.PluginId != "" { - return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "command cannot have both a CreatorId and a PluginId", http.StatusBadRequest) - } - - if !IsValidId(o.TeamId) { - return NewAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Trigger) < MIN_TRIGGER_LENGTH || len(o.Trigger) > MAX_TRIGGER_LENGTH || strings.Index(o.Trigger, "/") == 0 || strings.Contains(o.Trigger, " ") { - return NewAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "", http.StatusBadRequest) - } - - if o.URL == "" || len(o.URL) > 1024 { - return NewAppError("Command.IsValid", "model.command.is_valid.url.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidHttpUrl(o.URL) { - return NewAppError("Command.IsValid", "model.command.is_valid.url_http.app_error", nil, "", http.StatusBadRequest) - } - - if !(o.Method == COMMAND_METHOD_GET || o.Method == COMMAND_METHOD_POST) { - return NewAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.DisplayName) > 64 { - return NewAppError("Command.IsValid", "model.command.is_valid.display_name.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Description) > 128 { - return NewAppError("Command.IsValid", "model.command.is_valid.description.app_error", nil, "", http.StatusBadRequest) - } - - if o.AutocompleteData != nil { - if err := o.AutocompleteData.IsValid(); err != nil { - return NewAppError("Command.IsValid", "model.command.is_valid.autocomplete_data.app_error", nil, err.Error(), http.StatusBadRequest) - } - } - - return nil -} - -func (o *Command) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - if o.Token == "" { - o.Token = NewId() - } - - o.CreateAt = GetMillis() - o.UpdateAt = o.CreateAt -} - -func (o *Command) PreUpdate() { - o.UpdateAt = GetMillis() -} - -func (o *Command) Sanitize() { - o.Token = "" - o.CreatorId = "" - o.Method = "" - o.URL = "" - o.Username = "" - o.IconURL = "" -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command_args.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command_args.go deleted file mode 100644 index 45239d2c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command_args.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - - "github.com/mattermost/mattermost-server/v5/shared/i18n" -) - -type CommandArgs struct { - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - TeamId string `json:"team_id"` - RootId string `json:"root_id"` - ParentId string `json:"parent_id"` - TriggerId string `json:"trigger_id,omitempty"` - Command string `json:"command"` - SiteURL string `json:"-"` - T i18n.TranslateFunc `json:"-"` - UserMentions UserMentionMap `json:"-"` - ChannelMentions ChannelMentionMap `json:"-"` - - // DO NOT USE Session field is deprecated. MM-26398 - Session Session `json:"-"` -} - -func (o *CommandArgs) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func CommandArgsFromJson(data io.Reader) *CommandArgs { - var o *CommandArgs - json.NewDecoder(data).Decode(&o) - return o -} - -// AddUserMention adds or overrides an entry in UserMentions with name username -// and identifier userId -func (o *CommandArgs) AddUserMention(username, userId string) { - if o.UserMentions == nil { - o.UserMentions = make(UserMentionMap) - } - - o.UserMentions[username] = userId -} - -// AddChannelMention adds or overrides an entry in ChannelMentions with name -// channelName and identifier channelId -func (o *CommandArgs) AddChannelMention(channelName, channelId string) { - if o.ChannelMentions == nil { - o.ChannelMentions = make(ChannelMentionMap) - } - - o.ChannelMentions[channelName] = channelId -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command_autocomplete.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command_autocomplete.go deleted file mode 100644 index f115ed24..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command_autocomplete.go +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/url" - "path" - "reflect" - "strings" - - "github.com/pkg/errors" -) - -// AutocompleteArgType describes autocomplete argument type -type AutocompleteArgType string - -// Argument types -const ( - AutocompleteArgTypeText AutocompleteArgType = "TextInput" - AutocompleteArgTypeStaticList AutocompleteArgType = "StaticList" - AutocompleteArgTypeDynamicList AutocompleteArgType = "DynamicList" -) - -// AutocompleteData describes slash command autocomplete information. -type AutocompleteData struct { - // Trigger of the command - Trigger string - // Hint of a command - Hint string - // Text displayed to the user to help with the autocomplete description - HelpText string - // Role of the user who should be able to see the autocomplete info of this command - RoleID string - // Arguments of the command. Arguments can be named or positional. - // If they are positional order in the list matters, if they are named order does not matter. - // All arguments should be either named or positional, no mixing allowed. - Arguments []*AutocompleteArg - // Subcommands of the command - SubCommands []*AutocompleteData -} - -// AutocompleteArg describes an argument of the command. Arguments can be named or positional. -// If Name is empty string Argument is positional otherwise it is named argument. -// Named arguments are passed as --Name Argument_Value. -type AutocompleteArg struct { - // Name of the argument - Name string - // Text displayed to the user to help with the autocomplete - HelpText string - // Type of the argument - Type AutocompleteArgType - // Required determines if argument is optional or not. - Required bool - // Actual data of the argument (depends on the Type) - Data interface{} -} - -// AutocompleteTextArg describes text user can input as an argument. -type AutocompleteTextArg struct { - // Hint of the input text - Hint string - // Regex pattern to match - Pattern string -} - -// AutocompleteListItem describes an item in the AutocompleteStaticListArg. -type AutocompleteListItem struct { - Item string - Hint string - HelpText string -} - -// AutocompleteStaticListArg is used to input one of the arguments from the list, -// for example [yes, no], [on, off], and so on. -type AutocompleteStaticListArg struct { - PossibleArguments []AutocompleteListItem -} - -// AutocompleteDynamicListArg is used when user wants to download possible argument list from the URL. -type AutocompleteDynamicListArg struct { - FetchURL string -} - -// AutocompleteSuggestion describes a single suggestion item sent to the front-end -// Example: for user input `/jira cre` - -// Complete might be `/jira create` -// Suggestion might be `create`, -// Hint might be `[issue text]`, -// Description might be `Create a new Issue` -type AutocompleteSuggestion struct { - // Complete describes completed suggestion - Complete string - // Suggestion describes what user might want to input next - Suggestion string - // Hint describes a hint about the suggested input - Hint string - // Description of the command or a suggestion - Description string - // IconData is base64 encoded svg image - IconData string -} - -// NewAutocompleteData returns new Autocomplete data. -func NewAutocompleteData(trigger, hint, helpText string) *AutocompleteData { - return &AutocompleteData{ - Trigger: trigger, - Hint: hint, - HelpText: helpText, - RoleID: SYSTEM_USER_ROLE_ID, - Arguments: []*AutocompleteArg{}, - SubCommands: []*AutocompleteData{}, - } -} - -// AddCommand adds a subcommand to the autocomplete data. -func (ad *AutocompleteData) AddCommand(command *AutocompleteData) { - ad.SubCommands = append(ad.SubCommands, command) -} - -// AddTextArgument adds positional AutocompleteArgTypeText argument to the command. -func (ad *AutocompleteData) AddTextArgument(helpText, hint, pattern string) { - ad.AddNamedTextArgument("", helpText, hint, pattern, true) -} - -// AddNamedTextArgument adds named AutocompleteArgTypeText argument to the command. -func (ad *AutocompleteData) AddNamedTextArgument(name, helpText, hint, pattern string, required bool) { - argument := AutocompleteArg{ - Name: name, - HelpText: helpText, - Type: AutocompleteArgTypeText, - Required: required, - Data: &AutocompleteTextArg{Hint: hint, Pattern: pattern}, - } - ad.Arguments = append(ad.Arguments, &argument) -} - -// AddStaticListArgument adds positional AutocompleteArgTypeStaticList argument to the command. -func (ad *AutocompleteData) AddStaticListArgument(helpText string, required bool, items []AutocompleteListItem) { - ad.AddNamedStaticListArgument("", helpText, required, items) -} - -// AddNamedStaticListArgument adds named AutocompleteArgTypeStaticList argument to the command. -func (ad *AutocompleteData) AddNamedStaticListArgument(name, helpText string, required bool, items []AutocompleteListItem) { - argument := AutocompleteArg{ - Name: name, - HelpText: helpText, - Type: AutocompleteArgTypeStaticList, - Required: required, - Data: &AutocompleteStaticListArg{PossibleArguments: items}, - } - ad.Arguments = append(ad.Arguments, &argument) -} - -// AddDynamicListArgument adds positional AutocompleteArgTypeDynamicList argument to the command. -func (ad *AutocompleteData) AddDynamicListArgument(helpText, url string, required bool) { - ad.AddNamedDynamicListArgument("", helpText, url, required) -} - -// AddNamedDynamicListArgument adds named AutocompleteArgTypeDynamicList argument to the command. -func (ad *AutocompleteData) AddNamedDynamicListArgument(name, helpText, url string, required bool) { - argument := AutocompleteArg{ - Name: name, - HelpText: helpText, - Type: AutocompleteArgTypeDynamicList, - Required: required, - Data: &AutocompleteDynamicListArg{FetchURL: url}, - } - ad.Arguments = append(ad.Arguments, &argument) -} - -// Equals method checks if command is the same. -func (ad *AutocompleteData) Equals(command *AutocompleteData) bool { - if !(ad.Trigger == command.Trigger && ad.HelpText == command.HelpText && ad.RoleID == command.RoleID && ad.Hint == command.Hint) { - return false - } - if len(ad.Arguments) != len(command.Arguments) || len(ad.SubCommands) != len(command.SubCommands) { - return false - } - for i := range ad.Arguments { - if !ad.Arguments[i].Equals(command.Arguments[i]) { - return false - } - } - for i := range ad.SubCommands { - if !ad.SubCommands[i].Equals(command.SubCommands[i]) { - return false - } - } - return true -} - -// UpdateRelativeURLsForPluginCommands method updates relative urls for plugin commands -func (ad *AutocompleteData) UpdateRelativeURLsForPluginCommands(baseURL *url.URL) error { - for _, arg := range ad.Arguments { - if arg.Type != AutocompleteArgTypeDynamicList { - continue - } - dynamicList, ok := arg.Data.(*AutocompleteDynamicListArg) - if !ok { - return errors.New("Not a proper DynamicList type argument") - } - dynamicListURL, err := url.Parse(dynamicList.FetchURL) - if err != nil { - return errors.Wrapf(err, "FetchURL is not a proper url") - } - if !dynamicListURL.IsAbs() { - absURL := &url.URL{} - *absURL = *baseURL - absURL.Path = path.Join(absURL.Path, dynamicList.FetchURL) - dynamicList.FetchURL = absURL.String() - } - - } - for _, command := range ad.SubCommands { - err := command.UpdateRelativeURLsForPluginCommands(baseURL) - if err != nil { - return err - } - } - return nil -} - -// IsValid method checks if autocomplete data is valid. -func (ad *AutocompleteData) IsValid() error { - if ad == nil { - return errors.New("No nil commands are allowed in AutocompleteData") - } - if ad.Trigger == "" { - return errors.New("An empty command name in the autocomplete data") - } - if strings.ToLower(ad.Trigger) != ad.Trigger { - return errors.New("Command should be lowercase") - } - roles := []string{SYSTEM_ADMIN_ROLE_ID, SYSTEM_USER_ROLE_ID, ""} - if stringNotInSlice(ad.RoleID, roles) { - return errors.New("Wrong role in the autocomplete data") - } - if len(ad.Arguments) > 0 && len(ad.SubCommands) > 0 { - return errors.New("Command can't have arguments and subcommands") - } - if len(ad.Arguments) > 0 { - namedArgumentIndex := -1 - for i, arg := range ad.Arguments { - if arg.Name != "" { // it's a named argument - if namedArgumentIndex == -1 { // first named argument - namedArgumentIndex = i - } - } else { // it's a positional argument - if namedArgumentIndex != -1 { - return errors.New("Named argument should not be before positional argument") - } - } - if arg.Type == AutocompleteArgTypeDynamicList { - dynamicList, ok := arg.Data.(*AutocompleteDynamicListArg) - if !ok { - return errors.New("Not a proper DynamicList type argument") - } - _, err := url.Parse(dynamicList.FetchURL) - if err != nil { - return errors.Wrapf(err, "FetchURL is not a proper url") - } - } else if arg.Type == AutocompleteArgTypeStaticList { - staticList, ok := arg.Data.(*AutocompleteStaticListArg) - if !ok { - return errors.New("Not a proper StaticList type argument") - } - for _, arg := range staticList.PossibleArguments { - if arg.Item == "" { - return errors.New("Possible argument name not set in StaticList argument") - } - } - } else if arg.Type == AutocompleteArgTypeText { - if _, ok := arg.Data.(*AutocompleteTextArg); !ok { - return errors.New("Not a proper TextInput type argument") - } - if arg.Name == "" && !arg.Required { - return errors.New("Positional argument can not be optional") - } - } - } - } - for _, command := range ad.SubCommands { - err := command.IsValid() - if err != nil { - return err - } - } - return nil -} - -// ToJSON encodes AutocompleteData struct to the json -func (ad *AutocompleteData) ToJSON() ([]byte, error) { - b, err := json.Marshal(ad) - if err != nil { - return nil, errors.Wrapf(err, "can't marshal slash command %s", ad.Trigger) - } - return b, nil -} - -// AutocompleteDataFromJSON decodes AutocompleteData struct from the json -func AutocompleteDataFromJSON(data []byte) (*AutocompleteData, error) { - var ad AutocompleteData - if err := json.Unmarshal(data, &ad); err != nil { - return nil, errors.Wrap(err, "can't unmarshal AutocompleteData") - } - return &ad, nil -} - -// Equals method checks if argument is the same. -func (a *AutocompleteArg) Equals(arg *AutocompleteArg) bool { - if a.Name != arg.Name || - a.HelpText != arg.HelpText || - a.Type != arg.Type || - a.Required != arg.Required || - !reflect.DeepEqual(a.Data, arg.Data) { - return false - } - return true -} - -// UnmarshalJSON will unmarshal argument -func (a *AutocompleteArg) UnmarshalJSON(b []byte) error { - var arg map[string]interface{} - if err := json.Unmarshal(b, &arg); err != nil { - return errors.Wrapf(err, "Can't unmarshal argument %s", string(b)) - } - var ok bool - a.Name, ok = arg["Name"].(string) - if !ok { - return errors.Errorf("No field Name in the argument %s", string(b)) - } - - a.HelpText, ok = arg["HelpText"].(string) - if !ok { - return errors.Errorf("No field HelpText in the argument %s", string(b)) - } - - t, ok := arg["Type"].(string) - if !ok { - return errors.Errorf("No field Type in the argument %s", string(b)) - } - a.Type = AutocompleteArgType(t) - - a.Required, ok = arg["Required"].(bool) - if !ok { - return errors.Errorf("No field Required in the argument %s", string(b)) - } - - data, ok := arg["Data"] - if !ok { - return errors.Errorf("No field Data in the argument %s", string(b)) - } - - if a.Type == AutocompleteArgTypeText { - m, ok := data.(map[string]interface{}) - if !ok { - return errors.Errorf("Wrong Data type in the TextInput argument %s", string(b)) - } - pattern, ok := m["Pattern"].(string) - if !ok { - return errors.Errorf("No field Pattern in the TextInput argument %s", string(b)) - } - hint, ok := m["Hint"].(string) - if !ok { - return errors.Errorf("No field Hint in the TextInput argument %s", string(b)) - } - a.Data = &AutocompleteTextArg{Hint: hint, Pattern: pattern} - } else if a.Type == AutocompleteArgTypeStaticList { - m, ok := data.(map[string]interface{}) - if !ok { - return errors.Errorf("Wrong Data type in the StaticList argument %s", string(b)) - } - list, ok := m["PossibleArguments"].([]interface{}) - if !ok { - return errors.Errorf("No field PossibleArguments in the StaticList argument %s", string(b)) - } - - possibleArguments := []AutocompleteListItem{} - for i := range list { - args, ok := list[i].(map[string]interface{}) - if !ok { - return errors.Errorf("Wrong AutocompleteStaticListItem type in the StaticList argument %s", string(b)) - } - item, ok := args["Item"].(string) - if !ok { - return errors.Errorf("No field Item in the StaticList's possible arguments %s", string(b)) - } - - hint, ok := args["Hint"].(string) - if !ok { - return errors.Errorf("No field Hint in the StaticList's possible arguments %s", string(b)) - } - helpText, ok := args["HelpText"].(string) - if !ok { - return errors.Errorf("No field Hint in the StaticList's possible arguments %s", string(b)) - } - - possibleArguments = append(possibleArguments, AutocompleteListItem{ - Item: item, - Hint: hint, - HelpText: helpText, - }) - } - a.Data = &AutocompleteStaticListArg{PossibleArguments: possibleArguments} - } else if a.Type == AutocompleteArgTypeDynamicList { - m, ok := data.(map[string]interface{}) - if !ok { - return errors.Errorf("Wrong type in the DynamicList argument %s", string(b)) - } - url, ok := m["FetchURL"].(string) - if !ok { - return errors.Errorf("No field FetchURL in the DynamicList's argument %s", string(b)) - } - a.Data = &AutocompleteDynamicListArg{FetchURL: url} - } - return nil -} - -// AutocompleteSuggestionsToJSON returns json for a list of AutocompleteSuggestion objects -func AutocompleteSuggestionsToJSON(suggestions []AutocompleteSuggestion) []byte { - b, _ := json.Marshal(suggestions) - return b -} - -// AutocompleteSuggestionsFromJSON returns list of AutocompleteSuggestions from json. -func AutocompleteSuggestionsFromJSON(data io.Reader) []AutocompleteSuggestion { - var o []AutocompleteSuggestion - json.NewDecoder(data).Decode(&o) - return o -} - -// AutocompleteStaticListItemsToJSON returns json for a list of AutocompleteStaticListItem objects -func AutocompleteStaticListItemsToJSON(items []AutocompleteListItem) []byte { - b, _ := json.Marshal(items) - return b -} - -// AutocompleteStaticListItemsFromJSON returns list of AutocompleteStaticListItem from json. -func AutocompleteStaticListItemsFromJSON(data io.Reader) []AutocompleteListItem { - var o []AutocompleteListItem - json.NewDecoder(data).Decode(&o) - return o -} - -func stringNotInSlice(a string, slice []string) bool { - for _, b := range slice { - if b == a { - return false - } - } - return true -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command_request.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command_request.go deleted file mode 100644 index 9a4e40c8..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command_request.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type CommandMoveRequest struct { - TeamId string `json:"team_id"` -} - -func CommandMoveRequestFromJson(data io.Reader) (*CommandMoveRequest, error) { - decoder := json.NewDecoder(data) - var cmr CommandMoveRequest - err := decoder.Decode(&cmr) - if err != nil { - return nil, err - } - return &cmr, nil -} - -func (cmr *CommandMoveRequest) ToJson() string { - b, err := json.Marshal(cmr) - if err != nil { - return "" - } - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command_response.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command_response.go deleted file mode 100644 index 80cca5ab..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command_response.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "io/ioutil" - "strings" - - "github.com/mattermost/mattermost-server/v5/utils/jsonutils" -) - -const ( - COMMAND_RESPONSE_TYPE_IN_CHANNEL = "in_channel" - COMMAND_RESPONSE_TYPE_EPHEMERAL = "ephemeral" -) - -type CommandResponse struct { - ResponseType string `json:"response_type"` - Text string `json:"text"` - Username string `json:"username"` - ChannelId string `json:"channel_id"` - IconURL string `json:"icon_url"` - Type string `json:"type"` - Props StringInterface `json:"props"` - GotoLocation string `json:"goto_location"` - TriggerId string `json:"trigger_id"` - SkipSlackParsing bool `json:"skip_slack_parsing"` // Set to `true` to skip the Slack-compatibility handling of Text. - Attachments []*SlackAttachment `json:"attachments"` - ExtraResponses []*CommandResponse `json:"extra_responses"` -} - -func (o *CommandResponse) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func CommandResponseFromHTTPBody(contentType string, body io.Reader) (*CommandResponse, error) { - if strings.TrimSpace(strings.Split(contentType, ";")[0]) == "application/json" { - return CommandResponseFromJson(body) - } - if b, err := ioutil.ReadAll(body); err == nil { - return CommandResponseFromPlainText(string(b)), nil - } - return nil, nil -} - -func CommandResponseFromPlainText(text string) *CommandResponse { - return &CommandResponse{ - Text: text, - } -} - -func CommandResponseFromJson(data io.Reader) (*CommandResponse, error) { - b, err := ioutil.ReadAll(data) - if err != nil { - return nil, err - } - - var o CommandResponse - err = json.Unmarshal(b, &o) - if err != nil { - return nil, jsonutils.HumanizeJSONError(err, b) - } - - o.Attachments = StringifySlackFieldValue(o.Attachments) - - if o.ExtraResponses != nil { - for _, resp := range o.ExtraResponses { - resp.Attachments = StringifySlackFieldValue(resp.Attachments) - } - } - - return &o, nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/command_webhook.go b/vendor/github.com/mattermost/mattermost-server/v5/model/command_webhook.go deleted file mode 100644 index 3757ecc7..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/command_webhook.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "net/http" -) - -type CommandWebhook struct { - Id string - CreateAt int64 - CommandId string - UserId string - ChannelId string - RootId string - ParentId string - UseCount int -} - -const ( - COMMAND_WEBHOOK_LIFETIME = 1000 * 60 * 30 -) - -func (o *CommandWebhook) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - if o.CreateAt == 0 { - o.CreateAt = GetMillis() - } -} - -func (o *CommandWebhook) IsValid() *AppError { - if !IsValidId(o.Id) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.id.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidId(o.CommandId) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.command_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.UserId) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.ChannelId) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.channel_id.app_error", nil, "", http.StatusBadRequest) - } - - if o.RootId != "" && !IsValidId(o.RootId) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.root_id.app_error", nil, "", http.StatusBadRequest) - } - - if o.ParentId != "" && !IsValidId(o.ParentId) { - return NewAppError("CommandWebhook.IsValid", "model.command_hook.parent_id.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/compliance.go b/vendor/github.com/mattermost/mattermost-server/v5/model/compliance.go deleted file mode 100644 index 0211805e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/compliance.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "strings" -) - -const ( - COMPLIANCE_STATUS_CREATED = "created" - COMPLIANCE_STATUS_RUNNING = "running" - COMPLIANCE_STATUS_FINISHED = "finished" - COMPLIANCE_STATUS_FAILED = "failed" - COMPLIANCE_STATUS_REMOVED = "removed" - - COMPLIANCE_TYPE_DAILY = "daily" - COMPLIANCE_TYPE_ADHOC = "adhoc" -) - -type Compliance struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UserId string `json:"user_id"` - Status string `json:"status"` - Count int `json:"count"` - Desc string `json:"desc"` - Type string `json:"type"` - StartAt int64 `json:"start_at"` - EndAt int64 `json:"end_at"` - Keywords string `json:"keywords"` - Emails string `json:"emails"` -} - -type Compliances []Compliance - -// ComplianceExportCursor is used for paginated iteration of posts -// for compliance export. -// We need to keep track of the last post ID in addition to the last post -// CreateAt to break ties when two posts have the same CreateAt. -type ComplianceExportCursor struct { - LastChannelsQueryPostCreateAt int64 - LastChannelsQueryPostID string - ChannelsQueryCompleted bool - LastDirectMessagesQueryPostCreateAt int64 - LastDirectMessagesQueryPostID string - DirectMessagesQueryCompleted bool -} - -func (c *Compliance) ToJson() string { - b, _ := json.Marshal(c) - return string(b) -} - -func (c *Compliance) PreSave() { - if c.Id == "" { - c.Id = NewId() - } - - if c.Status == "" { - c.Status = COMPLIANCE_STATUS_CREATED - } - - c.Count = 0 - c.Emails = NormalizeEmail(c.Emails) - c.Keywords = strings.ToLower(c.Keywords) - - c.CreateAt = GetMillis() -} - -func (c *Compliance) DeepCopy() *Compliance { - copy := *c - return © -} - -func (c *Compliance) JobName() string { - jobName := c.Type - if c.Type == COMPLIANCE_TYPE_DAILY { - jobName += "-" + c.Desc - } - - jobName += "-" + c.Id - - return jobName -} - -func (c *Compliance) IsValid() *AppError { - - if !IsValidId(c.Id) { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if c.CreateAt == 0 { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - if len(c.Desc) > 512 || c.Desc == "" { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.desc.app_error", nil, "", http.StatusBadRequest) - } - - if c.StartAt == 0 { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.start_at.app_error", nil, "", http.StatusBadRequest) - } - - if c.EndAt == 0 { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.end_at.app_error", nil, "", http.StatusBadRequest) - } - - if c.EndAt <= c.StartAt { - return NewAppError("Compliance.IsValid", "model.compliance.is_valid.start_end_at.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func ComplianceFromJson(data io.Reader) *Compliance { - var c *Compliance - json.NewDecoder(data).Decode(&c) - return c -} - -func (c Compliances) ToJson() string { - b, err := json.Marshal(c) - if err != nil { - return "[]" - } - return string(b) -} - -func CompliancesFromJson(data io.Reader) Compliances { - var o Compliances - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/compliance_post.go b/vendor/github.com/mattermost/mattermost-server/v5/model/compliance_post.go deleted file mode 100644 index 5e18812b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/compliance_post.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "regexp" - "time" -) - -type CompliancePost struct { - - // From Team - TeamName string - TeamDisplayName string - - // From Channel - ChannelName string - ChannelDisplayName string - ChannelType string - - // From User - UserUsername string - UserEmail string - UserNickname string - - // From Post - PostId string - PostCreateAt int64 - PostUpdateAt int64 - PostDeleteAt int64 - PostRootId string - PostParentId string - PostOriginalId string - PostMessage string - PostType string - PostProps string - PostHashtags string - PostFileIds string - - IsBot bool -} - -func CompliancePostHeader() []string { - return []string{ - "TeamName", - "TeamDisplayName", - - "ChannelName", - "ChannelDisplayName", - "ChannelType", - - "UserUsername", - "UserEmail", - "UserNickname", - "UserType", - - "PostId", - "PostCreateAt", - "PostUpdateAt", - "PostDeleteAt", - "PostRootId", - "PostParentId", - "PostOriginalId", - "PostMessage", - "PostType", - "PostProps", - "PostHashtags", - "PostFileIds", - } -} - -func cleanComplianceStrings(in string) string { - if matched, _ := regexp.MatchString("^\\s*(=|\\+|\\-)", in); matched { - return "'" + in - } - return in -} - -func (cp *CompliancePost) Row() []string { - - postDeleteAt := "" - if cp.PostDeleteAt > 0 { - postDeleteAt = time.Unix(0, cp.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339) - } - - postUpdateAt := "" - if cp.PostUpdateAt != cp.PostCreateAt { - postUpdateAt = time.Unix(0, cp.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339) - } - - userType := "user" - if cp.IsBot { - userType = "bot" - } - - return []string{ - cleanComplianceStrings(cp.TeamName), - cleanComplianceStrings(cp.TeamDisplayName), - - cleanComplianceStrings(cp.ChannelName), - cleanComplianceStrings(cp.ChannelDisplayName), - cleanComplianceStrings(cp.ChannelType), - - cleanComplianceStrings(cp.UserUsername), - cleanComplianceStrings(cp.UserEmail), - cleanComplianceStrings(cp.UserNickname), - userType, - - cp.PostId, - time.Unix(0, cp.PostCreateAt*int64(1000*1000)).Format(time.RFC3339), - postUpdateAt, - postDeleteAt, - - cp.PostRootId, - cp.PostParentId, - cp.PostOriginalId, - cleanComplianceStrings(cp.PostMessage), - cp.PostType, - cp.PostProps, - cp.PostHashtags, - cp.PostFileIds, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/config.go b/vendor/github.com/mattermost/mattermost-server/v5/model/config.go deleted file mode 100644 index 52722161..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/config.go +++ /dev/null @@ -1,3994 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto/tls" - "encoding/json" - "io" - "math" - "net" - "net/http" - "net/url" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "time" - - "github.com/mattermost/ldap" - - "github.com/mattermost/mattermost-server/v5/shared/filestore" - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const ( - CONN_SECURITY_NONE = "" - CONN_SECURITY_PLAIN = "PLAIN" - CONN_SECURITY_TLS = "TLS" - CONN_SECURITY_STARTTLS = "STARTTLS" - - IMAGE_DRIVER_LOCAL = "local" - IMAGE_DRIVER_S3 = "amazons3" - - DATABASE_DRIVER_MYSQL = "mysql" - DATABASE_DRIVER_POSTGRES = "postgres" - - SEARCHENGINE_ELASTICSEARCH = "elasticsearch" - - MINIO_ACCESS_KEY = "minioaccesskey" - MINIO_SECRET_KEY = "miniosecretkey" - MINIO_BUCKET = "mattermost-test" - - PASSWORD_MAXIMUM_LENGTH = 64 - PASSWORD_MINIMUM_LENGTH = 5 - - SERVICE_GITLAB = "gitlab" - SERVICE_GOOGLE = "google" - SERVICE_OFFICE365 = "office365" - SERVICE_OPENID = "openid" - - GENERIC_NO_CHANNEL_NOTIFICATION = "generic_no_channel" - GENERIC_NOTIFICATION = "generic" - GENERIC_NOTIFICATION_SERVER = "https://push-test.mattermost.com" - MM_SUPPORT_ADVISOR_ADDRESS = "support-advisor@mattermost.com" - FULL_NOTIFICATION = "full" - ID_LOADED_NOTIFICATION = "id_loaded" - - DIRECT_MESSAGE_ANY = "any" - DIRECT_MESSAGE_TEAM = "team" - - SHOW_USERNAME = "username" - SHOW_NICKNAME_FULLNAME = "nickname_full_name" - SHOW_FULLNAME = "full_name" - - PERMISSIONS_ALL = "all" - PERMISSIONS_CHANNEL_ADMIN = "channel_admin" - PERMISSIONS_TEAM_ADMIN = "team_admin" - PERMISSIONS_SYSTEM_ADMIN = "system_admin" - - FAKE_SETTING = "********************************" - - RESTRICT_EMOJI_CREATION_ALL = "all" - 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" - - GROUP_UNREAD_CHANNELS_DISABLED = "disabled" - GROUP_UNREAD_CHANNELS_DEFAULT_ON = "default_on" - GROUP_UNREAD_CHANNELS_DEFAULT_OFF = "default_off" - - COLLAPSED_THREADS_DISABLED = "disabled" - COLLAPSED_THREADS_DEFAULT_ON = "default_on" - COLLAPSED_THREADS_DEFAULT_OFF = "default_off" - - EMAIL_BATCHING_BUFFER_SIZE = 256 - EMAIL_BATCHING_INTERVAL = 30 - - EMAIL_NOTIFICATION_CONTENTS_FULL = "full" - EMAIL_NOTIFICATION_CONTENTS_GENERIC = "generic" - - SITENAME_MAX_LENGTH = 30 - - SERVICE_SETTINGS_DEFAULT_SITE_URL = "http://localhost:8065" - 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_IDLE_TIMEOUT = 60 - SERVICE_SETTINGS_DEFAULT_MAX_LOGIN_ATTEMPTS = 10 - SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = "" - SERVICE_SETTINGS_DEFAULT_LISTEN_AND_ADDRESS = ":8065" - SERVICE_SETTINGS_DEFAULT_GFYCAT_API_KEY = "2_KtH_W5" - SERVICE_SETTINGS_DEFAULT_GFYCAT_API_SECRET = "3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof" - - TEAM_SETTINGS_DEFAULT_SITE_NAME = "Mattermost" - TEAM_SETTINGS_DEFAULT_MAX_USERS_PER_TEAM = 50 - TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT = "" - TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT = "" - TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT = 300 - - SQL_SETTINGS_DEFAULT_DATA_SOURCE = "postgres://mmuser:mostest@localhost/mattermost_test?sslmode=disable&connect_timeout=10" - - FILE_SETTINGS_DEFAULT_DIRECTORY = "./data/" - - IMPORT_SETTINGS_DEFAULT_DIRECTORY = "./import" - IMPORT_SETTINGS_DEFAULT_RETENTION_DAYS = 30 - - EXPORT_SETTINGS_DEFAULT_DIRECTORY = "./export" - EXPORT_SETTINGS_DEFAULT_RETENTION_DAYS = 30 - - EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = "" - - SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://mattermost.com/terms-of-service/" - SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://mattermost.com/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 = "" - SUPPORT_SETTINGS_DEFAULT_RE_ACCEPTANCE_PERIOD = 365 - - 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 = "" - LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE = "" - LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE = "" - LDAP_SETTINGS_DEFAULT_PICTURE_ATTRIBUTE = "" - - SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE = "" - SAML_SETTINGS_DEFAULT_GUEST_ATTRIBUTE = "" - SAML_SETTINGS_DEFAULT_ADMIN_ATTRIBUTE = "" - 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 = "" - - SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA1 = "RSAwithSHA1" - SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA256 = "RSAwithSHA256" - SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA512 = "RSAwithSHA512" - SAML_SETTINGS_DEFAULT_SIGNATURE_ALGORITHM = SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA1 - - SAML_SETTINGS_CANONICAL_ALGORITHM_C14N = "Canonical1.0" - SAML_SETTINGS_CANONICAL_ALGORITHM_C14N11 = "Canonical1.1" - SAML_SETTINGS_DEFAULT_CANONICAL_ALGORITHM = SAML_SETTINGS_CANONICAL_ALGORITHM_C14N - - NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK = "https://mattermost.com/download/#mattermostApps" - 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/" - - EXPERIMENTAL_SETTINGS_DEFAULT_LINK_METADATA_TIMEOUT_MILLISECONDS = 5000 - - ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500 - - ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR = "#f2a93b" - ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR = "#333333" - ANNOUNCEMENT_SETTINGS_DEFAULT_NOTICES_JSON_URL = "https://notices.mattermost.com/" - ANNOUNCEMENT_SETTINGS_DEFAULT_NOTICES_FETCH_FREQUENCY_SECONDS = 3600 - - TEAM_SETTINGS_DEFAULT_TEAM_TEXT = "default" - - ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL = "http://localhost:9200" - ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME = "elastic" - ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD = "changeme" - ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_REPLICAS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_REPLICAS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_SHARDS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_REPLICAS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_SHARDS = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_AGGREGATE_POSTS_AFTER_DAYS = 365 - ELASTICSEARCH_SETTINGS_DEFAULT_POSTS_AGGREGATOR_JOB_START_TIME = "03:00" - ELASTICSEARCH_SETTINGS_DEFAULT_INDEX_PREFIX = "" - ELASTICSEARCH_SETTINGS_DEFAULT_LIVE_INDEXING_BATCH_SIZE = 1 - ELASTICSEARCH_SETTINGS_DEFAULT_BULK_INDEXING_TIME_WINDOW_SECONDS = 3600 - ELASTICSEARCH_SETTINGS_DEFAULT_REQUEST_TIMEOUT_SECONDS = 30 - - BLEVE_SETTINGS_DEFAULT_INDEX_DIR = "" - BLEVE_SETTINGS_DEFAULT_BULK_INDEXING_TIME_WINDOW_SECONDS = 3600 - - DATA_RETENTION_SETTINGS_DEFAULT_MESSAGE_RETENTION_DAYS = 365 - DATA_RETENTION_SETTINGS_DEFAULT_FILE_RETENTION_DAYS = 365 - DATA_RETENTION_SETTINGS_DEFAULT_DELETION_JOB_START_TIME = "02:00" - DATA_RETENTION_SETTINGS_DEFAULT_BATCH_SIZE = 3000 - - PLUGIN_SETTINGS_DEFAULT_DIRECTORY = "./plugins" - PLUGIN_SETTINGS_DEFAULT_CLIENT_DIRECTORY = "./client/plugins" - PLUGIN_SETTINGS_DEFAULT_ENABLE_MARKETPLACE = true - PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL = "https://api.integrations.mattermost.com" - PLUGIN_SETTINGS_OLD_MARKETPLACE_URL = "https://marketplace.integrations.mattermost.com" - - COMPLIANCE_EXPORT_TYPE_CSV = "csv" - COMPLIANCE_EXPORT_TYPE_ACTIANCE = "actiance" - COMPLIANCE_EXPORT_TYPE_GLOBALRELAY = "globalrelay" - COMPLIANCE_EXPORT_TYPE_GLOBALRELAY_ZIP = "globalrelay-zip" - GLOBALRELAY_CUSTOMER_TYPE_A9 = "A9" - GLOBALRELAY_CUSTOMER_TYPE_A10 = "A10" - - CLIENT_SIDE_CERT_CHECK_PRIMARY_AUTH = "primary" - CLIENT_SIDE_CERT_CHECK_SECONDARY_AUTH = "secondary" - - IMAGE_PROXY_TYPE_LOCAL = "local" - IMAGE_PROXY_TYPE_ATMOS_CAMO = "atmos/camo" - - GOOGLE_SETTINGS_DEFAULT_SCOPE = "profile email" - GOOGLE_SETTINGS_DEFAULT_AUTH_ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth" - GOOGLE_SETTINGS_DEFAULT_TOKEN_ENDPOINT = "https://www.googleapis.com/oauth2/v4/token" - GOOGLE_SETTINGS_DEFAULT_USER_API_ENDPOINT = "https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,nicknames,metadata" - - OFFICE365_SETTINGS_DEFAULT_SCOPE = "User.Read" - OFFICE365_SETTINGS_DEFAULT_AUTH_ENDPOINT = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" - OFFICE365_SETTINGS_DEFAULT_TOKEN_ENDPOINT = "https://login.microsoftonline.com/common/oauth2/v2.0/token" - OFFICE365_SETTINGS_DEFAULT_USER_API_ENDPOINT = "https://graph.microsoft.com/v1.0/me" - - CLOUD_SETTINGS_DEFAULT_CWS_URL = "https://customers.mattermost.com" - CLOUD_SETTINGS_DEFAULT_CWS_API_URL = "https://portal.internal.prod.cloud.mattermost.com" - OPENID_SETTINGS_DEFAULT_SCOPE = "profile openid email" - - LOCAL_MODE_SOCKET_PATH = "/var/tmp/mattermost_local.socket" -) - -func GetDefaultAppCustomURLSchemes() []string { - return []string{"mmauth://", "mmauthbeta://"} -} - -var ServerTLSSupportedCiphers = map[string]uint16{ - "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, - "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, -} - -type ServiceSettings struct { - SiteURL *string `access:"environment_web_server,authentication_saml,write_restrictable"` - WebsocketURL *string `access:"write_restrictable,cloud_restrictable"` - LicenseFileLocation *string `access:"write_restrictable,cloud_restrictable"` // telemetry: none - ListenAddress *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` // telemetry: none - ConnectionSecurity *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - TLSCertFile *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - TLSKeyFile *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - TLSMinVer *string `access:"write_restrictable,cloud_restrictable"` // telemetry: none - TLSStrictTransport *bool `access:"write_restrictable,cloud_restrictable"` - TLSStrictTransportMaxAge *int64 `access:"write_restrictable,cloud_restrictable"` // telemetry: none - TLSOverwriteCiphers []string `access:"write_restrictable,cloud_restrictable"` // telemetry: none - UseLetsEncrypt *bool `access:"environment_web_server,write_restrictable,cloud_restrictable"` - LetsEncryptCertificateCacheFile *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` // telemetry: none - Forward80To443 *bool `access:"environment_web_server,write_restrictable,cloud_restrictable"` - TrustedProxyIPHeader []string `access:"write_restrictable,cloud_restrictable"` // telemetry: none - ReadTimeout *int `access:"environment_web_server,write_restrictable,cloud_restrictable"` - WriteTimeout *int `access:"environment_web_server,write_restrictable,cloud_restrictable"` - IdleTimeout *int `access:"write_restrictable,cloud_restrictable"` - MaximumLoginAttempts *int `access:"authentication_password,write_restrictable,cloud_restrictable"` - GoroutineHealthThreshold *int `access:"write_restrictable,cloud_restrictable"` // telemetry: none - EnableOAuthServiceProvider *bool `access:"integrations_integration_management"` - EnableIncomingWebhooks *bool `access:"integrations_integration_management"` - EnableOutgoingWebhooks *bool `access:"integrations_integration_management"` - EnableCommands *bool `access:"integrations_integration_management"` - EnablePostUsernameOverride *bool `access:"integrations_integration_management"` - EnablePostIconOverride *bool `access:"integrations_integration_management"` - GoogleDeveloperKey *string `access:"site_posts,write_restrictable,cloud_restrictable"` - DEPRECATED_DO_NOT_USE_EnableOnlyAdminIntegrations *bool `json:"EnableOnlyAdminIntegrations" mapstructure:"EnableOnlyAdminIntegrations"` // Deprecated: do not use - EnableLinkPreviews *bool `access:"site_posts"` - RestrictLinkPreviews *string `access:"site_posts"` - EnableTesting *bool `access:"environment_developer,write_restrictable,cloud_restrictable"` - EnableDeveloper *bool `access:"environment_developer,write_restrictable,cloud_restrictable"` - EnableOpenTracing *bool `access:"write_restrictable,cloud_restrictable"` - EnableSecurityFixAlert *bool `access:"environment_smtp,write_restrictable,cloud_restrictable"` - EnableInsecureOutgoingConnections *bool `access:"environment_web_server,write_restrictable,cloud_restrictable"` - AllowedUntrustedInternalConnections *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - EnableMultifactorAuthentication *bool `access:"authentication_mfa"` - EnforceMultifactorAuthentication *bool `access:"authentication_mfa"` - EnableUserAccessTokens *bool `access:"integrations_integration_management"` - AllowCorsFrom *string `access:"integrations_cors,write_restrictable,cloud_restrictable"` - CorsExposedHeaders *string `access:"integrations_cors,write_restrictable,cloud_restrictable"` - CorsAllowCredentials *bool `access:"integrations_cors,write_restrictable,cloud_restrictable"` - CorsDebug *bool `access:"integrations_cors,write_restrictable,cloud_restrictable"` - AllowCookiesForSubdomains *bool `access:"write_restrictable,cloud_restrictable"` - ExtendSessionLengthWithActivity *bool `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - SessionLengthWebInDays *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - SessionLengthMobileInDays *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - SessionLengthSSOInDays *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - SessionCacheInMinutes *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - SessionIdleTimeoutInMinutes *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` - WebsocketSecurePort *int `access:"write_restrictable,cloud_restrictable"` // telemetry: none - WebsocketPort *int `access:"write_restrictable,cloud_restrictable"` // telemetry: none - WebserverMode *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - EnableGifPicker *bool `access:"integrations_gif"` - GfycatApiKey *string `access:"integrations_gif"` - GfycatApiSecret *string `access:"integrations_gif"` - EnableCustomEmoji *bool `access:"site_emoji"` - EnableEmojiPicker *bool `access:"site_emoji"` - DEPRECATED_DO_NOT_USE_RestrictCustomEmojiCreation *string `json:"RestrictCustomEmojiCreation" mapstructure:"RestrictCustomEmojiCreation"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPostDelete *string `json:"RestrictPostDelete" mapstructure:"RestrictPostDelete"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_AllowEditPost *string `json:"AllowEditPost" mapstructure:"AllowEditPost"` // Deprecated: do not use - PostEditTimeLimit *int `access:"user_management_permissions"` - TimeBetweenUserTypingUpdatesMilliseconds *int64 `access:"experimental_features,write_restrictable,cloud_restrictable"` - EnablePostSearch *bool `access:"write_restrictable,cloud_restrictable"` - EnableFileSearch *bool `access:"write_restrictable"` - MinimumHashtagLength *int `access:"environment_database,write_restrictable,cloud_restrictable"` - EnableUserTypingMessages *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - EnableChannelViewedMessages *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - EnableUserStatuses *bool `access:"write_restrictable,cloud_restrictable"` - ExperimentalEnableAuthenticationTransfer *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - ClusterLogTimeoutMilliseconds *int `access:"write_restrictable,cloud_restrictable"` - CloseUnusedDirectMessages *bool `access:"experimental_features"` - EnablePreviewFeatures *bool `access:"experimental_features"` - EnableTutorial *bool `access:"experimental_features"` - ExperimentalEnableDefaultChannelLeaveJoinMessages *bool `access:"experimental_features"` - ExperimentalGroupUnreadChannels *string `access:"experimental_features"` - ExperimentalChannelOrganization *bool `access:"experimental_features"` - DEPRECATED_DO_NOT_USE_ImageProxyType *string `json:"ImageProxyType" mapstructure:"ImageProxyType"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_ImageProxyURL *string `json:"ImageProxyURL" mapstructure:"ImageProxyURL"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_ImageProxyOptions *string `json:"ImageProxyOptions" mapstructure:"ImageProxyOptions"` // Deprecated: do not use - EnableAPITeamDeletion *bool - EnableAPIUserDeletion *bool - ExperimentalEnableHardenedMode *bool `access:"experimental_features"` - DisableLegacyMFA *bool `access:"write_restrictable,cloud_restrictable"` - ExperimentalStrictCSRFEnforcement *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - EnableEmailInvitations *bool `access:"authentication_signup"` - DisableBotsWhenOwnerIsDeactivated *bool `access:"integrations_bot_accounts,write_restrictable,cloud_restrictable"` - EnableBotAccountCreation *bool `access:"integrations_bot_accounts"` - EnableSVGs *bool `access:"site_posts"` - EnableLatex *bool `access:"site_posts"` - EnableAPIChannelDeletion *bool - EnableLocalMode *bool - LocalModeSocketLocation *string // telemetry: none - EnableAWSMetering *bool // telemetry: none - SplitKey *string `access:"experimental_feature_flags,write_restrictable"` // telemetry: none - FeatureFlagSyncIntervalSeconds *int `access:"experimental_feature_flags,write_restrictable"` // telemetry: none - DebugSplit *bool `access:"experimental_feature_flags,write_restrictable"` // telemetry: none - ThreadAutoFollow *bool `access:"experimental_features"` - CollapsedThreads *string `access:"experimental_features"` - ManagedResourcePaths *string `access:"environment_web_server,write_restrictable,cloud_restrictable"` - EnableLegacySidebar *bool `access:"experimental_features"` - EnableReliableWebSockets *bool `access:"experimental_features"` // telemetry: none -} - -func (s *ServiceSettings) SetDefaults(isUpdate bool) { - if s.EnableEmailInvitations == nil { - // If the site URL is also not present then assume this is a clean install - if s.SiteURL == nil { - s.EnableEmailInvitations = NewBool(false) - } else { - s.EnableEmailInvitations = NewBool(true) - } - } - - if s.SiteURL == nil { - if s.EnableDeveloper != nil && *s.EnableDeveloper { - s.SiteURL = NewString(SERVICE_SETTINGS_DEFAULT_SITE_URL) - } else { - s.SiteURL = NewString("") - } - } - - if s.WebsocketURL == nil { - s.WebsocketURL = NewString("") - } - - if s.LicenseFileLocation == nil { - s.LicenseFileLocation = NewString("") - } - - if s.ListenAddress == nil { - s.ListenAddress = NewString(SERVICE_SETTINGS_DEFAULT_LISTEN_AND_ADDRESS) - } - - if s.EnableLinkPreviews == nil { - s.EnableLinkPreviews = NewBool(true) - } - - if s.RestrictLinkPreviews == nil { - s.RestrictLinkPreviews = NewString("") - } - - if s.EnableTesting == nil { - s.EnableTesting = NewBool(false) - } - - if s.EnableDeveloper == nil { - s.EnableDeveloper = NewBool(false) - } - - if s.EnableOpenTracing == nil { - s.EnableOpenTracing = NewBool(false) - } - - if s.EnableSecurityFixAlert == nil { - s.EnableSecurityFixAlert = NewBool(true) - } - - if s.EnableInsecureOutgoingConnections == nil { - s.EnableInsecureOutgoingConnections = NewBool(false) - } - - if s.AllowedUntrustedInternalConnections == nil { - s.AllowedUntrustedInternalConnections = NewString("") - } - - if s.EnableMultifactorAuthentication == nil { - s.EnableMultifactorAuthentication = NewBool(false) - } - - if s.EnforceMultifactorAuthentication == nil { - s.EnforceMultifactorAuthentication = NewBool(false) - } - - if s.EnableUserAccessTokens == nil { - s.EnableUserAccessTokens = NewBool(false) - } - - if s.GoroutineHealthThreshold == nil { - s.GoroutineHealthThreshold = NewInt(-1) - } - - if s.GoogleDeveloperKey == nil { - s.GoogleDeveloperKey = NewString("") - } - - if s.EnableOAuthServiceProvider == nil { - s.EnableOAuthServiceProvider = NewBool(false) - } - - if s.EnableIncomingWebhooks == nil { - s.EnableIncomingWebhooks = NewBool(true) - } - - if s.EnableOutgoingWebhooks == nil { - s.EnableOutgoingWebhooks = NewBool(true) - } - - if s.ConnectionSecurity == nil { - s.ConnectionSecurity = NewString("") - } - - if s.TLSKeyFile == nil { - s.TLSKeyFile = NewString(SERVICE_SETTINGS_DEFAULT_TLS_KEY_FILE) - } - - if s.TLSCertFile == nil { - s.TLSCertFile = NewString(SERVICE_SETTINGS_DEFAULT_TLS_CERT_FILE) - } - - if s.TLSMinVer == nil { - s.TLSMinVer = NewString("1.2") - } - - if s.TLSStrictTransport == nil { - s.TLSStrictTransport = NewBool(false) - } - - if s.TLSStrictTransportMaxAge == nil { - s.TLSStrictTransportMaxAge = NewInt64(63072000) - } - - if s.TLSOverwriteCiphers == nil { - s.TLSOverwriteCiphers = []string{} - } - - if s.UseLetsEncrypt == nil { - s.UseLetsEncrypt = NewBool(false) - } - - if s.LetsEncryptCertificateCacheFile == nil { - s.LetsEncryptCertificateCacheFile = NewString("./config/letsencrypt.cache") - } - - if s.ReadTimeout == nil { - s.ReadTimeout = NewInt(SERVICE_SETTINGS_DEFAULT_READ_TIMEOUT) - } - - if s.WriteTimeout == nil { - s.WriteTimeout = NewInt(SERVICE_SETTINGS_DEFAULT_WRITE_TIMEOUT) - } - - if s.IdleTimeout == nil { - s.IdleTimeout = NewInt(SERVICE_SETTINGS_DEFAULT_IDLE_TIMEOUT) - } - - if s.MaximumLoginAttempts == nil { - s.MaximumLoginAttempts = NewInt(SERVICE_SETTINGS_DEFAULT_MAX_LOGIN_ATTEMPTS) - } - - if s.Forward80To443 == nil { - s.Forward80To443 = NewBool(false) - } - - if isUpdate { - // When updating an existing configuration, ensure that defaults are set. - if s.TrustedProxyIPHeader == nil { - s.TrustedProxyIPHeader = []string{HEADER_FORWARDED, HEADER_REAL_IP} - } - } else { - // When generating a blank configuration, leave the list empty. - s.TrustedProxyIPHeader = []string{} - } - - if s.TimeBetweenUserTypingUpdatesMilliseconds == nil { - s.TimeBetweenUserTypingUpdatesMilliseconds = NewInt64(5000) - } - - if s.EnablePostSearch == nil { - s.EnablePostSearch = NewBool(true) - } - - if s.EnableFileSearch == nil { - s.EnableFileSearch = NewBool(true) - } - - if s.MinimumHashtagLength == nil { - s.MinimumHashtagLength = NewInt(3) - } - - if s.EnableUserTypingMessages == nil { - s.EnableUserTypingMessages = NewBool(true) - } - - if s.EnableChannelViewedMessages == nil { - s.EnableChannelViewedMessages = NewBool(true) - } - - if s.EnableUserStatuses == nil { - s.EnableUserStatuses = NewBool(true) - } - - if s.ClusterLogTimeoutMilliseconds == nil { - s.ClusterLogTimeoutMilliseconds = NewInt(2000) - } - - if s.CloseUnusedDirectMessages == nil { - s.CloseUnusedDirectMessages = NewBool(false) - } - - if s.EnableTutorial == nil { - s.EnableTutorial = NewBool(true) - } - - // Must be manually enabled for existing installations. - if s.ExtendSessionLengthWithActivity == nil { - s.ExtendSessionLengthWithActivity = NewBool(!isUpdate) - } - - if s.SessionLengthWebInDays == nil { - if isUpdate { - s.SessionLengthWebInDays = NewInt(180) - } else { - s.SessionLengthWebInDays = NewInt(30) - } - } - - if s.SessionLengthMobileInDays == nil { - if isUpdate { - s.SessionLengthMobileInDays = NewInt(180) - } else { - s.SessionLengthMobileInDays = NewInt(30) - } - } - - if s.SessionLengthSSOInDays == nil { - s.SessionLengthSSOInDays = NewInt(30) - } - - if s.SessionCacheInMinutes == nil { - s.SessionCacheInMinutes = NewInt(10) - } - - if s.SessionIdleTimeoutInMinutes == nil { - s.SessionIdleTimeoutInMinutes = NewInt(43200) - } - - if s.EnableCommands == nil { - s.EnableCommands = NewBool(true) - } - - if s.DEPRECATED_DO_NOT_USE_EnableOnlyAdminIntegrations == nil { - s.DEPRECATED_DO_NOT_USE_EnableOnlyAdminIntegrations = NewBool(true) - } - - if s.EnablePostUsernameOverride == nil { - s.EnablePostUsernameOverride = NewBool(false) - } - - if s.EnablePostIconOverride == nil { - s.EnablePostIconOverride = NewBool(false) - } - - if s.WebsocketPort == nil { - s.WebsocketPort = NewInt(80) - } - - if s.WebsocketSecurePort == nil { - s.WebsocketSecurePort = NewInt(443) - } - - if s.AllowCorsFrom == nil { - s.AllowCorsFrom = NewString(SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM) - } - - if s.CorsExposedHeaders == nil { - s.CorsExposedHeaders = NewString("") - } - - if s.CorsAllowCredentials == nil { - s.CorsAllowCredentials = NewBool(false) - } - - if s.CorsDebug == nil { - s.CorsDebug = NewBool(false) - } - - if s.AllowCookiesForSubdomains == nil { - s.AllowCookiesForSubdomains = NewBool(false) - } - - if s.WebserverMode == nil { - s.WebserverMode = NewString("gzip") - } else if *s.WebserverMode == "regular" { - *s.WebserverMode = "gzip" - } - - if s.EnableCustomEmoji == nil { - s.EnableCustomEmoji = NewBool(true) - } - - if s.EnableEmojiPicker == nil { - s.EnableEmojiPicker = NewBool(true) - } - - if s.EnableGifPicker == nil { - s.EnableGifPicker = NewBool(true) - } - - if s.GfycatApiKey == nil || *s.GfycatApiKey == "" { - s.GfycatApiKey = NewString(SERVICE_SETTINGS_DEFAULT_GFYCAT_API_KEY) - } - - if s.GfycatApiSecret == nil || *s.GfycatApiSecret == "" { - s.GfycatApiSecret = NewString(SERVICE_SETTINGS_DEFAULT_GFYCAT_API_SECRET) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictCustomEmojiCreation == nil { - s.DEPRECATED_DO_NOT_USE_RestrictCustomEmojiCreation = NewString(RESTRICT_EMOJI_CREATION_ALL) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPostDelete == nil { - s.DEPRECATED_DO_NOT_USE_RestrictPostDelete = NewString(PERMISSIONS_DELETE_POST_ALL) - } - - if s.DEPRECATED_DO_NOT_USE_AllowEditPost == nil { - s.DEPRECATED_DO_NOT_USE_AllowEditPost = NewString(ALLOW_EDIT_POST_ALWAYS) - } - - if s.ExperimentalEnableAuthenticationTransfer == nil { - s.ExperimentalEnableAuthenticationTransfer = NewBool(true) - } - - if s.PostEditTimeLimit == nil { - s.PostEditTimeLimit = NewInt(-1) - } - - if s.EnablePreviewFeatures == nil { - s.EnablePreviewFeatures = NewBool(true) - } - - if s.ExperimentalEnableDefaultChannelLeaveJoinMessages == nil { - s.ExperimentalEnableDefaultChannelLeaveJoinMessages = NewBool(true) - } - - if s.ExperimentalGroupUnreadChannels == nil { - s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DISABLED) - } else if *s.ExperimentalGroupUnreadChannels == "0" { - s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DISABLED) - } else if *s.ExperimentalGroupUnreadChannels == "1" { - s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DEFAULT_ON) - } - - if s.ExperimentalChannelOrganization == nil { - experimentalUnreadEnabled := *s.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DISABLED - s.ExperimentalChannelOrganization = NewBool(experimentalUnreadEnabled) - } - - if s.DEPRECATED_DO_NOT_USE_ImageProxyType == nil { - s.DEPRECATED_DO_NOT_USE_ImageProxyType = NewString("") - } - - if s.DEPRECATED_DO_NOT_USE_ImageProxyURL == nil { - s.DEPRECATED_DO_NOT_USE_ImageProxyURL = NewString("") - } - - if s.DEPRECATED_DO_NOT_USE_ImageProxyOptions == nil { - s.DEPRECATED_DO_NOT_USE_ImageProxyOptions = NewString("") - } - - if s.EnableAPITeamDeletion == nil { - s.EnableAPITeamDeletion = NewBool(false) - } - - if s.EnableAPIUserDeletion == nil { - s.EnableAPIUserDeletion = NewBool(false) - } - - if s.EnableAPIChannelDeletion == nil { - s.EnableAPIChannelDeletion = NewBool(false) - } - - if s.ExperimentalEnableHardenedMode == nil { - s.ExperimentalEnableHardenedMode = NewBool(false) - } - - if s.DisableLegacyMFA == nil { - s.DisableLegacyMFA = NewBool(!isUpdate) - } - - if s.ExperimentalStrictCSRFEnforcement == nil { - s.ExperimentalStrictCSRFEnforcement = NewBool(false) - } - - if s.DisableBotsWhenOwnerIsDeactivated == nil { - s.DisableBotsWhenOwnerIsDeactivated = NewBool(true) - } - - if s.EnableBotAccountCreation == nil { - s.EnableBotAccountCreation = NewBool(false) - } - - if s.EnableSVGs == nil { - if isUpdate { - s.EnableSVGs = NewBool(true) - } else { - s.EnableSVGs = NewBool(false) - } - } - - if s.EnableLatex == nil { - if isUpdate { - s.EnableLatex = NewBool(true) - } else { - s.EnableLatex = NewBool(false) - } - } - - if s.EnableLocalMode == nil { - s.EnableLocalMode = NewBool(false) - } - - if s.LocalModeSocketLocation == nil { - s.LocalModeSocketLocation = NewString(LOCAL_MODE_SOCKET_PATH) - } - - if s.EnableAWSMetering == nil { - s.EnableAWSMetering = NewBool(false) - } - - if s.SplitKey == nil { - s.SplitKey = NewString("") - } - - if s.FeatureFlagSyncIntervalSeconds == nil { - s.FeatureFlagSyncIntervalSeconds = NewInt(30) - } - - if s.DebugSplit == nil { - s.DebugSplit = NewBool(false) - } - - if s.ThreadAutoFollow == nil { - s.ThreadAutoFollow = NewBool(true) - } - - if s.CollapsedThreads == nil { - s.CollapsedThreads = NewString(COLLAPSED_THREADS_DISABLED) - } - - if s.ManagedResourcePaths == nil { - s.ManagedResourcePaths = NewString("") - } - - if s.EnableLegacySidebar == nil { - s.EnableLegacySidebar = NewBool(false) - } - - if s.EnableReliableWebSockets == nil { - s.EnableReliableWebSockets = NewBool(true) - } -} - -type ClusterSettings struct { - Enable *bool `access:"environment_high_availability,write_restrictable"` - ClusterName *string `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - OverrideHostname *string `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - NetworkInterface *string `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - BindAddress *string `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - AdvertiseAddress *string `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - UseIpAddress *bool `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - DEPRECATED_DO_NOT_USE_UseExperimentalGossip *bool `json:"UseExperimentalGossip" access:"environment_high_availability,write_restrictable,cloud_restrictable"` // Deprecated: do not use - EnableGossipCompression *bool `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - EnableExperimentalGossipEncryption *bool `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - ReadOnlyConfig *bool `access:"environment_high_availability,write_restrictable,cloud_restrictable"` - GossipPort *int `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - StreamingPort *int `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - MaxIdleConns *int `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - MaxIdleConnsPerHost *int `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none - IdleConnTimeoutMilliseconds *int `access:"environment_high_availability,write_restrictable,cloud_restrictable"` // telemetry: none -} - -func (s *ClusterSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.ClusterName == nil { - s.ClusterName = NewString("") - } - - if s.OverrideHostname == nil { - s.OverrideHostname = NewString("") - } - - if s.NetworkInterface == nil { - s.NetworkInterface = NewString("") - } - - if s.BindAddress == nil { - s.BindAddress = NewString("") - } - - if s.AdvertiseAddress == nil { - s.AdvertiseAddress = NewString("") - } - - if s.UseIpAddress == nil { - s.UseIpAddress = NewBool(true) - } - - if s.DEPRECATED_DO_NOT_USE_UseExperimentalGossip == nil { - s.DEPRECATED_DO_NOT_USE_UseExperimentalGossip = NewBool(true) - } - - if s.EnableExperimentalGossipEncryption == nil { - s.EnableExperimentalGossipEncryption = NewBool(false) - } - - if s.EnableGossipCompression == nil { - s.EnableGossipCompression = NewBool(true) - } - - if s.ReadOnlyConfig == nil { - s.ReadOnlyConfig = NewBool(true) - } - - if s.GossipPort == nil { - s.GossipPort = NewInt(8074) - } - - if s.StreamingPort == nil { - s.StreamingPort = NewInt(8075) - } - - if s.MaxIdleConns == nil { - s.MaxIdleConns = NewInt(100) - } - - if s.MaxIdleConnsPerHost == nil { - s.MaxIdleConnsPerHost = NewInt(128) - } - - if s.IdleConnTimeoutMilliseconds == nil { - s.IdleConnTimeoutMilliseconds = NewInt(90000) - } -} - -type MetricsSettings struct { - Enable *bool `access:"environment_performance_monitoring,write_restrictable,cloud_restrictable"` - BlockProfileRate *int `access:"environment_performance_monitoring,write_restrictable,cloud_restrictable"` - ListenAddress *string `access:"environment_performance_monitoring,write_restrictable,cloud_restrictable"` // telemetry: none -} - -func (s *MetricsSettings) SetDefaults() { - if s.ListenAddress == nil { - s.ListenAddress = NewString(":8067") - } - - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.BlockProfileRate == nil { - s.BlockProfileRate = NewInt(0) - } -} - -type ExperimentalSettings struct { - ClientSideCertEnable *bool `access:"experimental_features,cloud_restrictable"` - ClientSideCertCheck *string `access:"experimental_features,cloud_restrictable"` - EnableClickToReply *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - LinkMetadataTimeoutMilliseconds *int64 `access:"experimental_features,write_restrictable,cloud_restrictable"` - RestrictSystemAdmin *bool `access:"experimental_features,write_restrictable"` - UseNewSAMLLibrary *bool `access:"experimental_features,cloud_restrictable"` - CloudUserLimit *int64 `access:"experimental_features,write_restrictable"` - CloudBilling *bool `access:"experimental_features,write_restrictable"` - EnableSharedChannels *bool `access:"experimental_features"` - EnableRemoteClusterService *bool `access:"experimental_features"` -} - -func (s *ExperimentalSettings) SetDefaults() { - if s.ClientSideCertEnable == nil { - s.ClientSideCertEnable = NewBool(false) - } - - if s.ClientSideCertCheck == nil { - s.ClientSideCertCheck = NewString(CLIENT_SIDE_CERT_CHECK_SECONDARY_AUTH) - } - - if s.EnableClickToReply == nil { - s.EnableClickToReply = NewBool(false) - } - - if s.LinkMetadataTimeoutMilliseconds == nil { - s.LinkMetadataTimeoutMilliseconds = NewInt64(EXPERIMENTAL_SETTINGS_DEFAULT_LINK_METADATA_TIMEOUT_MILLISECONDS) - } - - if s.RestrictSystemAdmin == nil { - s.RestrictSystemAdmin = NewBool(false) - } - - if s.CloudUserLimit == nil { - // User limit 0 is treated as no limit - s.CloudUserLimit = NewInt64(0) - } - - if s.CloudBilling == nil { - s.CloudBilling = NewBool(false) - } - - if s.UseNewSAMLLibrary == nil { - s.UseNewSAMLLibrary = NewBool(false) - } - - if s.EnableSharedChannels == nil { - s.EnableSharedChannels = NewBool(false) - } - - if s.EnableRemoteClusterService == nil { - s.EnableRemoteClusterService = NewBool(false) - } -} - -type AnalyticsSettings struct { - MaxUsersForStatistics *int `access:"write_restrictable,cloud_restrictable"` -} - -func (s *AnalyticsSettings) SetDefaults() { - if s.MaxUsersForStatistics == nil { - s.MaxUsersForStatistics = NewInt(ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS) - } -} - -type SSOSettings struct { - Enable *bool `access:"authentication_openid"` - Secret *string `access:"authentication_openid"` // telemetry: none - Id *string `access:"authentication_openid"` // telemetry: none - Scope *string `access:"authentication_openid"` // telemetry: none - AuthEndpoint *string `access:"authentication_openid"` // telemetry: none - TokenEndpoint *string `access:"authentication_openid"` // telemetry: none - UserApiEndpoint *string `access:"authentication_openid"` // telemetry: none - DiscoveryEndpoint *string `access:"authentication_openid"` // telemetry: none - ButtonText *string `access:"authentication_openid"` // telemetry: none - ButtonColor *string `access:"authentication_openid"` // telemetry: none -} - -func (s *SSOSettings) setDefaults(scope, authEndpoint, tokenEndpoint, userApiEndpoint, buttonColor string) { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.Secret == nil { - s.Secret = NewString("") - } - - if s.Id == nil { - s.Id = NewString("") - } - - if s.Scope == nil { - s.Scope = NewString(scope) - } - - if s.DiscoveryEndpoint == nil { - s.DiscoveryEndpoint = NewString("") - } - - if s.AuthEndpoint == nil { - s.AuthEndpoint = NewString(authEndpoint) - } - - if s.TokenEndpoint == nil { - s.TokenEndpoint = NewString(tokenEndpoint) - } - - if s.UserApiEndpoint == nil { - s.UserApiEndpoint = NewString(userApiEndpoint) - } - - if s.ButtonText == nil { - s.ButtonText = NewString("") - } - - if s.ButtonColor == nil { - s.ButtonColor = NewString(buttonColor) - } -} - -type Office365Settings struct { - Enable *bool `access:"authentication_openid"` - Secret *string `access:"authentication_openid"` // telemetry: none - Id *string `access:"authentication_openid"` // telemetry: none - Scope *string `access:"authentication_openid"` - AuthEndpoint *string `access:"authentication_openid"` // telemetry: none - TokenEndpoint *string `access:"authentication_openid"` // telemetry: none - UserApiEndpoint *string `access:"authentication_openid"` // telemetry: none - DiscoveryEndpoint *string `access:"authentication_openid"` // telemetry: none - DirectoryId *string `access:"authentication_openid"` // telemetry: none -} - -func (s *Office365Settings) setDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.Id == nil { - s.Id = NewString("") - } - - if s.Secret == nil { - s.Secret = NewString("") - } - - if s.Scope == nil { - s.Scope = NewString(OFFICE365_SETTINGS_DEFAULT_SCOPE) - } - - if s.DiscoveryEndpoint == nil { - s.DiscoveryEndpoint = NewString("") - } - - if s.AuthEndpoint == nil { - s.AuthEndpoint = NewString(OFFICE365_SETTINGS_DEFAULT_AUTH_ENDPOINT) - } - - if s.TokenEndpoint == nil { - s.TokenEndpoint = NewString(OFFICE365_SETTINGS_DEFAULT_TOKEN_ENDPOINT) - } - - if s.UserApiEndpoint == nil { - s.UserApiEndpoint = NewString(OFFICE365_SETTINGS_DEFAULT_USER_API_ENDPOINT) - } - - if s.DirectoryId == nil { - s.DirectoryId = NewString("") - } -} - -func (s *Office365Settings) SSOSettings() *SSOSettings { - ssoSettings := SSOSettings{} - ssoSettings.Enable = s.Enable - ssoSettings.Secret = s.Secret - ssoSettings.Id = s.Id - ssoSettings.Scope = s.Scope - ssoSettings.DiscoveryEndpoint = s.DiscoveryEndpoint - ssoSettings.AuthEndpoint = s.AuthEndpoint - ssoSettings.TokenEndpoint = s.TokenEndpoint - ssoSettings.UserApiEndpoint = s.UserApiEndpoint - return &ssoSettings -} - -type ReplicaLagSettings struct { - DataSource *string `access:"environment,write_restrictable,cloud_restrictable"` // telemetry: none - QueryAbsoluteLag *string `access:"environment,write_restrictable,cloud_restrictable"` // telemetry: none - QueryTimeLag *string `access:"environment,write_restrictable,cloud_restrictable"` // telemetry: none -} - -type SqlSettings struct { - DriverName *string `access:"environment_database,write_restrictable,cloud_restrictable"` - DataSource *string `access:"environment_database,write_restrictable,cloud_restrictable"` // telemetry: none - DataSourceReplicas []string `access:"environment_database,write_restrictable,cloud_restrictable"` - DataSourceSearchReplicas []string `access:"environment_database,write_restrictable,cloud_restrictable"` - MaxIdleConns *int `access:"environment_database,write_restrictable,cloud_restrictable"` - ConnMaxLifetimeMilliseconds *int `access:"environment_database,write_restrictable,cloud_restrictable"` - ConnMaxIdleTimeMilliseconds *int `access:"environment_database,write_restrictable,cloud_restrictable"` - MaxOpenConns *int `access:"environment_database,write_restrictable,cloud_restrictable"` - Trace *bool `access:"environment_database,write_restrictable,cloud_restrictable"` - AtRestEncryptKey *string `access:"environment_database,write_restrictable,cloud_restrictable"` // telemetry: none - QueryTimeout *int `access:"environment_database,write_restrictable,cloud_restrictable"` - DisableDatabaseSearch *bool `access:"environment_database,write_restrictable,cloud_restrictable"` - ReplicaLagSettings []*ReplicaLagSettings `access:"environment_database,write_restrictable,cloud_restrictable"` // telemetry: none -} - -func (s *SqlSettings) SetDefaults(isUpdate bool) { - if s.DriverName == nil { - s.DriverName = NewString(DATABASE_DRIVER_POSTGRES) - } - - if s.DataSource == nil { - s.DataSource = NewString(SQL_SETTINGS_DEFAULT_DATA_SOURCE) - } - - if s.DataSourceReplicas == nil { - s.DataSourceReplicas = []string{} - } - - if s.DataSourceSearchReplicas == nil { - s.DataSourceSearchReplicas = []string{} - } - - if isUpdate { - // When updating an existing configuration, ensure an encryption key has been specified. - if s.AtRestEncryptKey == nil || *s.AtRestEncryptKey == "" { - s.AtRestEncryptKey = NewString(NewRandomString(32)) - } - } else { - // When generating a blank configuration, leave this key empty to be generated on server start. - s.AtRestEncryptKey = NewString("") - } - - if s.MaxIdleConns == nil { - s.MaxIdleConns = NewInt(20) - } - - if s.MaxOpenConns == nil { - s.MaxOpenConns = NewInt(300) - } - - if s.ConnMaxLifetimeMilliseconds == nil { - s.ConnMaxLifetimeMilliseconds = NewInt(3600000) - } - - if s.ConnMaxIdleTimeMilliseconds == nil { - s.ConnMaxIdleTimeMilliseconds = NewInt(300000) - } - - if s.Trace == nil { - s.Trace = NewBool(false) - } - - if s.QueryTimeout == nil { - s.QueryTimeout = NewInt(30) - } - - if s.DisableDatabaseSearch == nil { - s.DisableDatabaseSearch = NewBool(false) - } - - if s.ReplicaLagSettings == nil { - s.ReplicaLagSettings = []*ReplicaLagSettings{} - } -} - -type LogSettings struct { - EnableConsole *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` - ConsoleLevel *string `access:"environment_logging,write_restrictable,cloud_restrictable"` - ConsoleJson *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` - EnableColor *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` // telemetry: none - EnableFile *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` - FileLevel *string `access:"environment_logging,write_restrictable,cloud_restrictable"` - FileJson *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` - FileLocation *string `access:"environment_logging,write_restrictable,cloud_restrictable"` - EnableWebhookDebugging *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` - EnableDiagnostics *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` // telemetry: none - EnableSentry *bool `access:"environment_logging,write_restrictable,cloud_restrictable"` // telemetry: none - AdvancedLoggingConfig *string `access:"environment_logging,write_restrictable,cloud_restrictable"` -} - -func (s *LogSettings) SetDefaults() { - if s.EnableConsole == nil { - s.EnableConsole = NewBool(true) - } - - if s.ConsoleLevel == nil { - s.ConsoleLevel = NewString("DEBUG") - } - - if s.EnableColor == nil { - s.EnableColor = NewBool(false) - } - - if s.EnableFile == nil { - s.EnableFile = NewBool(true) - } - - if s.FileLevel == nil { - s.FileLevel = NewString("INFO") - } - - if s.FileLocation == nil { - s.FileLocation = NewString("") - } - - if s.EnableWebhookDebugging == nil { - s.EnableWebhookDebugging = NewBool(true) - } - - if s.EnableDiagnostics == nil { - s.EnableDiagnostics = NewBool(true) - } - - if s.EnableSentry == nil { - s.EnableSentry = NewBool(*s.EnableDiagnostics) - } - - if s.ConsoleJson == nil { - s.ConsoleJson = NewBool(true) - } - - if s.FileJson == nil { - s.FileJson = NewBool(true) - } - - if s.AdvancedLoggingConfig == nil { - s.AdvancedLoggingConfig = NewString("") - } -} - -type ExperimentalAuditSettings struct { - FileEnabled *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - FileName *string `access:"experimental_features,write_restrictable,cloud_restrictable"` // telemetry: none - FileMaxSizeMB *int `access:"experimental_features,write_restrictable,cloud_restrictable"` - FileMaxAgeDays *int `access:"experimental_features,write_restrictable,cloud_restrictable"` - FileMaxBackups *int `access:"experimental_features,write_restrictable,cloud_restrictable"` - FileCompress *bool `access:"experimental_features,write_restrictable,cloud_restrictable"` - FileMaxQueueSize *int `access:"experimental_features,write_restrictable,cloud_restrictable"` - AdvancedLoggingConfig *string `access:"experimental_features,write_restrictable,cloud_restrictable"` -} - -func (s *ExperimentalAuditSettings) SetDefaults() { - if s.FileEnabled == nil { - s.FileEnabled = NewBool(false) - } - - if s.FileName == nil { - s.FileName = NewString("") - } - - if s.FileMaxSizeMB == nil { - s.FileMaxSizeMB = NewInt(100) - } - - if s.FileMaxAgeDays == nil { - s.FileMaxAgeDays = NewInt(0) // no limit on age - } - - if s.FileMaxBackups == nil { // no limit on number of backups - s.FileMaxBackups = NewInt(0) - } - - if s.FileCompress == nil { - s.FileCompress = NewBool(false) - } - - if s.FileMaxQueueSize == nil { - s.FileMaxQueueSize = NewInt(1000) - } - - if s.AdvancedLoggingConfig == nil { - s.AdvancedLoggingConfig = NewString("") - } -} - -type NotificationLogSettings struct { - EnableConsole *bool `access:"write_restrictable,cloud_restrictable"` - ConsoleLevel *string `access:"write_restrictable,cloud_restrictable"` - ConsoleJson *bool `access:"write_restrictable,cloud_restrictable"` - EnableColor *bool `access:"write_restrictable,cloud_restrictable"` // telemetry: none - EnableFile *bool `access:"write_restrictable,cloud_restrictable"` - FileLevel *string `access:"write_restrictable,cloud_restrictable"` - FileJson *bool `access:"write_restrictable,cloud_restrictable"` - FileLocation *string `access:"write_restrictable,cloud_restrictable"` - AdvancedLoggingConfig *string `access:"write_restrictable,cloud_restrictable"` -} - -func (s *NotificationLogSettings) SetDefaults() { - if s.EnableConsole == nil { - s.EnableConsole = NewBool(true) - } - - if s.ConsoleLevel == nil { - s.ConsoleLevel = NewString("DEBUG") - } - - if s.EnableFile == nil { - s.EnableFile = NewBool(true) - } - - if s.FileLevel == nil { - s.FileLevel = NewString("INFO") - } - - if s.FileLocation == nil { - s.FileLocation = NewString("") - } - - if s.ConsoleJson == nil { - s.ConsoleJson = NewBool(true) - } - - if s.EnableColor == nil { - s.EnableColor = NewBool(false) - } - - if s.FileJson == nil { - s.FileJson = NewBool(true) - } - - if s.AdvancedLoggingConfig == nil { - s.AdvancedLoggingConfig = NewString("") - } -} - -type PasswordSettings struct { - MinimumLength *int `access:"authentication_password"` - Lowercase *bool `access:"authentication_password"` - Number *bool `access:"authentication_password"` - Uppercase *bool `access:"authentication_password"` - Symbol *bool `access:"authentication_password"` -} - -func (s *PasswordSettings) SetDefaults() { - if s.MinimumLength == nil { - s.MinimumLength = NewInt(10) - } - - if s.Lowercase == nil { - s.Lowercase = NewBool(true) - } - - if s.Number == nil { - s.Number = NewBool(true) - } - - if s.Uppercase == nil { - s.Uppercase = NewBool(true) - } - - if s.Symbol == nil { - s.Symbol = NewBool(true) - } -} - -type FileSettings struct { - EnableFileAttachments *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"` - EnableMobileUpload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"` - EnableMobileDownload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"` - MaxFileSize *int64 `access:"environment_file_storage,cloud_restrictable"` - DriverName *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` - Directory *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` - EnablePublicLink *bool `access:"site_public_links,cloud_restrictable"` - ExtractContent *bool `access:"environment_file_storage,write_restrictable"` - ArchiveRecursion *bool `access:"environment_file_storage,write_restrictable"` - PublicLinkSalt *string `access:"site_public_links,cloud_restrictable"` // telemetry: none - InitialFont *string `access:"environment_file_storage,cloud_restrictable"` // telemetry: none - AmazonS3AccessKeyId *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3SecretAccessKey *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3Bucket *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3PathPrefix *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3Region *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3Endpoint *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` // telemetry: none - AmazonS3SSL *bool `access:"environment_file_storage,write_restrictable,cloud_restrictable"` - AmazonS3SignV2 *bool `access:"environment_file_storage,write_restrictable,cloud_restrictable"` - AmazonS3SSE *bool `access:"environment_file_storage,write_restrictable,cloud_restrictable"` - AmazonS3Trace *bool `access:"environment_file_storage,write_restrictable,cloud_restrictable"` -} - -func (s *FileSettings) SetDefaults(isUpdate bool) { - if s.EnableFileAttachments == nil { - s.EnableFileAttachments = NewBool(true) - } - - if s.EnableMobileUpload == nil { - s.EnableMobileUpload = NewBool(true) - } - - if s.EnableMobileDownload == nil { - s.EnableMobileDownload = NewBool(true) - } - - if s.MaxFileSize == nil { - s.MaxFileSize = NewInt64(MB * 100) - } - - if s.DriverName == nil { - s.DriverName = NewString(IMAGE_DRIVER_LOCAL) - } - - if s.Directory == nil || *s.Directory == "" { - s.Directory = NewString(FILE_SETTINGS_DEFAULT_DIRECTORY) - } - - if s.EnablePublicLink == nil { - s.EnablePublicLink = NewBool(false) - } - - if s.ExtractContent == nil { - s.ExtractContent = NewBool(true) - } - - if s.ArchiveRecursion == nil { - s.ArchiveRecursion = NewBool(false) - } - - if isUpdate { - // When updating an existing configuration, ensure link salt has been specified. - if s.PublicLinkSalt == nil || *s.PublicLinkSalt == "" { - s.PublicLinkSalt = NewString(NewRandomString(32)) - } - } else { - // When generating a blank configuration, leave link salt empty to be generated on server start. - s.PublicLinkSalt = NewString("") - } - - if s.InitialFont == nil { - // Defaults to "nunito-bold.ttf" - s.InitialFont = NewString("nunito-bold.ttf") - } - - if s.AmazonS3AccessKeyId == nil { - s.AmazonS3AccessKeyId = NewString("") - } - - if s.AmazonS3SecretAccessKey == nil { - s.AmazonS3SecretAccessKey = NewString("") - } - - if s.AmazonS3Bucket == nil { - s.AmazonS3Bucket = NewString("") - } - - if s.AmazonS3PathPrefix == nil { - s.AmazonS3PathPrefix = NewString("") - } - - if s.AmazonS3Region == nil { - s.AmazonS3Region = NewString("") - } - - if s.AmazonS3Endpoint == nil || *s.AmazonS3Endpoint == "" { - // Defaults to "s3.amazonaws.com" - s.AmazonS3Endpoint = NewString("s3.amazonaws.com") - } - - if s.AmazonS3SSL == nil { - s.AmazonS3SSL = NewBool(true) // Secure by default. - } - - if s.AmazonS3SignV2 == nil { - s.AmazonS3SignV2 = new(bool) - // Signature v2 is not enabled by default. - } - - if s.AmazonS3SSE == nil { - s.AmazonS3SSE = NewBool(false) // Not Encrypted by default. - } - - if s.AmazonS3Trace == nil { - s.AmazonS3Trace = NewBool(false) - } -} - -func (s *FileSettings) ToFileBackendSettings(enableComplianceFeature bool) filestore.FileBackendSettings { - if *s.DriverName == IMAGE_DRIVER_LOCAL { - return filestore.FileBackendSettings{ - DriverName: *s.DriverName, - Directory: *s.Directory, - } - } - return filestore.FileBackendSettings{ - DriverName: *s.DriverName, - AmazonS3AccessKeyId: *s.AmazonS3AccessKeyId, - AmazonS3SecretAccessKey: *s.AmazonS3SecretAccessKey, - AmazonS3Bucket: *s.AmazonS3Bucket, - AmazonS3PathPrefix: *s.AmazonS3PathPrefix, - AmazonS3Region: *s.AmazonS3Region, - AmazonS3Endpoint: *s.AmazonS3Endpoint, - AmazonS3SSL: s.AmazonS3SSL == nil || *s.AmazonS3SSL, - AmazonS3SignV2: s.AmazonS3SignV2 != nil && *s.AmazonS3SignV2, - AmazonS3SSE: s.AmazonS3SSE != nil && *s.AmazonS3SSE && enableComplianceFeature, - AmazonS3Trace: s.AmazonS3Trace != nil && *s.AmazonS3Trace, - } -} - -type EmailSettings struct { - EnableSignUpWithEmail *bool `access:"authentication_email"` - EnableSignInWithEmail *bool `access:"authentication_email"` - EnableSignInWithUsername *bool `access:"authentication_email"` - SendEmailNotifications *bool `access:"site_notifications"` - UseChannelInEmailNotifications *bool `access:"experimental_features"` - RequireEmailVerification *bool `access:"authentication_email"` - FeedbackName *string `access:"site_notifications"` - FeedbackEmail *string `access:"site_notifications,cloud_restrictable"` - ReplyToAddress *string `access:"site_notifications,cloud_restrictable"` - FeedbackOrganization *string `access:"site_notifications"` - EnableSMTPAuth *bool `access:"environment_smtp,write_restrictable,cloud_restrictable"` - SMTPUsername *string `access:"environment_smtp,write_restrictable,cloud_restrictable"` // telemetry: none - SMTPPassword *string `access:"environment_smtp,write_restrictable,cloud_restrictable"` // telemetry: none - SMTPServer *string `access:"environment_smtp,write_restrictable,cloud_restrictable"` // telemetry: none - SMTPPort *string `access:"environment_smtp,write_restrictable,cloud_restrictable"` // telemetry: none - SMTPServerTimeout *int `access:"cloud_restrictable"` - ConnectionSecurity *string `access:"environment_smtp,write_restrictable,cloud_restrictable"` - SendPushNotifications *bool `access:"environment_push_notification_server"` - PushNotificationServer *string `access:"environment_push_notification_server"` // telemetry: none - PushNotificationContents *string `access:"site_notifications"` - PushNotificationBuffer *int // telemetry: none - EnableEmailBatching *bool `access:"site_notifications"` - EmailBatchingBufferSize *int `access:"experimental_features"` - EmailBatchingInterval *int `access:"experimental_features"` - EnablePreviewModeBanner *bool `access:"site_notifications"` - SkipServerCertificateVerification *bool `access:"environment_smtp,write_restrictable,cloud_restrictable"` - EmailNotificationContentsType *string `access:"site_notifications"` - LoginButtonColor *string `access:"experimental_features"` - LoginButtonBorderColor *string `access:"experimental_features"` - LoginButtonTextColor *string `access:"experimental_features"` -} - -func (s *EmailSettings) SetDefaults(isUpdate bool) { - if s.EnableSignUpWithEmail == nil { - s.EnableSignUpWithEmail = NewBool(true) - } - - if s.EnableSignInWithEmail == nil { - s.EnableSignInWithEmail = NewBool(*s.EnableSignUpWithEmail) - } - - if s.EnableSignInWithUsername == nil { - s.EnableSignInWithUsername = NewBool(true) - } - - if s.SendEmailNotifications == nil { - s.SendEmailNotifications = NewBool(true) - } - - if s.UseChannelInEmailNotifications == nil { - s.UseChannelInEmailNotifications = NewBool(false) - } - - if s.RequireEmailVerification == nil { - s.RequireEmailVerification = NewBool(false) - } - - if s.FeedbackName == nil { - s.FeedbackName = NewString("") - } - - if s.FeedbackEmail == nil { - s.FeedbackEmail = NewString("test@example.com") - } - - if s.ReplyToAddress == nil { - s.ReplyToAddress = NewString("test@example.com") - } - - if s.FeedbackOrganization == nil { - s.FeedbackOrganization = NewString(EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION) - } - - if s.EnableSMTPAuth == nil { - if s.ConnectionSecurity == nil || *s.ConnectionSecurity == CONN_SECURITY_NONE { - s.EnableSMTPAuth = NewBool(false) - } else { - s.EnableSMTPAuth = NewBool(true) - } - } - - if s.SMTPUsername == nil { - s.SMTPUsername = NewString("") - } - - if s.SMTPPassword == nil { - s.SMTPPassword = NewString("") - } - - if s.SMTPServer == nil || *s.SMTPServer == "" { - s.SMTPServer = NewString("localhost") - } - - if s.SMTPPort == nil || *s.SMTPPort == "" { - s.SMTPPort = NewString("10025") - } - - if s.SMTPServerTimeout == nil || *s.SMTPServerTimeout == 0 { - s.SMTPServerTimeout = NewInt(10) - } - - if s.ConnectionSecurity == nil || *s.ConnectionSecurity == CONN_SECURITY_PLAIN { - s.ConnectionSecurity = NewString(CONN_SECURITY_NONE) - } - - if s.SendPushNotifications == nil { - s.SendPushNotifications = NewBool(!isUpdate) - } - - if s.PushNotificationServer == nil { - if isUpdate { - s.PushNotificationServer = NewString("") - } else { - s.PushNotificationServer = NewString(GENERIC_NOTIFICATION_SERVER) - } - } - - if s.PushNotificationContents == nil { - s.PushNotificationContents = NewString(FULL_NOTIFICATION) - } - - if s.PushNotificationBuffer == nil { - s.PushNotificationBuffer = NewInt(1000) - } - - if s.EnableEmailBatching == nil { - s.EnableEmailBatching = NewBool(false) - } - - if s.EmailBatchingBufferSize == nil { - s.EmailBatchingBufferSize = NewInt(EMAIL_BATCHING_BUFFER_SIZE) - } - - if s.EmailBatchingInterval == nil { - s.EmailBatchingInterval = NewInt(EMAIL_BATCHING_INTERVAL) - } - - if s.EnablePreviewModeBanner == nil { - s.EnablePreviewModeBanner = NewBool(true) - } - - if s.EnableSMTPAuth == nil { - if *s.ConnectionSecurity == CONN_SECURITY_NONE { - s.EnableSMTPAuth = NewBool(false) - } else { - s.EnableSMTPAuth = NewBool(true) - } - } - - if *s.ConnectionSecurity == CONN_SECURITY_PLAIN { - *s.ConnectionSecurity = CONN_SECURITY_NONE - } - - if s.SkipServerCertificateVerification == nil { - s.SkipServerCertificateVerification = NewBool(false) - } - - if s.EmailNotificationContentsType == nil { - s.EmailNotificationContentsType = NewString(EMAIL_NOTIFICATION_CONTENTS_FULL) - } - - if s.LoginButtonColor == nil { - s.LoginButtonColor = NewString("#0000") - } - - if s.LoginButtonBorderColor == nil { - s.LoginButtonBorderColor = NewString("#2389D7") - } - - if s.LoginButtonTextColor == nil { - s.LoginButtonTextColor = NewString("#2389D7") - } -} - -type RateLimitSettings struct { - Enable *bool `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - PerSec *int `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - MaxBurst *int `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - MemoryStoreSize *int `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - VaryByRemoteAddr *bool `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - VaryByUser *bool `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` - VaryByHeader string `access:"environment_rate_limiting,write_restrictable,cloud_restrictable"` -} - -func (s *RateLimitSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.PerSec == nil { - s.PerSec = NewInt(10) - } - - if s.MaxBurst == nil { - s.MaxBurst = NewInt(100) - } - - if s.MemoryStoreSize == nil { - s.MemoryStoreSize = NewInt(10000) - } - - if s.VaryByRemoteAddr == nil { - s.VaryByRemoteAddr = NewBool(true) - } - - if s.VaryByUser == nil { - s.VaryByUser = NewBool(false) - } -} - -type PrivacySettings struct { - ShowEmailAddress *bool `access:"site_users_and_teams"` - ShowFullName *bool `access:"site_users_and_teams"` -} - -func (s *PrivacySettings) setDefaults() { - if s.ShowEmailAddress == nil { - s.ShowEmailAddress = NewBool(true) - } - - if s.ShowFullName == nil { - s.ShowFullName = NewBool(true) - } -} - -type SupportSettings struct { - TermsOfServiceLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - PrivacyPolicyLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - AboutLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - HelpLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - ReportAProblemLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - SupportEmail *string `access:"site_customization"` - CustomTermsOfServiceEnabled *bool `access:"compliance_custom_terms_of_service"` - CustomTermsOfServiceReAcceptancePeriod *int `access:"compliance_custom_terms_of_service"` - EnableAskCommunityLink *bool `access:"site_customization"` -} - -func (s *SupportSettings) SetDefaults() { - if !IsSafeLink(s.TermsOfServiceLink) { - *s.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK - } - - if s.TermsOfServiceLink == nil { - s.TermsOfServiceLink = NewString(SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK) - } - - if !IsSafeLink(s.PrivacyPolicyLink) { - *s.PrivacyPolicyLink = "" - } - - if s.PrivacyPolicyLink == nil { - s.PrivacyPolicyLink = NewString(SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK) - } - - if !IsSafeLink(s.AboutLink) { - *s.AboutLink = "" - } - - if s.AboutLink == nil { - s.AboutLink = NewString(SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK) - } - - if !IsSafeLink(s.HelpLink) { - *s.HelpLink = "" - } - - if s.HelpLink == nil { - s.HelpLink = NewString(SUPPORT_SETTINGS_DEFAULT_HELP_LINK) - } - - if !IsSafeLink(s.ReportAProblemLink) { - *s.ReportAProblemLink = "" - } - - if s.ReportAProblemLink == nil { - s.ReportAProblemLink = NewString(SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK) - } - - if s.SupportEmail == nil { - s.SupportEmail = NewString(SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL) - } - - if s.CustomTermsOfServiceEnabled == nil { - s.CustomTermsOfServiceEnabled = NewBool(false) - } - - if s.CustomTermsOfServiceReAcceptancePeriod == nil { - s.CustomTermsOfServiceReAcceptancePeriod = NewInt(SUPPORT_SETTINGS_DEFAULT_RE_ACCEPTANCE_PERIOD) - } - - if s.EnableAskCommunityLink == nil { - s.EnableAskCommunityLink = NewBool(true) - } -} - -type AnnouncementSettings struct { - EnableBanner *bool `access:"site_announcement_banner"` - BannerText *string `access:"site_announcement_banner"` // telemetry: none - BannerColor *string `access:"site_announcement_banner"` - BannerTextColor *string `access:"site_announcement_banner"` - AllowBannerDismissal *bool `access:"site_announcement_banner"` - AdminNoticesEnabled *bool `access:"site_notices"` - UserNoticesEnabled *bool `access:"site_notices"` - NoticesURL *string `access:"site_notices,write_restrictable"` // telemetry: none - NoticesFetchFrequency *int `access:"site_notices,write_restrictable"` // telemetry: none - NoticesSkipCache *bool `access:"site_notices,write_restrictable"` // telemetry: none -} - -func (s *AnnouncementSettings) SetDefaults() { - if s.EnableBanner == nil { - s.EnableBanner = NewBool(false) - } - - if s.BannerText == nil { - s.BannerText = NewString("") - } - - if s.BannerColor == nil { - s.BannerColor = NewString(ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR) - } - - if s.BannerTextColor == nil { - s.BannerTextColor = NewString(ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR) - } - - if s.AllowBannerDismissal == nil { - s.AllowBannerDismissal = NewBool(true) - } - - if s.AdminNoticesEnabled == nil { - s.AdminNoticesEnabled = NewBool(true) - } - - if s.UserNoticesEnabled == nil { - s.UserNoticesEnabled = NewBool(true) - } - if s.NoticesURL == nil { - s.NoticesURL = NewString(ANNOUNCEMENT_SETTINGS_DEFAULT_NOTICES_JSON_URL) - } - if s.NoticesSkipCache == nil { - s.NoticesSkipCache = NewBool(false) - } - if s.NoticesFetchFrequency == nil { - s.NoticesFetchFrequency = NewInt(ANNOUNCEMENT_SETTINGS_DEFAULT_NOTICES_FETCH_FREQUENCY_SECONDS) - } - -} - -type ThemeSettings struct { - EnableThemeSelection *bool `access:"experimental_features"` - DefaultTheme *string `access:"experimental_features"` - AllowCustomThemes *bool `access:"experimental_features"` - AllowedThemes []string -} - -func (s *ThemeSettings) SetDefaults() { - if s.EnableThemeSelection == nil { - s.EnableThemeSelection = NewBool(true) - } - - if s.DefaultTheme == nil { - s.DefaultTheme = NewString(TEAM_SETTINGS_DEFAULT_TEAM_TEXT) - } - - if s.AllowCustomThemes == nil { - s.AllowCustomThemes = NewBool(true) - } - - if s.AllowedThemes == nil { - s.AllowedThemes = []string{} - } -} - -type TeamSettings struct { - SiteName *string `access:"site_customization"` - MaxUsersPerTeam *int `access:"site_users_and_teams"` - DEPRECATED_DO_NOT_USE_EnableTeamCreation *bool `json:"EnableTeamCreation" mapstructure:"EnableTeamCreation"` // Deprecated: do not use - EnableUserCreation *bool `access:"authentication_signup"` - EnableOpenServer *bool `access:"authentication_signup"` - EnableUserDeactivation *bool `access:"experimental_features"` - RestrictCreationToDomains *string `access:"authentication_signup"` // telemetry: none - EnableCustomUserStatuses *bool `access:"site_users_and_teams"` - EnableCustomBrand *bool `access:"site_customization"` - CustomBrandText *string `access:"site_customization"` - CustomDescriptionText *string `access:"site_customization"` - RestrictDirectMessage *string `access:"site_users_and_teams"` - DEPRECATED_DO_NOT_USE_RestrictTeamInvite *string `json:"RestrictTeamInvite" mapstructure:"RestrictTeamInvite"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement *string `json:"RestrictPublicChannelManagement" mapstructure:"RestrictPublicChannelManagement"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement *string `json:"RestrictPrivateChannelManagement" mapstructure:"RestrictPrivateChannelManagement"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPublicChannelCreation *string `json:"RestrictPublicChannelCreation" mapstructure:"RestrictPublicChannelCreation"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPrivateChannelCreation *string `json:"RestrictPrivateChannelCreation" mapstructure:"RestrictPrivateChannelCreation"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPublicChannelDeletion *string `json:"RestrictPublicChannelDeletion" mapstructure:"RestrictPublicChannelDeletion"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPrivateChannelDeletion *string `json:"RestrictPrivateChannelDeletion" mapstructure:"RestrictPrivateChannelDeletion"` // Deprecated: do not use - DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManageMembers *string `json:"RestrictPrivateChannelManageMembers" mapstructure:"RestrictPrivateChannelManageMembers"` // Deprecated: do not use - EnableXToLeaveChannelsFromLHS *bool `access:"experimental_features"` - UserStatusAwayTimeout *int64 `access:"experimental_features"` - MaxChannelsPerTeam *int64 `access:"site_users_and_teams"` - MaxNotificationsPerChannel *int64 `access:"environment_push_notification_server"` - EnableConfirmNotificationsToChannel *bool `access:"site_notifications"` - TeammateNameDisplay *string `access:"site_users_and_teams"` - ExperimentalViewArchivedChannels *bool `access:"experimental_features,site_users_and_teams"` - ExperimentalEnableAutomaticReplies *bool `access:"experimental_features"` - ExperimentalHideTownSquareinLHS *bool `access:"experimental_features"` - ExperimentalTownSquareIsReadOnly *bool `access:"experimental_features"` - LockTeammateNameDisplay *bool `access:"site_users_and_teams"` - ExperimentalPrimaryTeam *string `access:"experimental_features"` - ExperimentalDefaultChannels []string `access:"experimental_features"` -} - -func (s *TeamSettings) SetDefaults() { - - if s.SiteName == nil || *s.SiteName == "" { - s.SiteName = NewString(TEAM_SETTINGS_DEFAULT_SITE_NAME) - } - - if s.MaxUsersPerTeam == nil { - s.MaxUsersPerTeam = NewInt(TEAM_SETTINGS_DEFAULT_MAX_USERS_PER_TEAM) - } - - if s.DEPRECATED_DO_NOT_USE_EnableTeamCreation == nil { - s.DEPRECATED_DO_NOT_USE_EnableTeamCreation = NewBool(true) - } - - if s.EnableUserCreation == nil { - s.EnableUserCreation = NewBool(true) - } - - if s.EnableOpenServer == nil { - s.EnableOpenServer = NewBool(false) - } - - if s.RestrictCreationToDomains == nil { - s.RestrictCreationToDomains = NewString("") - } - - if s.EnableCustomUserStatuses == nil { - s.EnableCustomUserStatuses = NewBool(true) - } - - if s.EnableCustomBrand == nil { - s.EnableCustomBrand = NewBool(false) - } - - if s.EnableUserDeactivation == nil { - s.EnableUserDeactivation = NewBool(false) - } - - if s.CustomBrandText == nil { - s.CustomBrandText = NewString(TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT) - } - - if s.CustomDescriptionText == nil { - s.CustomDescriptionText = NewString(TEAM_SETTINGS_DEFAULT_CUSTOM_DESCRIPTION_TEXT) - } - - if s.RestrictDirectMessage == nil { - s.RestrictDirectMessage = NewString(DIRECT_MESSAGE_ANY) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictTeamInvite == nil { - s.DEPRECATED_DO_NOT_USE_RestrictTeamInvite = NewString(PERMISSIONS_ALL) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement == nil { - s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement = NewString(PERMISSIONS_ALL) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement == nil { - s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement = NewString(PERMISSIONS_ALL) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelCreation == nil { - s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelCreation = new(string) - // If this setting does not exist, assume migration from <3.6, so use management setting as default. - if *s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement == PERMISSIONS_CHANNEL_ADMIN { - *s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelCreation = PERMISSIONS_TEAM_ADMIN - } else { - *s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelCreation = *s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement - } - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelCreation == nil { - // If this setting does not exist, assume migration from <3.6, so use management setting as default. - if *s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement == PERMISSIONS_CHANNEL_ADMIN { - s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelCreation = NewString(PERMISSIONS_TEAM_ADMIN) - } else { - s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelCreation = NewString(*s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement) - } - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelDeletion == nil { - // If this setting does not exist, assume migration from <3.6, so use management setting as default. - s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelDeletion = NewString(*s.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelDeletion == nil { - // If this setting does not exist, assume migration from <3.6, so use management setting as default. - s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelDeletion = NewString(*s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement) - } - - if s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManageMembers == nil { - s.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManageMembers = NewString(PERMISSIONS_ALL) - } - - if s.EnableXToLeaveChannelsFromLHS == nil { - s.EnableXToLeaveChannelsFromLHS = NewBool(false) - } - - if s.UserStatusAwayTimeout == nil { - s.UserStatusAwayTimeout = NewInt64(TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT) - } - - if s.MaxChannelsPerTeam == nil { - s.MaxChannelsPerTeam = NewInt64(2000) - } - - if s.MaxNotificationsPerChannel == nil { - s.MaxNotificationsPerChannel = NewInt64(1000) - } - - if s.EnableConfirmNotificationsToChannel == nil { - s.EnableConfirmNotificationsToChannel = NewBool(true) - } - - if s.ExperimentalEnableAutomaticReplies == nil { - s.ExperimentalEnableAutomaticReplies = NewBool(false) - } - - if s.ExperimentalHideTownSquareinLHS == nil { - s.ExperimentalHideTownSquareinLHS = NewBool(false) - } - - if s.ExperimentalTownSquareIsReadOnly == nil { - s.ExperimentalTownSquareIsReadOnly = NewBool(false) - } - - if s.ExperimentalPrimaryTeam == nil { - s.ExperimentalPrimaryTeam = NewString("") - } - - if s.ExperimentalDefaultChannels == nil { - s.ExperimentalDefaultChannels = []string{} - } - - if s.DEPRECATED_DO_NOT_USE_EnableTeamCreation == nil { - s.DEPRECATED_DO_NOT_USE_EnableTeamCreation = NewBool(true) - } - - if s.EnableUserCreation == nil { - s.EnableUserCreation = NewBool(true) - } - - if s.ExperimentalViewArchivedChannels == nil { - s.ExperimentalViewArchivedChannels = NewBool(true) - } - - if s.LockTeammateNameDisplay == nil { - s.LockTeammateNameDisplay = NewBool(false) - } -} - -type ClientRequirements struct { - AndroidLatestVersion string `access:"write_restrictable,cloud_restrictable"` - AndroidMinVersion string `access:"write_restrictable,cloud_restrictable"` - DesktopLatestVersion string `access:"write_restrictable,cloud_restrictable"` - DesktopMinVersion string `access:"write_restrictable,cloud_restrictable"` - IosLatestVersion string `access:"write_restrictable,cloud_restrictable"` - IosMinVersion string `access:"write_restrictable,cloud_restrictable"` -} - -type LdapSettings struct { - // Basic - Enable *bool `access:"authentication_ldap"` - EnableSync *bool `access:"authentication_ldap"` - LdapServer *string `access:"authentication_ldap"` // telemetry: none - LdapPort *int `access:"authentication_ldap"` // telemetry: none - ConnectionSecurity *string `access:"authentication_ldap"` - BaseDN *string `access:"authentication_ldap"` // telemetry: none - BindUsername *string `access:"authentication_ldap"` // telemetry: none - BindPassword *string `access:"authentication_ldap"` // telemetry: none - - // Filtering - UserFilter *string `access:"authentication_ldap"` // telemetry: none - GroupFilter *string `access:"authentication_ldap"` - GuestFilter *string `access:"authentication_ldap"` - EnableAdminFilter *bool - AdminFilter *string - - // Group Mapping - GroupDisplayNameAttribute *string `access:"authentication_ldap"` - GroupIdAttribute *string `access:"authentication_ldap"` - - // User Mapping - FirstNameAttribute *string `access:"authentication_ldap"` - LastNameAttribute *string `access:"authentication_ldap"` - EmailAttribute *string `access:"authentication_ldap"` - UsernameAttribute *string `access:"authentication_ldap"` - NicknameAttribute *string `access:"authentication_ldap"` - IdAttribute *string `access:"authentication_ldap"` - PositionAttribute *string `access:"authentication_ldap"` - LoginIdAttribute *string `access:"authentication_ldap"` - PictureAttribute *string `access:"authentication_ldap"` - - // Synchronization - SyncIntervalMinutes *int `access:"authentication_ldap"` - - // Advanced - SkipCertificateVerification *bool `access:"authentication_ldap"` - PublicCertificateFile *string `access:"authentication_ldap"` - PrivateKeyFile *string `access:"authentication_ldap"` - QueryTimeout *int `access:"authentication_ldap"` - MaxPageSize *int `access:"authentication_ldap"` - - // Customization - LoginFieldName *string `access:"authentication_ldap"` - - LoginButtonColor *string `access:"experimental_features"` - LoginButtonBorderColor *string `access:"experimental_features"` - LoginButtonTextColor *string `access:"experimental_features"` - - Trace *bool `access:"authentication_ldap"` // telemetry: none -} - -func (s *LdapSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - // When unset should default to LDAP Enabled - if s.EnableSync == nil { - s.EnableSync = NewBool(*s.Enable) - } - - if s.EnableAdminFilter == nil { - s.EnableAdminFilter = NewBool(false) - } - - if s.LdapServer == nil { - s.LdapServer = NewString("") - } - - if s.LdapPort == nil { - s.LdapPort = NewInt(389) - } - - if s.ConnectionSecurity == nil { - s.ConnectionSecurity = NewString("") - } - - if s.PublicCertificateFile == nil { - s.PublicCertificateFile = NewString("") - } - - if s.PrivateKeyFile == nil { - s.PrivateKeyFile = NewString("") - } - - if s.BaseDN == nil { - s.BaseDN = NewString("") - } - - if s.BindUsername == nil { - s.BindUsername = NewString("") - } - - if s.BindPassword == nil { - s.BindPassword = NewString("") - } - - if s.UserFilter == nil { - s.UserFilter = NewString("") - } - - if s.GuestFilter == nil { - s.GuestFilter = NewString("") - } - - if s.AdminFilter == nil { - s.AdminFilter = NewString("") - } - - if s.GroupFilter == nil { - s.GroupFilter = NewString("") - } - - if s.GroupDisplayNameAttribute == nil { - s.GroupDisplayNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE) - } - - if s.GroupIdAttribute == nil { - s.GroupIdAttribute = NewString(LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE) - } - - if s.FirstNameAttribute == nil { - s.FirstNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE) - } - - if s.LastNameAttribute == nil { - s.LastNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE) - } - - if s.EmailAttribute == nil { - s.EmailAttribute = NewString(LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE) - } - - if s.UsernameAttribute == nil { - s.UsernameAttribute = NewString(LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE) - } - - if s.NicknameAttribute == nil { - s.NicknameAttribute = NewString(LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE) - } - - if s.IdAttribute == nil { - s.IdAttribute = NewString(LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE) - } - - if s.PositionAttribute == nil { - s.PositionAttribute = NewString(LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE) - } - - if s.PictureAttribute == nil { - s.PictureAttribute = NewString(LDAP_SETTINGS_DEFAULT_PICTURE_ATTRIBUTE) - } - - // For those upgrading to the version when LoginIdAttribute was added - // they need IdAttribute == LoginIdAttribute not to break - if s.LoginIdAttribute == nil { - s.LoginIdAttribute = s.IdAttribute - } - - if s.SyncIntervalMinutes == nil { - s.SyncIntervalMinutes = NewInt(60) - } - - if s.SkipCertificateVerification == nil { - s.SkipCertificateVerification = NewBool(false) - } - - if s.QueryTimeout == nil { - s.QueryTimeout = NewInt(60) - } - - if s.MaxPageSize == nil { - s.MaxPageSize = NewInt(0) - } - - if s.LoginFieldName == nil { - s.LoginFieldName = NewString(LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME) - } - - if s.LoginButtonColor == nil { - s.LoginButtonColor = NewString("#0000") - } - - if s.LoginButtonBorderColor == nil { - s.LoginButtonBorderColor = NewString("#2389D7") - } - - if s.LoginButtonTextColor == nil { - s.LoginButtonTextColor = NewString("#2389D7") - } - - if s.Trace == nil { - s.Trace = NewBool(false) - } -} - -type ComplianceSettings struct { - Enable *bool `access:"compliance_compliance_monitoring"` - Directory *string `access:"compliance_compliance_monitoring"` // telemetry: none - EnableDaily *bool `access:"compliance_compliance_monitoring"` - BatchSize *int `access:"compliance_compliance_monitoring"` // telemetry: none -} - -func (s *ComplianceSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.Directory == nil { - s.Directory = NewString("./data/") - } - - if s.EnableDaily == nil { - s.EnableDaily = NewBool(false) - } - - if s.BatchSize == nil { - s.BatchSize = NewInt(30000) - } -} - -type LocalizationSettings struct { - DefaultServerLocale *string `access:"site_localization"` - DefaultClientLocale *string `access:"site_localization"` - AvailableLocales *string `access:"site_localization"` -} - -func (s *LocalizationSettings) SetDefaults() { - if s.DefaultServerLocale == nil { - s.DefaultServerLocale = NewString(DEFAULT_LOCALE) - } - - if s.DefaultClientLocale == nil { - s.DefaultClientLocale = NewString(DEFAULT_LOCALE) - } - - if s.AvailableLocales == nil { - s.AvailableLocales = NewString("") - } -} - -type SamlSettings struct { - // Basic - Enable *bool `access:"authentication_saml"` - EnableSyncWithLdap *bool `access:"authentication_saml"` - EnableSyncWithLdapIncludeAuth *bool `access:"authentication_saml"` - IgnoreGuestsLdapSync *bool `access:"authentication_saml"` - - Verify *bool `access:"authentication_saml"` - Encrypt *bool `access:"authentication_saml"` - SignRequest *bool `access:"authentication_saml"` - - IdpUrl *string `access:"authentication_saml"` // telemetry: none - IdpDescriptorUrl *string `access:"authentication_saml"` // telemetry: none - IdpMetadataUrl *string `access:"authentication_saml"` // telemetry: none - ServiceProviderIdentifier *string `access:"authentication_saml"` // telemetry: none - AssertionConsumerServiceURL *string `access:"authentication_saml"` // telemetry: none - - SignatureAlgorithm *string `access:"authentication_saml"` - CanonicalAlgorithm *string `access:"authentication_saml"` - - ScopingIDPProviderId *string `access:"authentication_saml"` - ScopingIDPName *string `access:"authentication_saml"` - - IdpCertificateFile *string `access:"authentication_saml"` // telemetry: none - PublicCertificateFile *string `access:"authentication_saml"` // telemetry: none - PrivateKeyFile *string `access:"authentication_saml"` // telemetry: none - - // User Mapping - IdAttribute *string `access:"authentication_saml"` - GuestAttribute *string `access:"authentication_saml"` - EnableAdminAttribute *bool - AdminAttribute *string - FirstNameAttribute *string `access:"authentication_saml"` - LastNameAttribute *string `access:"authentication_saml"` - EmailAttribute *string `access:"authentication_saml"` - UsernameAttribute *string `access:"authentication_saml"` - NicknameAttribute *string `access:"authentication_saml"` - LocaleAttribute *string `access:"authentication_saml"` - PositionAttribute *string `access:"authentication_saml"` - - LoginButtonText *string `access:"authentication_saml"` - - LoginButtonColor *string `access:"experimental_features"` - LoginButtonBorderColor *string `access:"experimental_features"` - LoginButtonTextColor *string `access:"experimental_features"` -} - -func (s *SamlSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.EnableSyncWithLdap == nil { - s.EnableSyncWithLdap = NewBool(false) - } - - if s.EnableSyncWithLdapIncludeAuth == nil { - s.EnableSyncWithLdapIncludeAuth = NewBool(false) - } - - if s.IgnoreGuestsLdapSync == nil { - s.IgnoreGuestsLdapSync = NewBool(false) - } - - if s.EnableAdminAttribute == nil { - s.EnableAdminAttribute = NewBool(false) - } - - if s.Verify == nil { - s.Verify = NewBool(true) - } - - if s.Encrypt == nil { - s.Encrypt = NewBool(true) - } - - if s.SignRequest == nil { - s.SignRequest = NewBool(false) - } - - if s.SignatureAlgorithm == nil { - s.SignatureAlgorithm = NewString(SAML_SETTINGS_DEFAULT_SIGNATURE_ALGORITHM) - } - - if s.CanonicalAlgorithm == nil { - s.CanonicalAlgorithm = NewString(SAML_SETTINGS_DEFAULT_CANONICAL_ALGORITHM) - } - - if s.IdpUrl == nil { - s.IdpUrl = NewString("") - } - - if s.IdpDescriptorUrl == nil { - s.IdpDescriptorUrl = NewString("") - } - - if s.ServiceProviderIdentifier == nil { - if s.IdpDescriptorUrl != nil { - s.ServiceProviderIdentifier = NewString(*s.IdpDescriptorUrl) - } else { - s.ServiceProviderIdentifier = NewString("") - } - } - - if s.IdpMetadataUrl == nil { - s.IdpMetadataUrl = NewString("") - } - - if s.IdpCertificateFile == nil { - s.IdpCertificateFile = NewString("") - } - - if s.PublicCertificateFile == nil { - s.PublicCertificateFile = NewString("") - } - - if s.PrivateKeyFile == nil { - s.PrivateKeyFile = NewString("") - } - - if s.AssertionConsumerServiceURL == nil { - s.AssertionConsumerServiceURL = NewString("") - } - - if s.ScopingIDPProviderId == nil { - s.ScopingIDPProviderId = NewString("") - } - - if s.ScopingIDPName == nil { - s.ScopingIDPName = NewString("") - } - - if s.LoginButtonText == nil || *s.LoginButtonText == "" { - s.LoginButtonText = NewString(USER_AUTH_SERVICE_SAML_TEXT) - } - - if s.IdAttribute == nil { - s.IdAttribute = NewString(SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE) - } - - if s.GuestAttribute == nil { - s.GuestAttribute = NewString(SAML_SETTINGS_DEFAULT_GUEST_ATTRIBUTE) - } - if s.AdminAttribute == nil { - s.AdminAttribute = NewString(SAML_SETTINGS_DEFAULT_ADMIN_ATTRIBUTE) - } - if s.FirstNameAttribute == nil { - s.FirstNameAttribute = NewString(SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE) - } - - if s.LastNameAttribute == nil { - s.LastNameAttribute = NewString(SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE) - } - - if s.EmailAttribute == nil { - s.EmailAttribute = NewString(SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE) - } - - if s.UsernameAttribute == nil { - s.UsernameAttribute = NewString(SAML_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE) - } - - if s.NicknameAttribute == nil { - s.NicknameAttribute = NewString(SAML_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE) - } - - if s.PositionAttribute == nil { - s.PositionAttribute = NewString(SAML_SETTINGS_DEFAULT_POSITION_ATTRIBUTE) - } - - if s.LocaleAttribute == nil { - s.LocaleAttribute = NewString(SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE) - } - - if s.LoginButtonColor == nil { - s.LoginButtonColor = NewString("#34a28b") - } - - if s.LoginButtonBorderColor == nil { - s.LoginButtonBorderColor = NewString("#2389D7") - } - - if s.LoginButtonTextColor == nil { - s.LoginButtonTextColor = NewString("#ffffff") - } -} - -type NativeAppSettings struct { - AppCustomURLSchemes []string `access:"site_customization,write_restrictable,cloud_restrictable"` // telemetry: none - AppDownloadLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - AndroidAppDownloadLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` - IosAppDownloadLink *string `access:"site_customization,write_restrictable,cloud_restrictable"` -} - -func (s *NativeAppSettings) SetDefaults() { - if s.AppDownloadLink == nil { - s.AppDownloadLink = NewString(NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK) - } - - if s.AndroidAppDownloadLink == nil { - s.AndroidAppDownloadLink = NewString(NATIVEAPP_SETTINGS_DEFAULT_ANDROID_APP_DOWNLOAD_LINK) - } - - if s.IosAppDownloadLink == nil { - s.IosAppDownloadLink = NewString(NATIVEAPP_SETTINGS_DEFAULT_IOS_APP_DOWNLOAD_LINK) - } - - if s.AppCustomURLSchemes == nil { - s.AppCustomURLSchemes = GetDefaultAppCustomURLSchemes() - } -} - -type ElasticsearchSettings struct { - ConnectionUrl *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - Username *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - Password *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - EnableIndexing *bool `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - EnableSearching *bool `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - EnableAutocomplete *bool `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - Sniff *bool `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - PostIndexReplicas *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - PostIndexShards *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - ChannelIndexReplicas *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - ChannelIndexShards *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - UserIndexReplicas *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - UserIndexShards *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - AggregatePostsAfterDays *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` // telemetry: none - PostsAggregatorJobStartTime *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` // telemetry: none - IndexPrefix *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - LiveIndexingBatchSize *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - BulkIndexingTimeWindowSeconds *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - RequestTimeoutSeconds *int `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - SkipTLSVerification *bool `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` - Trace *string `access:"environment_elasticsearch,write_restrictable,cloud_restrictable"` -} - -func (s *ElasticsearchSettings) SetDefaults() { - if s.ConnectionUrl == nil { - s.ConnectionUrl = NewString(ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL) - } - - if s.Username == nil { - s.Username = NewString(ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME) - } - - if s.Password == nil { - s.Password = NewString(ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD) - } - - if s.EnableIndexing == nil { - s.EnableIndexing = NewBool(false) - } - - if s.EnableSearching == nil { - s.EnableSearching = NewBool(false) - } - - if s.EnableAutocomplete == nil { - s.EnableAutocomplete = NewBool(false) - } - - if s.Sniff == nil { - s.Sniff = NewBool(true) - } - - if s.PostIndexReplicas == nil { - s.PostIndexReplicas = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_REPLICAS) - } - - if s.PostIndexShards == nil { - s.PostIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS) - } - - if s.ChannelIndexReplicas == nil { - s.ChannelIndexReplicas = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_REPLICAS) - } - - if s.ChannelIndexShards == nil { - s.ChannelIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_CHANNEL_INDEX_SHARDS) - } - - if s.UserIndexReplicas == nil { - s.UserIndexReplicas = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_REPLICAS) - } - - if s.UserIndexShards == nil { - s.UserIndexShards = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_USER_INDEX_SHARDS) - } - - if s.AggregatePostsAfterDays == nil { - s.AggregatePostsAfterDays = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_AGGREGATE_POSTS_AFTER_DAYS) - } - - if s.PostsAggregatorJobStartTime == nil { - s.PostsAggregatorJobStartTime = NewString(ELASTICSEARCH_SETTINGS_DEFAULT_POSTS_AGGREGATOR_JOB_START_TIME) - } - - if s.IndexPrefix == nil { - s.IndexPrefix = NewString(ELASTICSEARCH_SETTINGS_DEFAULT_INDEX_PREFIX) - } - - if s.LiveIndexingBatchSize == nil { - s.LiveIndexingBatchSize = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_LIVE_INDEXING_BATCH_SIZE) - } - - if s.BulkIndexingTimeWindowSeconds == nil { - s.BulkIndexingTimeWindowSeconds = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_BULK_INDEXING_TIME_WINDOW_SECONDS) - } - - if s.RequestTimeoutSeconds == nil { - s.RequestTimeoutSeconds = NewInt(ELASTICSEARCH_SETTINGS_DEFAULT_REQUEST_TIMEOUT_SECONDS) - } - - if s.SkipTLSVerification == nil { - s.SkipTLSVerification = NewBool(false) - } - - if s.Trace == nil { - s.Trace = NewString("") - } -} - -type BleveSettings struct { - IndexDir *string `access:"experimental_bleve"` // telemetry: none - EnableIndexing *bool `access:"experimental_bleve"` - EnableSearching *bool `access:"experimental_bleve"` - EnableAutocomplete *bool `access:"experimental_bleve"` - BulkIndexingTimeWindowSeconds *int `access:"experimental_bleve"` -} - -func (bs *BleveSettings) SetDefaults() { - if bs.IndexDir == nil { - bs.IndexDir = NewString(BLEVE_SETTINGS_DEFAULT_INDEX_DIR) - } - - if bs.EnableIndexing == nil { - bs.EnableIndexing = NewBool(false) - } - - if bs.EnableSearching == nil { - bs.EnableSearching = NewBool(false) - } - - if bs.EnableAutocomplete == nil { - bs.EnableAutocomplete = NewBool(false) - } - - if bs.BulkIndexingTimeWindowSeconds == nil { - bs.BulkIndexingTimeWindowSeconds = NewInt(BLEVE_SETTINGS_DEFAULT_BULK_INDEXING_TIME_WINDOW_SECONDS) - } -} - -type DataRetentionSettings struct { - EnableMessageDeletion *bool `access:"compliance_data_retention_policy"` - EnableFileDeletion *bool `access:"compliance_data_retention_policy"` - MessageRetentionDays *int `access:"compliance_data_retention_policy"` - FileRetentionDays *int `access:"compliance_data_retention_policy"` - DeletionJobStartTime *string `access:"compliance_data_retention_policy"` - BatchSize *int `access:"compliance_data_retention_policy"` -} - -func (s *DataRetentionSettings) SetDefaults() { - if s.EnableMessageDeletion == nil { - s.EnableMessageDeletion = NewBool(false) - } - - if s.EnableFileDeletion == nil { - s.EnableFileDeletion = NewBool(false) - } - - if s.MessageRetentionDays == nil { - s.MessageRetentionDays = NewInt(DATA_RETENTION_SETTINGS_DEFAULT_MESSAGE_RETENTION_DAYS) - } - - if s.FileRetentionDays == nil { - s.FileRetentionDays = NewInt(DATA_RETENTION_SETTINGS_DEFAULT_FILE_RETENTION_DAYS) - } - - if s.DeletionJobStartTime == nil { - s.DeletionJobStartTime = NewString(DATA_RETENTION_SETTINGS_DEFAULT_DELETION_JOB_START_TIME) - } - - if s.BatchSize == nil { - s.BatchSize = NewInt(DATA_RETENTION_SETTINGS_DEFAULT_BATCH_SIZE) - } -} - -type JobSettings struct { - RunJobs *bool `access:"write_restrictable,cloud_restrictable"` - RunScheduler *bool `access:"write_restrictable,cloud_restrictable"` -} - -func (s *JobSettings) SetDefaults() { - if s.RunJobs == nil { - s.RunJobs = NewBool(true) - } - - if s.RunScheduler == nil { - s.RunScheduler = NewBool(true) - } -} - -type CloudSettings struct { - CWSUrl *string `access:"write_restrictable"` - CWSAPIUrl *string `access:"write_restrictable"` -} - -func (s *CloudSettings) SetDefaults() { - if s.CWSUrl == nil { - s.CWSUrl = NewString(CLOUD_SETTINGS_DEFAULT_CWS_URL) - } - if s.CWSAPIUrl == nil { - s.CWSAPIUrl = NewString(CLOUD_SETTINGS_DEFAULT_CWS_API_URL) - } -} - -type PluginState struct { - Enable bool -} - -type PluginSettings struct { - Enable *bool `access:"plugins,write_restrictable"` - EnableUploads *bool `access:"plugins,write_restrictable,cloud_restrictable"` - AllowInsecureDownloadUrl *bool `access:"plugins,write_restrictable,cloud_restrictable"` - EnableHealthCheck *bool `access:"plugins,write_restrictable,cloud_restrictable"` - Directory *string `access:"plugins,write_restrictable,cloud_restrictable"` // telemetry: none - ClientDirectory *string `access:"plugins,write_restrictable,cloud_restrictable"` // telemetry: none - Plugins map[string]map[string]interface{} `access:"plugins"` // telemetry: none - PluginStates map[string]*PluginState `access:"plugins"` // telemetry: none - EnableMarketplace *bool `access:"plugins,write_restrictable,cloud_restrictable"` - EnableRemoteMarketplace *bool `access:"plugins,write_restrictable,cloud_restrictable"` - AutomaticPrepackagedPlugins *bool `access:"plugins,write_restrictable,cloud_restrictable"` - RequirePluginSignature *bool `access:"plugins,write_restrictable,cloud_restrictable"` - MarketplaceUrl *string `access:"plugins,write_restrictable,cloud_restrictable"` - SignaturePublicKeyFiles []string `access:"plugins,write_restrictable,cloud_restrictable"` - ChimeraOAuthProxyUrl *string `access:"plugins,write_restrictable,cloud_restrictable"` -} - -func (s *PluginSettings) SetDefaults(ls LogSettings) { - if s.Enable == nil { - s.Enable = NewBool(true) - } - - if s.EnableUploads == nil { - s.EnableUploads = NewBool(false) - } - - if s.AllowInsecureDownloadUrl == nil { - s.AllowInsecureDownloadUrl = NewBool(false) - } - - if s.EnableHealthCheck == nil { - s.EnableHealthCheck = NewBool(true) - } - - if s.Directory == nil || *s.Directory == "" { - s.Directory = NewString(PLUGIN_SETTINGS_DEFAULT_DIRECTORY) - } - - if s.ClientDirectory == nil || *s.ClientDirectory == "" { - s.ClientDirectory = NewString(PLUGIN_SETTINGS_DEFAULT_CLIENT_DIRECTORY) - } - - if s.Plugins == nil { - s.Plugins = make(map[string]map[string]interface{}) - } - - if s.PluginStates == nil { - s.PluginStates = make(map[string]*PluginState) - } - - if s.PluginStates["com.mattermost.nps"] == nil { - // Enable the NPS plugin by default if diagnostics are enabled - s.PluginStates["com.mattermost.nps"] = &PluginState{Enable: ls.EnableDiagnostics == nil || *ls.EnableDiagnostics} - } - - if s.PluginStates["com.mattermost.plugin-incident-management"] == nil && BuildEnterpriseReady == "true" { - // Enable the incident management plugin by default - s.PluginStates["com.mattermost.plugin-incident-management"] = &PluginState{Enable: true} - } - - if s.PluginStates["com.mattermost.plugin-channel-export"] == nil && BuildEnterpriseReady == "true" { - // Enable the channel export plugin by default - s.PluginStates["com.mattermost.plugin-channel-export"] = &PluginState{Enable: true} - } - - if s.EnableMarketplace == nil { - s.EnableMarketplace = NewBool(PLUGIN_SETTINGS_DEFAULT_ENABLE_MARKETPLACE) - } - - if s.EnableRemoteMarketplace == nil { - s.EnableRemoteMarketplace = NewBool(true) - } - - if s.AutomaticPrepackagedPlugins == nil { - s.AutomaticPrepackagedPlugins = NewBool(true) - } - - if s.MarketplaceUrl == nil || *s.MarketplaceUrl == "" || *s.MarketplaceUrl == PLUGIN_SETTINGS_OLD_MARKETPLACE_URL { - s.MarketplaceUrl = NewString(PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL) - } - - if s.RequirePluginSignature == nil { - s.RequirePluginSignature = NewBool(false) - } - - if s.SignaturePublicKeyFiles == nil { - s.SignaturePublicKeyFiles = []string{} - } - - if s.ChimeraOAuthProxyUrl == nil { - s.ChimeraOAuthProxyUrl = NewString("") - } -} - -type GlobalRelayMessageExportSettings struct { - CustomerType *string `access:"compliance_compliance_export"` // must be either A9 or A10, dictates SMTP server url - SmtpUsername *string `access:"compliance_compliance_export"` - SmtpPassword *string `access:"compliance_compliance_export"` - EmailAddress *string `access:"compliance_compliance_export"` // the address to send messages to - SMTPServerTimeout *int `access:"compliance_compliance_export"` -} - -func (s *GlobalRelayMessageExportSettings) SetDefaults() { - if s.CustomerType == nil { - s.CustomerType = NewString(GLOBALRELAY_CUSTOMER_TYPE_A9) - } - if s.SmtpUsername == nil { - s.SmtpUsername = NewString("") - } - if s.SmtpPassword == nil { - s.SmtpPassword = NewString("") - } - if s.EmailAddress == nil { - s.EmailAddress = NewString("") - } - if s.SMTPServerTimeout == nil || *s.SMTPServerTimeout == 0 { - s.SMTPServerTimeout = NewInt(1800) - } -} - -type MessageExportSettings struct { - EnableExport *bool `access:"compliance_compliance_export"` - ExportFormat *string `access:"compliance_compliance_export"` - DailyRunTime *string `access:"compliance_compliance_export"` - ExportFromTimestamp *int64 `access:"compliance_compliance_export"` - BatchSize *int `access:"compliance_compliance_export"` - DownloadExportResults *bool `access:"compliance_compliance_export"` - - // formatter-specific settings - these are only expected to be non-nil if ExportFormat is set to the associated format - GlobalRelaySettings *GlobalRelayMessageExportSettings `access:"compliance_compliance_export"` -} - -func (s *MessageExportSettings) SetDefaults() { - if s.EnableExport == nil { - s.EnableExport = NewBool(false) - } - - if s.DownloadExportResults == nil { - s.DownloadExportResults = NewBool(false) - } - - if s.ExportFormat == nil { - s.ExportFormat = NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE) - } - - if s.DailyRunTime == nil { - s.DailyRunTime = NewString("01:00") - } - - if s.ExportFromTimestamp == nil { - s.ExportFromTimestamp = NewInt64(0) - } - - if s.BatchSize == nil { - s.BatchSize = NewInt(10000) - } - - if s.GlobalRelaySettings == nil { - s.GlobalRelaySettings = &GlobalRelayMessageExportSettings{} - } - s.GlobalRelaySettings.SetDefaults() -} - -type DisplaySettings struct { - CustomUrlSchemes []string `access:"site_customization"` - ExperimentalTimezone *bool `access:"experimental_features"` -} - -func (s *DisplaySettings) SetDefaults() { - if s.CustomUrlSchemes == nil { - customUrlSchemes := []string{} - s.CustomUrlSchemes = customUrlSchemes - } - - if s.ExperimentalTimezone == nil { - s.ExperimentalTimezone = NewBool(true) - } -} - -type GuestAccountsSettings struct { - Enable *bool `access:"authentication_guest_access"` - AllowEmailAccounts *bool `access:"authentication_guest_access"` - EnforceMultifactorAuthentication *bool `access:"authentication_guest_access"` - RestrictCreationToDomains *string `access:"authentication_guest_access"` -} - -func (s *GuestAccountsSettings) SetDefaults() { - if s.Enable == nil { - s.Enable = NewBool(false) - } - - if s.AllowEmailAccounts == nil { - s.AllowEmailAccounts = NewBool(true) - } - - if s.EnforceMultifactorAuthentication == nil { - s.EnforceMultifactorAuthentication = NewBool(false) - } - - if s.RestrictCreationToDomains == nil { - s.RestrictCreationToDomains = NewString("") - } -} - -type ImageProxySettings struct { - Enable *bool `access:"environment_image_proxy"` - ImageProxyType *string `access:"environment_image_proxy"` - RemoteImageProxyURL *string `access:"environment_image_proxy"` - RemoteImageProxyOptions *string `access:"environment_image_proxy"` -} - -func (s *ImageProxySettings) SetDefaults(ss ServiceSettings) { - if s.Enable == nil { - if ss.DEPRECATED_DO_NOT_USE_ImageProxyType == nil || *ss.DEPRECATED_DO_NOT_USE_ImageProxyType == "" { - s.Enable = NewBool(false) - } else { - s.Enable = NewBool(true) - } - } - - if s.ImageProxyType == nil { - if ss.DEPRECATED_DO_NOT_USE_ImageProxyType == nil || *ss.DEPRECATED_DO_NOT_USE_ImageProxyType == "" { - s.ImageProxyType = NewString(IMAGE_PROXY_TYPE_LOCAL) - } else { - s.ImageProxyType = ss.DEPRECATED_DO_NOT_USE_ImageProxyType - } - } - - if s.RemoteImageProxyURL == nil { - if ss.DEPRECATED_DO_NOT_USE_ImageProxyURL == nil { - s.RemoteImageProxyURL = NewString("") - } else { - s.RemoteImageProxyURL = ss.DEPRECATED_DO_NOT_USE_ImageProxyURL - } - } - - if s.RemoteImageProxyOptions == nil { - if ss.DEPRECATED_DO_NOT_USE_ImageProxyOptions == nil { - s.RemoteImageProxyOptions = NewString("") - } else { - s.RemoteImageProxyOptions = ss.DEPRECATED_DO_NOT_USE_ImageProxyOptions - } - } -} - -// ImportSettings defines configuration settings for file imports. -type ImportSettings struct { - // The directory where to store the imported files. - Directory *string - // The number of days to retain the imported files before deleting them. - RetentionDays *int -} - -func (s *ImportSettings) isValid() *AppError { - if *s.Directory == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.import.directory.app_error", nil, "", http.StatusBadRequest) - } - - if *s.RetentionDays <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.import.retention_days_too_low.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -// SetDefaults applies the default settings to the struct. -func (s *ImportSettings) SetDefaults() { - if s.Directory == nil || *s.Directory == "" { - s.Directory = NewString(IMPORT_SETTINGS_DEFAULT_DIRECTORY) - } - - if s.RetentionDays == nil { - s.RetentionDays = NewInt(IMPORT_SETTINGS_DEFAULT_RETENTION_DAYS) - } -} - -// ExportSettings defines configuration settings for file exports. -type ExportSettings struct { - // The directory where to store the exported files. - Directory *string // telemetry: none - // The number of days to retain the exported files before deleting them. - RetentionDays *int -} - -func (s *ExportSettings) isValid() *AppError { - if *s.Directory == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.export.directory.app_error", nil, "", http.StatusBadRequest) - } - - if *s.RetentionDays <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.export.retention_days_too_low.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -// SetDefaults applies the default settings to the struct. -func (s *ExportSettings) SetDefaults() { - if s.Directory == nil || *s.Directory == "" { - s.Directory = NewString(EXPORT_SETTINGS_DEFAULT_DIRECTORY) - } - - if s.RetentionDays == nil { - s.RetentionDays = NewInt(EXPORT_SETTINGS_DEFAULT_RETENTION_DAYS) - } -} - -type ConfigFunc func() *Config - -const ConfigAccessTagType = "access" -const ConfigAccessTagWriteRestrictable = "write_restrictable" -const ConfigAccessTagCloudRestrictable = "cloud_restrictable" - -// Allows read access if any PERMISSION_SYSCONSOLE_READ_* is allowed -const ConfigAccessTagAnySysConsoleRead = "*_read" - -// Config fields support the 'access' tag with the following values corresponding to the suffix of the associated -// PERMISSION_SYSCONSOLE_*_* permission Id: 'about', 'reporting', 'user_management_users', -// 'user_management_groups', 'user_management_teams', 'user_management_channels', -// 'user_management_permissions', 'environment_web_server', 'environment_database', 'environment_elasticsearch', -// 'environment_file_storage', 'environment_image_proxy', 'environment_smtp', 'environment_push_notification_server', -// 'environment_high_availability', 'environment_rate_limiting', 'environment_logging', 'environment_session_lengths', -// 'environment_performance_monitoring', 'environment_developer', 'site', 'authentication', 'plugins', -// 'integrations', 'compliance', 'plugins', and 'experimental'. They grant read and/or write access to the config field -// to roles without PERMISSION_MANAGE_SYSTEM. -// -// The 'access' tag '*_read' checks for any SYSCONSOLE read permission and grants access if any read permission is allowed. -// -// By default config values can be written with PERMISSION_MANAGE_SYSTEM, but if ExperimentalSettings.RestrictSystemAdmin is true -// and the access tag contains the value 'write_restrictable', then even PERMISSION_MANAGE_SYSTEM does not grant write access. -// -// PERMISSION_MANAGE_SYSTEM always grants read access. -// -// Config values with the access tag 'cloud_restrictable' mean that are marked to be filtered when it's used in a cloud licensed -// environment with ExperimentalSettings.RestrictedSystemAdmin set to true. -// -// Example: -// type HairSettings struct { -// // Colour is writeable with either PERMISSION_SYSCONSOLE_WRITE_REPORTING or PERMISSION_SYSCONSOLE_WRITE_USER_MANAGEMENT_GROUPS. -// // It is readable by PERMISSION_SYSCONSOLE_READ_REPORTING and PERMISSION_SYSCONSOLE_READ_USER_MANAGEMENT_GROUPS permissions. -// // PERMISSION_MANAGE_SYSTEM grants read and write access. -// Colour string `access:"reporting,user_management_groups"` -// -// -// // Length is only readable and writable via PERMISSION_MANAGE_SYSTEM. -// Length string -// -// // Product is only writeable by PERMISSION_MANAGE_SYSTEM if ExperimentalSettings.RestrictSystemAdmin is false. -// // PERMISSION_MANAGE_SYSTEM can always read the value. -// Product bool `access:write_restrictable` -// } -type Config struct { - ServiceSettings ServiceSettings - TeamSettings TeamSettings - ClientRequirements ClientRequirements - SqlSettings SqlSettings - LogSettings LogSettings - ExperimentalAuditSettings ExperimentalAuditSettings - NotificationLogSettings NotificationLogSettings - PasswordSettings PasswordSettings - FileSettings FileSettings - EmailSettings EmailSettings - RateLimitSettings RateLimitSettings - PrivacySettings PrivacySettings - SupportSettings SupportSettings - AnnouncementSettings AnnouncementSettings - ThemeSettings ThemeSettings - GitLabSettings SSOSettings - GoogleSettings SSOSettings - Office365Settings Office365Settings - OpenIdSettings SSOSettings - LdapSettings LdapSettings - ComplianceSettings ComplianceSettings - LocalizationSettings LocalizationSettings - SamlSettings SamlSettings - NativeAppSettings NativeAppSettings - ClusterSettings ClusterSettings - MetricsSettings MetricsSettings - ExperimentalSettings ExperimentalSettings - AnalyticsSettings AnalyticsSettings - ElasticsearchSettings ElasticsearchSettings - BleveSettings BleveSettings - DataRetentionSettings DataRetentionSettings - MessageExportSettings MessageExportSettings - JobSettings JobSettings // telemetry: none - PluginSettings PluginSettings - DisplaySettings DisplaySettings - GuestAccountsSettings GuestAccountsSettings - ImageProxySettings ImageProxySettings - CloudSettings CloudSettings // telemetry: none - FeatureFlags *FeatureFlags `access:"*_read" json:",omitempty"` - ImportSettings ImportSettings // telemetry: none - ExportSettings ExportSettings -} - -func (o *Config) Clone() *Config { - var ret Config - if err := json.Unmarshal([]byte(o.ToJson()), &ret); err != nil { - panic(err) - } - return &ret -} - -func (o *Config) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *Config) ToJsonFiltered(tagType, tagValue string) string { - filteredConfigMap := structToMapFilteredByTag(*o, tagType, tagValue) - for key, value := range filteredConfigMap { - v, ok := value.(map[string]interface{}) - if ok && len(v) == 0 { - delete(filteredConfigMap, key) - } - } - b, _ := json.Marshal(filteredConfigMap) - return string(b) -} - -func (o *Config) GetSSOService(service string) *SSOSettings { - switch service { - case SERVICE_GITLAB: - return &o.GitLabSettings - case SERVICE_GOOGLE: - return &o.GoogleSettings - case SERVICE_OFFICE365: - return o.Office365Settings.SSOSettings() - case SERVICE_OPENID: - return &o.OpenIdSettings - } - - return nil -} - -func ConfigFromJson(data io.Reader) *Config { - var o *Config - json.NewDecoder(data).Decode(&o) - return o -} - -// isUpdate detects a pre-existing config based on whether SiteURL has been changed -func (o *Config) isUpdate() bool { - return o.ServiceSettings.SiteURL != nil -} - -func (o *Config) SetDefaults() { - isUpdate := o.isUpdate() - - o.LdapSettings.SetDefaults() - o.SamlSettings.SetDefaults() - - if o.TeamSettings.TeammateNameDisplay == nil { - o.TeamSettings.TeammateNameDisplay = NewString(SHOW_USERNAME) - - if *o.SamlSettings.Enable || *o.LdapSettings.Enable { - *o.TeamSettings.TeammateNameDisplay = SHOW_FULLNAME - } - } - - o.SqlSettings.SetDefaults(isUpdate) - o.FileSettings.SetDefaults(isUpdate) - o.EmailSettings.SetDefaults(isUpdate) - o.PrivacySettings.setDefaults() - o.Office365Settings.setDefaults() - o.Office365Settings.setDefaults() - o.GitLabSettings.setDefaults("", "", "", "", "") - o.GoogleSettings.setDefaults(GOOGLE_SETTINGS_DEFAULT_SCOPE, GOOGLE_SETTINGS_DEFAULT_AUTH_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_TOKEN_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_USER_API_ENDPOINT, "") - o.OpenIdSettings.setDefaults(OPENID_SETTINGS_DEFAULT_SCOPE, "", "", "", "#145DBF") - o.ServiceSettings.SetDefaults(isUpdate) - o.PasswordSettings.SetDefaults() - o.TeamSettings.SetDefaults() - o.MetricsSettings.SetDefaults() - o.ExperimentalSettings.SetDefaults() - o.SupportSettings.SetDefaults() - o.AnnouncementSettings.SetDefaults() - o.ThemeSettings.SetDefaults() - o.ClusterSettings.SetDefaults() - o.PluginSettings.SetDefaults(o.LogSettings) - o.AnalyticsSettings.SetDefaults() - o.ComplianceSettings.SetDefaults() - o.LocalizationSettings.SetDefaults() - o.ElasticsearchSettings.SetDefaults() - o.BleveSettings.SetDefaults() - o.NativeAppSettings.SetDefaults() - o.DataRetentionSettings.SetDefaults() - o.RateLimitSettings.SetDefaults() - o.LogSettings.SetDefaults() - o.ExperimentalAuditSettings.SetDefaults() - o.NotificationLogSettings.SetDefaults() - o.JobSettings.SetDefaults() - o.MessageExportSettings.SetDefaults() - o.DisplaySettings.SetDefaults() - o.GuestAccountsSettings.SetDefaults() - o.ImageProxySettings.SetDefaults(o.ServiceSettings) - o.CloudSettings.SetDefaults() - if o.FeatureFlags == nil { - o.FeatureFlags = &FeatureFlags{} - o.FeatureFlags.SetDefaults() - } - o.ImportSettings.SetDefaults() - o.ExportSettings.SetDefaults() -} - -func (o *Config) IsValid() *AppError { - if *o.ServiceSettings.SiteURL == "" && *o.EmailSettings.EnableEmailBatching { - return NewAppError("Config.IsValid", "model.config.is_valid.site_url_email_batching.app_error", nil, "", http.StatusBadRequest) - } - - if *o.ClusterSettings.Enable && *o.EmailSettings.EnableEmailBatching { - return NewAppError("Config.IsValid", "model.config.is_valid.cluster_email_batching.app_error", nil, "", http.StatusBadRequest) - } - - if *o.ServiceSettings.SiteURL == "" && *o.ServiceSettings.AllowCookiesForSubdomains { - return NewAppError("Config.IsValid", "model.config.is_valid.allow_cookies_for_subdomains.app_error", nil, "", http.StatusBadRequest) - } - - if err := o.TeamSettings.isValid(); err != nil { - return err - } - - if err := o.SqlSettings.isValid(); err != nil { - return err - } - - if err := o.FileSettings.isValid(); err != nil { - return err - } - - if err := o.EmailSettings.isValid(); err != nil { - return err - } - - if err := o.LdapSettings.isValid(); err != nil { - return err - } - - if err := o.SamlSettings.isValid(); err != nil { - return err - } - - if *o.PasswordSettings.MinimumLength < PASSWORD_MINIMUM_LENGTH || *o.PasswordSettings.MinimumLength > PASSWORD_MAXIMUM_LENGTH { - return NewAppError("Config.IsValid", "model.config.is_valid.password_length.app_error", map[string]interface{}{"MinLength": PASSWORD_MINIMUM_LENGTH, "MaxLength": PASSWORD_MAXIMUM_LENGTH}, "", http.StatusBadRequest) - } - - if err := o.RateLimitSettings.isValid(); err != nil { - return err - } - - if err := o.ServiceSettings.isValid(); err != nil { - return err - } - - if err := o.ElasticsearchSettings.isValid(); err != nil { - return err - } - - if err := o.BleveSettings.isValid(); err != nil { - return err - } - - if err := o.DataRetentionSettings.isValid(); err != nil { - return err - } - - if err := o.LocalizationSettings.isValid(); err != nil { - return err - } - - if err := o.MessageExportSettings.isValid(); err != nil { - return err - } - - if err := o.DisplaySettings.isValid(); err != nil { - return err - } - - if err := o.ImageProxySettings.isValid(); err != nil { - return err - } - - if err := o.ImportSettings.isValid(); err != nil { - return err - } - return nil -} - -func (s *TeamSettings) isValid() *AppError { - if *s.MaxUsersPerTeam <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.max_users.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxChannelsPerTeam <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.max_channels.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxNotificationsPerChannel <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.max_notify_per_channel.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.RestrictDirectMessage == DIRECT_MESSAGE_ANY || *s.RestrictDirectMessage == DIRECT_MESSAGE_TEAM) { - return NewAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.TeammateNameDisplay == SHOW_FULLNAME || *s.TeammateNameDisplay == SHOW_NICKNAME_FULLNAME || *s.TeammateNameDisplay == SHOW_USERNAME) { - return NewAppError("Config.IsValid", "model.config.is_valid.teammate_name_display.app_error", nil, "", http.StatusBadRequest) - } - - if len(*s.SiteName) > SITENAME_MAX_LENGTH { - return NewAppError("Config.IsValid", "model.config.is_valid.sitename_length.app_error", map[string]interface{}{"MaxLength": SITENAME_MAX_LENGTH}, "", http.StatusBadRequest) - } - - return nil -} - -func (s *SqlSettings) isValid() *AppError { - if *s.AtRestEncryptKey != "" && len(*s.AtRestEncryptKey) < 32 { - return NewAppError("Config.IsValid", "model.config.is_valid.encrypt_sql.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.DriverName == DATABASE_DRIVER_MYSQL || *s.DriverName == DATABASE_DRIVER_POSTGRES) { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_driver.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxIdleConns <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_idle.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ConnMaxLifetimeMilliseconds < 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_conn_max_lifetime_milliseconds.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ConnMaxIdleTimeMilliseconds < 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_conn_max_idle_time_milliseconds.app_error", nil, "", http.StatusBadRequest) - } - - if *s.QueryTimeout <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_query_timeout.app_error", nil, "", http.StatusBadRequest) - } - - if *s.DataSource == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_data_src.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxOpenConns <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.sql_max_conn.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *FileSettings) isValid() *AppError { - if *s.MaxFileSize <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.max_file_size.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.DriverName == IMAGE_DRIVER_LOCAL || *s.DriverName == IMAGE_DRIVER_S3) { - return NewAppError("Config.IsValid", "model.config.is_valid.file_driver.app_error", nil, "", http.StatusBadRequest) - } - - if *s.PublicLinkSalt != "" && len(*s.PublicLinkSalt) < 32 { - return NewAppError("Config.IsValid", "model.config.is_valid.file_salt.app_error", nil, "", http.StatusBadRequest) - } - - if *s.Directory == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.directory.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *EmailSettings) isValid() *AppError { - if !(*s.ConnectionSecurity == CONN_SECURITY_NONE || *s.ConnectionSecurity == CONN_SECURITY_TLS || *s.ConnectionSecurity == CONN_SECURITY_STARTTLS || *s.ConnectionSecurity == CONN_SECURITY_PLAIN) { - return NewAppError("Config.IsValid", "model.config.is_valid.email_security.app_error", nil, "", http.StatusBadRequest) - } - - if *s.EmailBatchingBufferSize <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.email_batching_buffer_size.app_error", nil, "", http.StatusBadRequest) - } - - if *s.EmailBatchingInterval < 30 { - return NewAppError("Config.IsValid", "model.config.is_valid.email_batching_interval.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.EmailNotificationContentsType == EMAIL_NOTIFICATION_CONTENTS_FULL || *s.EmailNotificationContentsType == EMAIL_NOTIFICATION_CONTENTS_GENERIC) { - return NewAppError("Config.IsValid", "model.config.is_valid.email_notification_contents_type.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *RateLimitSettings) isValid() *AppError { - if *s.MemoryStoreSize <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.rate_mem.app_error", nil, "", http.StatusBadRequest) - } - - if *s.PerSec <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.rate_sec.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxBurst <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.max_burst.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *LdapSettings) isValid() *AppError { - if !(*s.ConnectionSecurity == CONN_SECURITY_NONE || *s.ConnectionSecurity == CONN_SECURITY_TLS || *s.ConnectionSecurity == CONN_SECURITY_STARTTLS) { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_security.app_error", nil, "", http.StatusBadRequest) - } - - if *s.SyncIntervalMinutes <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_sync_interval.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaxPageSize < 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_max_page_size.app_error", nil, "", http.StatusBadRequest) - } - - if *s.Enable { - if *s.LdapServer == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_server", nil, "", http.StatusBadRequest) - } - - if *s.BaseDN == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_basedn", nil, "", http.StatusBadRequest) - } - - if *s.EmailAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_email", nil, "", http.StatusBadRequest) - } - - if *s.UsernameAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_username", nil, "", http.StatusBadRequest) - } - - if *s.IdAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "", http.StatusBadRequest) - } - - if *s.LoginIdAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.ldap_login_id", nil, "", http.StatusBadRequest) - } - - if *s.UserFilter != "" { - if _, err := ldap.CompileFilter(*s.UserFilter); err != nil { - return NewAppError("ValidateFilter", "ent.ldap.validate_filter.app_error", nil, err.Error(), http.StatusBadRequest) - } - } - - if *s.GuestFilter != "" { - if _, err := ldap.CompileFilter(*s.GuestFilter); err != nil { - return NewAppError("LdapSettings.isValid", "ent.ldap.validate_guest_filter.app_error", nil, err.Error(), http.StatusBadRequest) - } - } - - if *s.AdminFilter != "" { - if _, err := ldap.CompileFilter(*s.AdminFilter); err != nil { - return NewAppError("LdapSettings.isValid", "ent.ldap.validate_admin_filter.app_error", nil, err.Error(), http.StatusBadRequest) - } - } - } - - return nil -} - -func (s *SamlSettings) isValid() *AppError { - if *s.Enable { - if *s.IdpUrl == "" || !IsValidHttpUrl(*s.IdpUrl) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_idp_url.app_error", nil, "", http.StatusBadRequest) - } - - if *s.IdpDescriptorUrl == "" || !IsValidHttpUrl(*s.IdpDescriptorUrl) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_idp_descriptor_url.app_error", nil, "", http.StatusBadRequest) - } - - if *s.IdpCertificateFile == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_idp_cert.app_error", nil, "", http.StatusBadRequest) - } - - if *s.EmailAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_email_attribute.app_error", nil, "", http.StatusBadRequest) - } - - if *s.UsernameAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_username_attribute.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ServiceProviderIdentifier == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_spidentifier_attribute.app_error", nil, "", http.StatusBadRequest) - } - - if *s.Verify { - if *s.AssertionConsumerServiceURL == "" || !IsValidHttpUrl(*s.AssertionConsumerServiceURL) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_assertion_consumer_service_url.app_error", nil, "", http.StatusBadRequest) - } - } - - if *s.Encrypt { - if *s.PrivateKeyFile == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_private_key.app_error", nil, "", http.StatusBadRequest) - } - - if *s.PublicCertificateFile == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_public_cert.app_error", nil, "", http.StatusBadRequest) - } - } - - if *s.EmailAttribute == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_email_attribute.app_error", nil, "", http.StatusBadRequest) - } - - if !(*s.SignatureAlgorithm == SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA1 || *s.SignatureAlgorithm == SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA256 || *s.SignatureAlgorithm == SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA512) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_signature_algorithm.app_error", nil, "", http.StatusBadRequest) - } - if !(*s.CanonicalAlgorithm == SAML_SETTINGS_CANONICAL_ALGORITHM_C14N || *s.CanonicalAlgorithm == SAML_SETTINGS_CANONICAL_ALGORITHM_C14N11) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_canonical_algorithm.app_error", nil, "", http.StatusBadRequest) - } - - if *s.GuestAttribute != "" { - if !(strings.Contains(*s.GuestAttribute, "=")) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_guest_attribute.app_error", nil, "", http.StatusBadRequest) - } - if len(strings.Split(*s.GuestAttribute, "=")) != 2 { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_guest_attribute.app_error", nil, "", http.StatusBadRequest) - } - } - - if *s.AdminAttribute != "" { - if !(strings.Contains(*s.AdminAttribute, "=")) { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_admin_attribute.app_error", nil, "", http.StatusBadRequest) - } - if len(strings.Split(*s.AdminAttribute, "=")) != 2 { - return NewAppError("Config.IsValid", "model.config.is_valid.saml_admin_attribute.app_error", nil, "", http.StatusBadRequest) - } - } - } - - return nil -} - -func (s *ServiceSettings) isValid() *AppError { - if !(*s.ConnectionSecurity == CONN_SECURITY_NONE || *s.ConnectionSecurity == CONN_SECURITY_TLS) { - return NewAppError("Config.IsValid", "model.config.is_valid.webserver_security.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ConnectionSecurity == CONN_SECURITY_TLS && !*s.UseLetsEncrypt { - appErr := NewAppError("Config.IsValid", "model.config.is_valid.tls_cert_file_missing.app_error", nil, "", http.StatusBadRequest) - - if *s.TLSCertFile == "" { - return appErr - } else if _, err := os.Stat(*s.TLSCertFile); os.IsNotExist(err) { - return appErr - } - - appErr = NewAppError("Config.IsValid", "model.config.is_valid.tls_key_file_missing.app_error", nil, "", http.StatusBadRequest) - - if *s.TLSKeyFile == "" { - return appErr - } else if _, err := os.Stat(*s.TLSKeyFile); os.IsNotExist(err) { - return appErr - } - } - - if len(s.TLSOverwriteCiphers) > 0 { - for _, cipher := range s.TLSOverwriteCiphers { - if _, ok := ServerTLSSupportedCiphers[cipher]; !ok { - return NewAppError("Config.IsValid", "model.config.is_valid.tls_overwrite_cipher.app_error", map[string]interface{}{"name": cipher}, "", http.StatusBadRequest) - } - } - } - - if *s.ReadTimeout <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.read_timeout.app_error", nil, "", http.StatusBadRequest) - } - - if *s.WriteTimeout <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "", http.StatusBadRequest) - } - - if *s.TimeBetweenUserTypingUpdatesMilliseconds < 1000 { - return NewAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "", http.StatusBadRequest) - } - - if *s.MaximumLoginAttempts <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.login_attempts.app_error", nil, "", http.StatusBadRequest) - } - - if *s.SiteURL != "" { - if _, err := url.ParseRequestURI(*s.SiteURL); err != nil { - return NewAppError("Config.IsValid", "model.config.is_valid.site_url.app_error", nil, "", http.StatusBadRequest) - } - } - - if *s.WebsocketURL != "" { - if _, err := url.ParseRequestURI(*s.WebsocketURL); err != nil { - return NewAppError("Config.IsValid", "model.config.is_valid.websocket_url.app_error", nil, "", http.StatusBadRequest) - } - } - - host, port, _ := net.SplitHostPort(*s.ListenAddress) - var isValidHost bool - if host == "" { - isValidHost = true - } else { - isValidHost = (net.ParseIP(host) != nil) || IsDomainName(host) - } - portInt, err := strconv.Atoi(port) - if err != nil || !isValidHost || portInt < 0 || portInt > math.MaxUint16 { - return NewAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DISABLED && - *s.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DEFAULT_ON && - *s.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DEFAULT_OFF { - return NewAppError("Config.IsValid", "model.config.is_valid.group_unread_channels.app_error", nil, "", http.StatusBadRequest) - } - - if *s.CollapsedThreads != COLLAPSED_THREADS_DISABLED && !*s.ThreadAutoFollow { - return NewAppError("Config.IsValid", "model.config.is_valid.collapsed_threads.autofollow.app_error", nil, "", http.StatusBadRequest) - } - - if *s.CollapsedThreads != COLLAPSED_THREADS_DISABLED && - *s.CollapsedThreads != COLLAPSED_THREADS_DEFAULT_ON && - *s.CollapsedThreads != COLLAPSED_THREADS_DEFAULT_OFF { - return NewAppError("Config.IsValid", "model.config.is_valid.collapsed_threads.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *ElasticsearchSettings) isValid() *AppError { - if *s.EnableIndexing { - if *s.ConnectionUrl == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.connection_url.app_error", nil, "", http.StatusBadRequest) - } - } - - if *s.EnableSearching && !*s.EnableIndexing { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.enable_searching.app_error", nil, "", http.StatusBadRequest) - } - - if *s.EnableAutocomplete && !*s.EnableIndexing { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.enable_autocomplete.app_error", nil, "", http.StatusBadRequest) - } - - if *s.AggregatePostsAfterDays < 1 { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.aggregate_posts_after_days.app_error", nil, "", http.StatusBadRequest) - } - - if _, err := time.Parse("15:04", *s.PostsAggregatorJobStartTime); err != nil { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.posts_aggregator_job_start_time.app_error", nil, err.Error(), http.StatusBadRequest) - } - - if *s.LiveIndexingBatchSize < 1 { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.live_indexing_batch_size.app_error", nil, "", http.StatusBadRequest) - } - - if *s.BulkIndexingTimeWindowSeconds < 1 { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.bulk_indexing_time_window_seconds.app_error", nil, "", http.StatusBadRequest) - } - - if *s.RequestTimeoutSeconds < 1 { - return NewAppError("Config.IsValid", "model.config.is_valid.elastic_search.request_timeout_seconds.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (bs *BleveSettings) isValid() *AppError { - if *bs.EnableIndexing { - if *bs.IndexDir == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.bleve_search.filename.app_error", nil, "", http.StatusBadRequest) - } - } else { - if *bs.EnableSearching { - return NewAppError("Config.IsValid", "model.config.is_valid.bleve_search.enable_searching.app_error", nil, "", http.StatusBadRequest) - } - if *bs.EnableAutocomplete { - return NewAppError("Config.IsValid", "model.config.is_valid.bleve_search.enable_autocomplete.app_error", nil, "", http.StatusBadRequest) - } - } - if *bs.BulkIndexingTimeWindowSeconds < 1 { - return NewAppError("Config.IsValid", "model.config.is_valid.bleve_search.bulk_indexing_time_window_seconds.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (s *DataRetentionSettings) isValid() *AppError { - if *s.MessageRetentionDays <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.message_retention_days_too_low.app_error", nil, "", http.StatusBadRequest) - } - - if *s.FileRetentionDays <= 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.file_retention_days_too_low.app_error", nil, "", http.StatusBadRequest) - } - - if _, err := time.Parse("15:04", *s.DeletionJobStartTime); err != nil { - return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.deletion_job_start_time.app_error", nil, err.Error(), http.StatusBadRequest) - } - - return nil -} - -func (s *LocalizationSettings) isValid() *AppError { - if *s.AvailableLocales != "" { - if !strings.Contains(*s.AvailableLocales, *s.DefaultClientLocale) { - return NewAppError("Config.IsValid", "model.config.is_valid.localization.available_locales.app_error", nil, "", http.StatusBadRequest) - } - } - - return nil -} - -func (s *MessageExportSettings) isValid() *AppError { - if s.EnableExport == nil { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.enable.app_error", nil, "", http.StatusBadRequest) - } - if *s.EnableExport { - if s.ExportFromTimestamp == nil || *s.ExportFromTimestamp < 0 || *s.ExportFromTimestamp > GetMillis() { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.export_from.app_error", nil, "", http.StatusBadRequest) - } else if s.DailyRunTime == nil { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.daily_runtime.app_error", nil, "", http.StatusBadRequest) - } else if _, err := time.Parse("15:04", *s.DailyRunTime); err != nil { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.daily_runtime.app_error", nil, err.Error(), http.StatusBadRequest) - } else if s.BatchSize == nil || *s.BatchSize < 0 { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.batch_size.app_error", nil, "", http.StatusBadRequest) - } else if s.ExportFormat == nil || (*s.ExportFormat != COMPLIANCE_EXPORT_TYPE_ACTIANCE && *s.ExportFormat != COMPLIANCE_EXPORT_TYPE_GLOBALRELAY && *s.ExportFormat != COMPLIANCE_EXPORT_TYPE_CSV) { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.export_type.app_error", nil, "", http.StatusBadRequest) - } - - if *s.ExportFormat == COMPLIANCE_EXPORT_TYPE_GLOBALRELAY { - if s.GlobalRelaySettings == nil { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.config_missing.app_error", nil, "", http.StatusBadRequest) - } else if s.GlobalRelaySettings.CustomerType == nil || (*s.GlobalRelaySettings.CustomerType != GLOBALRELAY_CUSTOMER_TYPE_A9 && *s.GlobalRelaySettings.CustomerType != GLOBALRELAY_CUSTOMER_TYPE_A10) { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.customer_type.app_error", nil, "", http.StatusBadRequest) - } else if s.GlobalRelaySettings.EmailAddress == nil || !strings.Contains(*s.GlobalRelaySettings.EmailAddress, "@") { - // validating email addresses is hard - just make sure it contains an '@' sign - // see https://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.email_address.app_error", nil, "", http.StatusBadRequest) - } else if s.GlobalRelaySettings.SmtpUsername == nil || *s.GlobalRelaySettings.SmtpUsername == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.smtp_username.app_error", nil, "", http.StatusBadRequest) - } else if s.GlobalRelaySettings.SmtpPassword == nil || *s.GlobalRelaySettings.SmtpPassword == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.smtp_password.app_error", nil, "", http.StatusBadRequest) - } - } - } - return nil -} - -func (s *DisplaySettings) isValid() *AppError { - if len(s.CustomUrlSchemes) != 0 { - validProtocolPattern := regexp.MustCompile(`(?i)^\s*[A-Za-z][A-Za-z0-9.+-]*\s*$`) - - for _, scheme := range s.CustomUrlSchemes { - if !validProtocolPattern.MatchString(scheme) { - return NewAppError( - "Config.IsValid", - "model.config.is_valid.display.custom_url_schemes.app_error", - map[string]interface{}{"Scheme": scheme}, - "", - http.StatusBadRequest, - ) - } - } - } - - return nil -} - -func (s *ImageProxySettings) isValid() *AppError { - if *s.Enable { - switch *s.ImageProxyType { - case IMAGE_PROXY_TYPE_LOCAL: - // No other settings to validate - case IMAGE_PROXY_TYPE_ATMOS_CAMO: - if *s.RemoteImageProxyURL == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_url.app_error", nil, "", http.StatusBadRequest) - } - - if *s.RemoteImageProxyOptions == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_options.app_error", nil, "", http.StatusBadRequest) - } - default: - return NewAppError("Config.IsValid", "model.config.is_valid.image_proxy_type.app_error", nil, "", http.StatusBadRequest) - } - } - - return nil -} - -func (o *Config) GetSanitizeOptions() map[string]bool { - options := map[string]bool{} - options["fullname"] = *o.PrivacySettings.ShowFullName - options["email"] = *o.PrivacySettings.ShowEmailAddress - - return options -} - -func (o *Config) Sanitize() { - if o.LdapSettings.BindPassword != nil && *o.LdapSettings.BindPassword != "" { - *o.LdapSettings.BindPassword = FAKE_SETTING - } - - if o.FileSettings.PublicLinkSalt != nil { - *o.FileSettings.PublicLinkSalt = FAKE_SETTING - } - - if o.FileSettings.AmazonS3SecretAccessKey != nil && *o.FileSettings.AmazonS3SecretAccessKey != "" { - *o.FileSettings.AmazonS3SecretAccessKey = FAKE_SETTING - } - - if o.EmailSettings.SMTPPassword != nil && *o.EmailSettings.SMTPPassword != "" { - *o.EmailSettings.SMTPPassword = FAKE_SETTING - } - - if o.GitLabSettings.Secret != nil && *o.GitLabSettings.Secret != "" { - *o.GitLabSettings.Secret = FAKE_SETTING - } - - if o.GoogleSettings.Secret != nil && *o.GoogleSettings.Secret != "" { - *o.GoogleSettings.Secret = FAKE_SETTING - } - - if o.Office365Settings.Secret != nil && *o.Office365Settings.Secret != "" { - *o.Office365Settings.Secret = FAKE_SETTING - } - - if o.OpenIdSettings.Secret != nil && *o.OpenIdSettings.Secret != "" { - *o.OpenIdSettings.Secret = FAKE_SETTING - } - - if o.SqlSettings.DataSource != nil { - *o.SqlSettings.DataSource = FAKE_SETTING - } - - if o.SqlSettings.AtRestEncryptKey != nil { - *o.SqlSettings.AtRestEncryptKey = FAKE_SETTING - } - - if o.ElasticsearchSettings.Password != nil { - *o.ElasticsearchSettings.Password = FAKE_SETTING - } - - for i := range o.SqlSettings.DataSourceReplicas { - o.SqlSettings.DataSourceReplicas[i] = FAKE_SETTING - } - - for i := range o.SqlSettings.DataSourceSearchReplicas { - o.SqlSettings.DataSourceSearchReplicas[i] = FAKE_SETTING - } - - if o.MessageExportSettings.GlobalRelaySettings != nil && - o.MessageExportSettings.GlobalRelaySettings.SmtpPassword != nil && - *o.MessageExportSettings.GlobalRelaySettings.SmtpPassword != "" { - *o.MessageExportSettings.GlobalRelaySettings.SmtpPassword = FAKE_SETTING - } - - if o.ServiceSettings.GfycatApiSecret != nil && *o.ServiceSettings.GfycatApiSecret != "" { - *o.ServiceSettings.GfycatApiSecret = FAKE_SETTING - } - - if o.ServiceSettings.SplitKey != nil { - *o.ServiceSettings.SplitKey = FAKE_SETTING - } -} - -// structToMapFilteredByTag converts a struct into a map removing those fields that has the tag passed -// as argument -func structToMapFilteredByTag(t interface{}, typeOfTag, filterTag string) map[string]interface{} { - defer func() { - if r := recover(); r != nil { - mlog.Warn("Panicked in structToMapFilteredByTag. This should never happen.", mlog.Any("recover", r)) - } - }() - - val := reflect.ValueOf(t) - elemField := reflect.TypeOf(t) - - if val.Kind() != reflect.Struct { - return nil - } - - out := map[string]interface{}{} - - for i := 0; i < val.NumField(); i++ { - field := val.Field(i) - - structField := elemField.Field(i) - tagPermissions := strings.Split(structField.Tag.Get(typeOfTag), ",") - if isTagPresent(filterTag, tagPermissions) { - continue - } - - var value interface{} - - switch field.Kind() { - case reflect.Struct: - value = structToMapFilteredByTag(field.Interface(), typeOfTag, filterTag) - case reflect.Ptr: - indirectType := field.Elem() - if indirectType.Kind() == reflect.Struct { - value = structToMapFilteredByTag(indirectType.Interface(), typeOfTag, filterTag) - } else if indirectType.Kind() != reflect.Invalid { - value = indirectType.Interface() - } - default: - value = field.Interface() - } - - out[val.Type().Field(i).Name] = value - } - - return out -} - -func isTagPresent(tag string, tags []string) bool { - for _, val := range tags { - tagValue := strings.TrimSpace(val) - if tagValue != "" && tagValue == tag { - return true - } - } - - return false -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/custom_status.go b/vendor/github.com/mattermost/mattermost-server/v5/model/custom_status.go deleted file mode 100644 index 9898ce6c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/custom_status.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "time" -) - -const ( - UserPropsKeyCustomStatus = "customStatus" - - CustomStatusTextMaxRunes = 100 - MaxRecentCustomStatuses = 5 - DefaultCustomStatusEmoji = "speech_balloon" -) - -var validCustomStatusDuration = map[string]bool{ - "thirty_minutes": true, - "one_hour": true, - "four_hours": true, - "today": true, - "this_week": true, - "date_and_time": true, -} - -type CustomStatus struct { - Emoji string `json:"emoji"` - Text string `json:"text"` - Duration string `json:"duration"` - ExpiresAt time.Time `json:"expires_at"` -} - -func (cs *CustomStatus) PreSave() { - if cs.Emoji == "" { - cs.Emoji = DefaultCustomStatusEmoji - } - - if cs.Duration == "" && !cs.ExpiresAt.Before(time.Now()) { - cs.Duration = "date_and_time" - } - - runes := []rune(cs.Text) - if len(runes) > CustomStatusTextMaxRunes { - cs.Text = string(runes[:CustomStatusTextMaxRunes]) - } -} - -func (cs *CustomStatus) ToJson() string { - csCopy := *cs - b, _ := json.Marshal(csCopy) - return string(b) -} - -func (cs *CustomStatus) AreDurationAndExpirationTimeValid() bool { - if cs.Duration == "" && (cs.ExpiresAt.IsZero() || !cs.ExpiresAt.Before(time.Now())) { - return true - } - - if validCustomStatusDuration[cs.Duration] && !cs.ExpiresAt.Before(time.Now()) { - return true - } - - return false -} - -func CustomStatusFromJson(data io.Reader) *CustomStatus { - var cs *CustomStatus - _ = json.NewDecoder(data).Decode(&cs) - return cs -} - -func RuneToHexadecimalString(r rune) string { - return fmt.Sprintf("%04x", r) -} - -type RecentCustomStatuses []CustomStatus - -func (rcs *RecentCustomStatuses) Contains(cs *CustomStatus) bool { - var csJSON = cs.ToJson() - - // status is empty - if cs == nil || csJSON == "" || (cs.Emoji == "" && cs.Text == "") { - return false - } - - for _, status := range *rcs { - if status.ToJson() == csJSON { - return true - } - } - - return false -} - -func (rcs *RecentCustomStatuses) Add(cs *CustomStatus) *RecentCustomStatuses { - newRCS := (*rcs)[:0] - - // if same `text` exists in existing recent custom statuses, modify existing status - for _, status := range *rcs { - if status.Text != cs.Text { - newRCS = append(newRCS, status) - } - } - newRCS = append(RecentCustomStatuses{*cs}, newRCS...) - if len(newRCS) > MaxRecentCustomStatuses { - newRCS = newRCS[:MaxRecentCustomStatuses] - } - return &newRCS -} - -func (rcs *RecentCustomStatuses) Remove(cs *CustomStatus) *RecentCustomStatuses { - var csJSON = cs.ToJson() - if csJSON == "" || (cs.Emoji == "" && cs.Text == "") { - return rcs - } - - newRCS := (*rcs)[:0] - for _, status := range *rcs { - if status.ToJson() != csJSON { - newRCS = append(newRCS, status) - } - } - - return &newRCS -} - -func (rcs *RecentCustomStatuses) ToJson() string { - rcsCopy := *rcs - b, _ := json.Marshal(rcsCopy) - return string(b) -} - -func RecentCustomStatusesFromJson(data io.Reader) *RecentCustomStatuses { - var rcs *RecentCustomStatuses - _ = json.NewDecoder(data).Decode(&rcs) - return rcs -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/data_retention_policy.go b/vendor/github.com/mattermost/mattermost-server/v5/model/data_retention_policy.go deleted file mode 100644 index 3f984d1b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/data_retention_policy.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type GlobalRetentionPolicy struct { - MessageDeletionEnabled bool `json:"message_deletion_enabled"` - FileDeletionEnabled bool `json:"file_deletion_enabled"` - MessageRetentionCutoff int64 `json:"message_retention_cutoff"` - FileRetentionCutoff int64 `json:"file_retention_cutoff"` -} - -type RetentionPolicy struct { - ID string `db:"Id" json:"id"` - DisplayName string `json:"display_name"` - PostDuration *int64 `json:"post_duration"` -} - -type RetentionPolicyWithTeamAndChannelIDs struct { - RetentionPolicy - TeamIDs []string `json:"team_ids"` - ChannelIDs []string `json:"channel_ids"` -} - -type RetentionPolicyWithTeamAndChannelCounts struct { - RetentionPolicy - ChannelCount int64 `json:"channel_count"` - TeamCount int64 `json:"team_count"` -} - -type RetentionPolicyChannel struct { - PolicyID string `db:"PolicyId"` - ChannelID string `db:"ChannelId"` -} - -type RetentionPolicyTeam struct { - PolicyID string `db:"PolicyId"` - TeamID string `db:"TeamId"` -} - -type RetentionPolicyWithTeamAndChannelCountsList struct { - Policies []*RetentionPolicyWithTeamAndChannelCounts `json:"policies"` - TotalCount int64 `json:"total_count"` -} - -type RetentionPolicyForTeam struct { - TeamID string `db:"Id" json:"team_id"` - PostDuration int64 `json:"post_duration"` -} - -type RetentionPolicyForTeamList struct { - Policies []*RetentionPolicyForTeam `json:"policies"` - TotalCount int64 `json:"total_count"` -} - -type RetentionPolicyForChannel struct { - ChannelID string `db:"Id" json:"channel_id"` - PostDuration int64 `json:"post_duration"` -} - -type RetentionPolicyForChannelList struct { - Policies []*RetentionPolicyForChannel `json:"policies"` - TotalCount int64 `json:"total_count"` -} - -type RetentionPolicyCursor struct { - ChannelPoliciesDone bool - TeamPoliciesDone bool - GlobalPoliciesDone bool -} - -func (rp *GlobalRetentionPolicy) ToJson() []byte { - b, _ := json.Marshal(rp) - return b -} - -func GlobalRetentionPolicyFromJson(data io.Reader) *GlobalRetentionPolicy { - var grp *GlobalRetentionPolicy - json.NewDecoder(data).Decode(&grp) - return grp -} - -func RetentionPolicyWithTeamAndChannelCountsFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelCounts, error) { - var rp RetentionPolicyWithTeamAndChannelCounts - err := json.NewDecoder(data).Decode(&rp) - return &rp, err -} - -func (rp *RetentionPolicyWithTeamAndChannelCounts) ToJson() []byte { - b, _ := json.Marshal(rp) - return b -} - -func RetentionPolicyWithTeamAndChannelCountsListFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelCountsList, error) { - var rpList *RetentionPolicyWithTeamAndChannelCountsList - err := json.NewDecoder(data).Decode(&rpList) - if err != nil { - return nil, err - } - return rpList, nil -} - -func (rpList *RetentionPolicyWithTeamAndChannelCountsList) ToJson() []byte { - b, _ := json.Marshal(rpList) - return b -} - -func RetentionPolicyWithTeamAndChannelIdsFromJson(data io.Reader) (*RetentionPolicyWithTeamAndChannelIDs, error) { - var rp *RetentionPolicyWithTeamAndChannelIDs - err := json.NewDecoder(data).Decode(&rp) - return rp, err -} - -func (rp *RetentionPolicyWithTeamAndChannelIDs) ToJson() []byte { - b, _ := json.Marshal(rp) - return b -} - -func (lst *RetentionPolicyForTeamList) ToJson() []byte { - b, _ := json.Marshal(lst) - return b -} - -func (lst *RetentionPolicyForChannelList) ToJson() []byte { - b, _ := json.Marshal(lst) - return b -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji.go b/vendor/github.com/mattermost/mattermost-server/v5/model/emoji.go deleted file mode 100644 index f990c670..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "regexp" - "sort" -) - -const ( - EMOJI_NAME_MAX_LENGTH = 64 - EMOJI_SORT_BY_NAME = "name" -) - -var EMOJI_PATTERN = regexp.MustCompile(`:[a-zA-Z0-9_+-]+:`) - -var ReverseSystemEmojisMap = makeReverseEmojiMap() - -type Emoji struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - CreatorId string `json:"creator_id"` - Name string `json:"name"` -} - -func inSystemEmoji(emojiName string) bool { - _, ok := SystemEmojis[emojiName] - return ok -} - -func GetSystemEmojiId(emojiName string) (string, bool) { - id, found := SystemEmojis[emojiName] - return id, found -} - -func makeReverseEmojiMap() map[string][]string { - reverseEmojiMap := make(map[string][]string) - for key, value := range SystemEmojis { - emojiNames := reverseEmojiMap[value] - emojiNames = append(emojiNames, key) - sort.Strings(emojiNames) - reverseEmojiMap[value] = emojiNames - } - - return reverseEmojiMap -} - -func GetEmojiNameFromUnicode(unicode string) (emojiName string, count int) { - if emojiNames, found := ReverseSystemEmojisMap[unicode]; found { - return emojiNames[0], len(emojiNames) - } - - return "", 0 -} - -func (emoji *Emoji) IsValid() *AppError { - if !IsValidId(emoji.Id) { - return NewAppError("Emoji.IsValid", "model.emoji.id.app_error", nil, "", http.StatusBadRequest) - } - - if emoji.CreateAt == 0 { - return NewAppError("Emoji.IsValid", "model.emoji.create_at.app_error", nil, "id="+emoji.Id, http.StatusBadRequest) - } - - if emoji.UpdateAt == 0 { - return NewAppError("Emoji.IsValid", "model.emoji.update_at.app_error", nil, "id="+emoji.Id, http.StatusBadRequest) - } - - if len(emoji.CreatorId) > 26 { - return NewAppError("Emoji.IsValid", "model.emoji.user_id.app_error", nil, "", http.StatusBadRequest) - } - - return IsValidEmojiName(emoji.Name) -} - -func IsValidEmojiName(name string) *AppError { - if name == "" || len(name) > EMOJI_NAME_MAX_LENGTH || !IsValidAlphaNumHyphenUnderscorePlus(name) || inSystemEmoji(name) { - return NewAppError("Emoji.IsValid", "model.emoji.name.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (emoji *Emoji) PreSave() { - if emoji.Id == "" { - emoji.Id = NewId() - } - - emoji.CreateAt = GetMillis() - emoji.UpdateAt = emoji.CreateAt -} - -func (emoji *Emoji) ToJson() string { - b, _ := json.Marshal(emoji) - return string(b) -} - -func EmojiFromJson(data io.Reader) *Emoji { - var emoji *Emoji - json.NewDecoder(data).Decode(&emoji) - return emoji -} - -func EmojiListToJson(emojiList []*Emoji) string { - b, _ := json.Marshal(emojiList) - return string(b) -} - -func EmojiListFromJson(data io.Reader) []*Emoji { - var emojiList []*Emoji - json.NewDecoder(data).Decode(&emojiList) - return emojiList -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_data.go b/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_data.go deleted file mode 100644 index 849c2046..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_data.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -// This file is automatically generated via `make emojis`. Do not modify it manually. - -package model - -var SystemEmojis = map[string]string{"grinning": "1f600", "smiley": "1f603", "smile": "1f604", "grin": "1f601", "laughing": "1f606", "satisfied": "1f606", "sweat_smile": "1f605", "rolling_on_the_floor_laughing": "1f923", "rofl": "1f923", "joy": "1f602", "slightly_smiling_face": "1f642", "upside_down_face": "1f643", "wink": "1f609", "blush": "1f60a", "innocent": "1f607", "smiling_face_with_3_hearts": "1f970", "heart_eyes": "1f60d", "star-struck": "1f929", "grinning_face_with_star_eyes": "1f929", "kissing_heart": "1f618", "kissing": "1f617", "relaxed": "263a-fe0f", "kissing_closed_eyes": "1f61a", "kissing_smiling_eyes": "1f619", "smiling_face_with_tear": "1f972", "yum": "1f60b", "stuck_out_tongue": "1f61b", "stuck_out_tongue_winking_eye": "1f61c", "zany_face": "1f92a", "grinning_face_with_one_large_and_one_small_eye": "1f92a", "stuck_out_tongue_closed_eyes": "1f61d", "money_mouth_face": "1f911", "hugging_face": "1f917", "hugs": "1f917", "face_with_hand_over_mouth": "1f92d", "smiling_face_with_smiling_eyes_and_hand_covering_mouth": "1f92d", "shushing_face": "1f92b", "face_with_finger_covering_closed_lips": "1f92b", "thinking_face": "1f914", "thinking": "1f914", "zipper_mouth_face": "1f910", "face_with_raised_eyebrow": "1f928", "face_with_one_eyebrow_raised": "1f928", "neutral_face": "1f610", "expressionless": "1f611", "no_mouth": "1f636", "smirk": "1f60f", "unamused": "1f612", "face_with_rolling_eyes": "1f644", "roll_eyes": "1f644", "grimacing": "1f62c", "lying_face": "1f925", "relieved": "1f60c", "pensive": "1f614", "sleepy": "1f62a", "drooling_face": "1f924", "sleeping": "1f634", "mask": "1f637", "face_with_thermometer": "1f912", "face_with_head_bandage": "1f915", "nauseated_face": "1f922", "face_vomiting": "1f92e", "face_with_open_mouth_vomiting": "1f92e", "sneezing_face": "1f927", "hot_face": "1f975", "cold_face": "1f976", "woozy_face": "1f974", "dizzy_face": "1f635", "exploding_head": "1f92f", "shocked_face_with_exploding_head": "1f92f", "face_with_cowboy_hat": "1f920", "cowboy_hat_face": "1f920", "partying_face": "1f973", "disguised_face": "1f978", "sunglasses": "1f60e", "nerd_face": "1f913", "face_with_monocle": "1f9d0", "confused": "1f615", "worried": "1f61f", "slightly_frowning_face": "1f641", "white_frowning_face": "2639-fe0f", "frowning_face": "2639-fe0f", "open_mouth": "1f62e", "hushed": "1f62f", "astonished": "1f632", "flushed": "1f633", "pleading_face": "1f97a", "frowning": "1f626", "anguished": "1f627", "fearful": "1f628", "cold_sweat": "1f630", "disappointed_relieved": "1f625", "cry": "1f622", "sob": "1f62d", "scream": "1f631", "confounded": "1f616", "persevere": "1f623", "disappointed": "1f61e", "sweat": "1f613", "weary": "1f629", "tired_face": "1f62b", "yawning_face": "1f971", "triumph": "1f624", "rage": "1f621", "pout": "1f621", "angry": "1f620", "face_with_symbols_on_mouth": "1f92c", "serious_face_with_symbols_covering_mouth": "1f92c", "smiling_imp": "1f608", "imp": "1f47f", "skull": "1f480", "skull_and_crossbones": "2620-fe0f", "hankey": "1f4a9", "poop": "1f4a9", "shit": "1f4a9", "clown_face": "1f921", "japanese_ogre": "1f479", "japanese_goblin": "1f47a", "ghost": "1f47b", "alien": "1f47d", "space_invader": "1f47e", "robot_face": "1f916", "robot": "1f916", "smiley_cat": "1f63a", "smile_cat": "1f638", "joy_cat": "1f639", "heart_eyes_cat": "1f63b", "smirk_cat": "1f63c", "kissing_cat": "1f63d", "scream_cat": "1f640", "crying_cat_face": "1f63f", "pouting_cat": "1f63e", "see_no_evil": "1f648", "hear_no_evil": "1f649", "speak_no_evil": "1f64a", "kiss": "1f48b", "love_letter": "1f48c", "cupid": "1f498", "gift_heart": "1f49d", "sparkling_heart": "1f496", "heartpulse": "1f497", "heartbeat": "1f493", "revolving_hearts": "1f49e", "two_hearts": "1f495", "heart_decoration": "1f49f", "heavy_heart_exclamation_mark_ornament": "2763-fe0f", "heavy_heart_exclamation": "2763-fe0f", "broken_heart": "1f494", "heart": "2764-fe0f", "orange_heart": "1f9e1", "yellow_heart": "1f49b", "green_heart": "1f49a", "blue_heart": "1f499", "purple_heart": "1f49c", "brown_heart": "1f90e", "black_heart": "1f5a4", "white_heart": "1f90d", "100": "1f4af", "anger": "1f4a2", "boom": "1f4a5", "collision": "1f4a5", "dizzy": "1f4ab", "sweat_drops": "1f4a6", "dash": "1f4a8", "hole": "1f573-fe0f", "bomb": "1f4a3", "speech_balloon": "1f4ac", "eye-in-speech-bubble": "1f441-fe0f-200d-1f5e8-fe0f", "left_speech_bubble": "1f5e8-fe0f", "right_anger_bubble": "1f5ef-fe0f", "thought_balloon": "1f4ad", "zzz": "1f4a4", "wave": "1f44b", "raised_back_of_hand": "1f91a", "raised_hand_with_fingers_splayed": "1f590-fe0f", "hand": "270b", "raised_hand": "270b", "spock-hand": "1f596", "vulcan_salute": "1f596", "ok_hand": "1f44c", "pinched_fingers": "1f90c", "pinching_hand": "1f90f", "v": "270c-fe0f", "crossed_fingers": "1f91e", "hand_with_index_and_middle_fingers_crossed": "1f91e", "i_love_you_hand_sign": "1f91f", "the_horns": "1f918", "sign_of_the_horns": "1f918", "metal": "1f918", "call_me_hand": "1f919", "point_left": "1f448", "point_right": "1f449", "point_up_2": "1f446", "middle_finger": "1f595", "reversed_hand_with_middle_finger_extended": "1f595", "fu": "1f595", "point_down": "1f447", "point_up": "261d-fe0f", "+1": "1f44d", "thumbsup": "1f44d", "-1": "1f44e", "thumbsdown": "1f44e", "fist": "270a", "fist_raised": "270a", "facepunch": "1f44a", "punch": "1f44a", "fist_oncoming": "1f44a", "left-facing_fist": "1f91b", "fist_left": "1f91b", "right-facing_fist": "1f91c", "fist_right": "1f91c", "clap": "1f44f", "raised_hands": "1f64c", "open_hands": "1f450", "palms_up_together": "1f932", "handshake": "1f91d", "pray": "1f64f", "writing_hand": "270d-fe0f", "nail_care": "1f485", "selfie": "1f933", "muscle": "1f4aa", "mechanical_arm": "1f9be", "mechanical_leg": "1f9bf", "leg": "1f9b5", "foot": "1f9b6", "ear": "1f442", "ear_with_hearing_aid": "1f9bb", "nose": "1f443", "brain": "1f9e0", "anatomical_heart": "1fac0", "lungs": "1fac1", "tooth": "1f9b7", "bone": "1f9b4", "eyes": "1f440", "eye": "1f441-fe0f", "tongue": "1f445", "lips": "1f444", "baby": "1f476", "child": "1f9d2", "boy": "1f466", "girl": "1f467", "adult": "1f9d1", "person_with_blond_hair": "1f471", "man": "1f468", "bearded_person": "1f9d4", "red_haired_man": "1f468-200d-1f9b0", "curly_haired_man": "1f468-200d-1f9b1", "white_haired_man": "1f468-200d-1f9b3", "bald_man": "1f468-200d-1f9b2", "woman": "1f469", "red_haired_woman": "1f469-200d-1f9b0", "red_haired_person": "1f9d1-200d-1f9b0", "curly_haired_woman": "1f469-200d-1f9b1", "curly_haired_person": "1f9d1-200d-1f9b1", "white_haired_woman": "1f469-200d-1f9b3", "white_haired_person": "1f9d1-200d-1f9b3", "bald_woman": "1f469-200d-1f9b2", "bald_person": "1f9d1-200d-1f9b2", "blond-haired-woman": "1f471-200d-2640-fe0f", "blonde_woman": "1f471-200d-2640-fe0f", "blond-haired-man": "1f471-200d-2642-fe0f", "blonde_man": "1f471-200d-2642-fe0f", "older_adult": "1f9d3", "older_man": "1f474", "older_woman": "1f475", "person_frowning": "1f64d", "man-frowning": "1f64d-200d-2642-fe0f", "frowning_man": "1f64d-200d-2642-fe0f", "woman-frowning": "1f64d-200d-2640-fe0f", "frowning_woman": "1f64d-200d-2640-fe0f", "person_with_pouting_face": "1f64e", "man-pouting": "1f64e-200d-2642-fe0f", "pouting_man": "1f64e-200d-2642-fe0f", "woman-pouting": "1f64e-200d-2640-fe0f", "pouting_woman": "1f64e-200d-2640-fe0f", "no_good": "1f645", "man-gesturing-no": "1f645-200d-2642-fe0f", "ng_man": "1f645-200d-2642-fe0f", "no_good_man": "1f645-200d-2642-fe0f", "woman-gesturing-no": "1f645-200d-2640-fe0f", "no_good_woman": "1f645-200d-2640-fe0f", "ng_woman": "1f645-200d-2640-fe0f", "ok_woman": "1f646", "man-gesturing-ok": "1f646-200d-2642-fe0f", "ok_man": "1f646-200d-2642-fe0f", "woman-gesturing-ok": "1f646-200d-2640-fe0f", "information_desk_person": "1f481", "man-tipping-hand": "1f481-200d-2642-fe0f", "tipping_hand_man": "1f481-200d-2642-fe0f", "woman-tipping-hand": "1f481-200d-2640-fe0f", "tipping_hand_woman": "1f481-200d-2640-fe0f", "raising_hand": "1f64b", "man-raising-hand": "1f64b-200d-2642-fe0f", "raising_hand_man": "1f64b-200d-2642-fe0f", "woman-raising-hand": "1f64b-200d-2640-fe0f", "raising_hand_woman": "1f64b-200d-2640-fe0f", "deaf_person": "1f9cf", "deaf_man": "1f9cf-200d-2642-fe0f", "deaf_woman": "1f9cf-200d-2640-fe0f", "bow": "1f647", "man-bowing": "1f647-200d-2642-fe0f", "bowing_man": "1f647-200d-2642-fe0f", "woman-bowing": "1f647-200d-2640-fe0f", "bowing_woman": "1f647-200d-2640-fe0f", "face_palm": "1f926", "man-facepalming": "1f926-200d-2642-fe0f", "man_facepalming": "1f926-200d-2642-fe0f", "woman-facepalming": "1f926-200d-2640-fe0f", "woman_facepalming": "1f926-200d-2640-fe0f", "shrug": "1f937", "man-shrugging": "1f937-200d-2642-fe0f", "man_shrugging": "1f937-200d-2642-fe0f", "woman-shrugging": "1f937-200d-2640-fe0f", "woman_shrugging": "1f937-200d-2640-fe0f", "health_worker": "1f9d1-200d-2695-fe0f", "male-doctor": "1f468-200d-2695-fe0f", "man_health_worker": "1f468-200d-2695-fe0f", "female-doctor": "1f469-200d-2695-fe0f", "woman_health_worker": "1f469-200d-2695-fe0f", "student": "1f9d1-200d-1f393", "male-student": "1f468-200d-1f393", "man_student": "1f468-200d-1f393", "female-student": "1f469-200d-1f393", "woman_student": "1f469-200d-1f393", "teacher": "1f9d1-200d-1f3eb", "male-teacher": "1f468-200d-1f3eb", "man_teacher": "1f468-200d-1f3eb", "female-teacher": "1f469-200d-1f3eb", "woman_teacher": "1f469-200d-1f3eb", "judge": "1f9d1-200d-2696-fe0f", "male-judge": "1f468-200d-2696-fe0f", "man_judge": "1f468-200d-2696-fe0f", "female-judge": "1f469-200d-2696-fe0f", "woman_judge": "1f469-200d-2696-fe0f", "farmer": "1f9d1-200d-1f33e", "male-farmer": "1f468-200d-1f33e", "man_farmer": "1f468-200d-1f33e", "female-farmer": "1f469-200d-1f33e", "woman_farmer": "1f469-200d-1f33e", "cook": "1f9d1-200d-1f373", "male-cook": "1f468-200d-1f373", "man_cook": "1f468-200d-1f373", "female-cook": "1f469-200d-1f373", "woman_cook": "1f469-200d-1f373", "mechanic": "1f9d1-200d-1f527", "male-mechanic": "1f468-200d-1f527", "man_mechanic": "1f468-200d-1f527", "female-mechanic": "1f469-200d-1f527", "woman_mechanic": "1f469-200d-1f527", "factory_worker": "1f9d1-200d-1f3ed", "male-factory-worker": "1f468-200d-1f3ed", "man_factory_worker": "1f468-200d-1f3ed", "female-factory-worker": "1f469-200d-1f3ed", "woman_factory_worker": "1f469-200d-1f3ed", "office_worker": "1f9d1-200d-1f4bc", "male-office-worker": "1f468-200d-1f4bc", "man_office_worker": "1f468-200d-1f4bc", "female-office-worker": "1f469-200d-1f4bc", "woman_office_worker": "1f469-200d-1f4bc", "scientist": "1f9d1-200d-1f52c", "male-scientist": "1f468-200d-1f52c", "man_scientist": "1f468-200d-1f52c", "female-scientist": "1f469-200d-1f52c", "woman_scientist": "1f469-200d-1f52c", "technologist": "1f9d1-200d-1f4bb", "male-technologist": "1f468-200d-1f4bb", "man_technologist": "1f468-200d-1f4bb", "female-technologist": "1f469-200d-1f4bb", "woman_technologist": "1f469-200d-1f4bb", "singer": "1f9d1-200d-1f3a4", "male-singer": "1f468-200d-1f3a4", "man_singer": "1f468-200d-1f3a4", "female-singer": "1f469-200d-1f3a4", "woman_singer": "1f469-200d-1f3a4", "artist": "1f9d1-200d-1f3a8", "male-artist": "1f468-200d-1f3a8", "man_artist": "1f468-200d-1f3a8", "female-artist": "1f469-200d-1f3a8", "woman_artist": "1f469-200d-1f3a8", "pilot": "1f9d1-200d-2708-fe0f", "male-pilot": "1f468-200d-2708-fe0f", "man_pilot": "1f468-200d-2708-fe0f", "female-pilot": "1f469-200d-2708-fe0f", "woman_pilot": "1f469-200d-2708-fe0f", "astronaut": "1f9d1-200d-1f680", "male-astronaut": "1f468-200d-1f680", "man_astronaut": "1f468-200d-1f680", "female-astronaut": "1f469-200d-1f680", "woman_astronaut": "1f469-200d-1f680", "firefighter": "1f9d1-200d-1f692", "male-firefighter": "1f468-200d-1f692", "man_firefighter": "1f468-200d-1f692", "female-firefighter": "1f469-200d-1f692", "woman_firefighter": "1f469-200d-1f692", "cop": "1f46e", "male-police-officer": "1f46e-200d-2642-fe0f", "policeman": "1f46e-200d-2642-fe0f", "female-police-officer": "1f46e-200d-2640-fe0f", "policewoman": "1f46e-200d-2640-fe0f", "sleuth_or_spy": "1f575-fe0f", "detective": "1f575-fe0f", "male-detective": "1f575-fe0f-200d-2642-fe0f", "male_detective": "1f575-fe0f-200d-2642-fe0f", "female-detective": "1f575-fe0f-200d-2640-fe0f", "female_detective": "1f575-fe0f-200d-2640-fe0f", "guardsman": "1f482", "male-guard": "1f482-200d-2642-fe0f", "female-guard": "1f482-200d-2640-fe0f", "guardswoman": "1f482-200d-2640-fe0f", "ninja": "1f977", "construction_worker": "1f477", "male-construction-worker": "1f477-200d-2642-fe0f", "construction_worker_man": "1f477-200d-2642-fe0f", "female-construction-worker": "1f477-200d-2640-fe0f", "construction_worker_woman": "1f477-200d-2640-fe0f", "prince": "1f934", "princess": "1f478", "man_with_turban": "1f473", "man-wearing-turban": "1f473-200d-2642-fe0f", "woman-wearing-turban": "1f473-200d-2640-fe0f", "woman_with_turban": "1f473-200d-2640-fe0f", "man_with_gua_pi_mao": "1f472", "person_with_headscarf": "1f9d5", "person_in_tuxedo": "1f935", "man_in_tuxedo": "1f935-200d-2642-fe0f", "woman_in_tuxedo": "1f935-200d-2640-fe0f", "bride_with_veil": "1f470", "man_with_veil": "1f470-200d-2642-fe0f", "woman_with_veil": "1f470-200d-2640-fe0f", "pregnant_woman": "1f930", "breast-feeding": "1f931", "woman_feeding_baby": "1f469-200d-1f37c", "man_feeding_baby": "1f468-200d-1f37c", "person_feeding_baby": "1f9d1-200d-1f37c", "angel": "1f47c", "santa": "1f385", "mrs_claus": "1f936", "mother_christmas": "1f936", "mx_claus": "1f9d1-200d-1f384", "superhero": "1f9b8", "male_superhero": "1f9b8-200d-2642-fe0f", "female_superhero": "1f9b8-200d-2640-fe0f", "supervillain": "1f9b9", "male_supervillain": "1f9b9-200d-2642-fe0f", "female_supervillain": "1f9b9-200d-2640-fe0f", "mage": "1f9d9", "male_mage": "1f9d9-200d-2642-fe0f", "female_mage": "1f9d9-200d-2640-fe0f", "fairy": "1f9da", "male_fairy": "1f9da-200d-2642-fe0f", "female_fairy": "1f9da-200d-2640-fe0f", "vampire": "1f9db", "male_vampire": "1f9db-200d-2642-fe0f", "female_vampire": "1f9db-200d-2640-fe0f", "merperson": "1f9dc", "merman": "1f9dc-200d-2642-fe0f", "mermaid": "1f9dc-200d-2640-fe0f", "elf": "1f9dd", "male_elf": "1f9dd-200d-2642-fe0f", "female_elf": "1f9dd-200d-2640-fe0f", "genie": "1f9de", "male_genie": "1f9de-200d-2642-fe0f", "female_genie": "1f9de-200d-2640-fe0f", "zombie": "1f9df", "male_zombie": "1f9df-200d-2642-fe0f", "female_zombie": "1f9df-200d-2640-fe0f", "massage": "1f486", "man-getting-massage": "1f486-200d-2642-fe0f", "massage_man": "1f486-200d-2642-fe0f", "woman-getting-massage": "1f486-200d-2640-fe0f", "massage_woman": "1f486-200d-2640-fe0f", "haircut": "1f487", "man-getting-haircut": "1f487-200d-2642-fe0f", "haircut_man": "1f487-200d-2642-fe0f", "woman-getting-haircut": "1f487-200d-2640-fe0f", "haircut_woman": "1f487-200d-2640-fe0f", "walking": "1f6b6", "man-walking": "1f6b6-200d-2642-fe0f", "walking_man": "1f6b6-200d-2642-fe0f", "woman-walking": "1f6b6-200d-2640-fe0f", "walking_woman": "1f6b6-200d-2640-fe0f", "standing_person": "1f9cd", "man_standing": "1f9cd-200d-2642-fe0f", "woman_standing": "1f9cd-200d-2640-fe0f", "kneeling_person": "1f9ce", "man_kneeling": "1f9ce-200d-2642-fe0f", "woman_kneeling": "1f9ce-200d-2640-fe0f", "person_with_probing_cane": "1f9d1-200d-1f9af", "man_with_probing_cane": "1f468-200d-1f9af", "woman_with_probing_cane": "1f469-200d-1f9af", "person_in_motorized_wheelchair": "1f9d1-200d-1f9bc", "man_in_motorized_wheelchair": "1f468-200d-1f9bc", "woman_in_motorized_wheelchair": "1f469-200d-1f9bc", "person_in_manual_wheelchair": "1f9d1-200d-1f9bd", "man_in_manual_wheelchair": "1f468-200d-1f9bd", "woman_in_manual_wheelchair": "1f469-200d-1f9bd", "runner": "1f3c3", "running": "1f3c3", "man-running": "1f3c3-200d-2642-fe0f", "running_man": "1f3c3-200d-2642-fe0f", "woman-running": "1f3c3-200d-2640-fe0f", "running_woman": "1f3c3-200d-2640-fe0f", "dancer": "1f483", "man_dancing": "1f57a", "man_in_business_suit_levitating": "1f574-fe0f", "business_suit_levitating": "1f574-fe0f", "dancers": "1f46f", "man-with-bunny-ears-partying": "1f46f-200d-2642-fe0f", "dancing_men": "1f46f-200d-2642-fe0f", "woman-with-bunny-ears-partying": "1f46f-200d-2640-fe0f", "dancing_women": "1f46f-200d-2640-fe0f", "person_in_steamy_room": "1f9d6", "man_in_steamy_room": "1f9d6-200d-2642-fe0f", "woman_in_steamy_room": "1f9d6-200d-2640-fe0f", "person_climbing": "1f9d7", "man_climbing": "1f9d7-200d-2642-fe0f", "woman_climbing": "1f9d7-200d-2640-fe0f", "fencer": "1f93a", "person_fencing": "1f93a", "horse_racing": "1f3c7", "skier": "26f7-fe0f", "snowboarder": "1f3c2", "golfer": "1f3cc-fe0f", "man-golfing": "1f3cc-fe0f-200d-2642-fe0f", "golfing_man": "1f3cc-fe0f-200d-2642-fe0f", "woman-golfing": "1f3cc-fe0f-200d-2640-fe0f", "golfing_woman": "1f3cc-fe0f-200d-2640-fe0f", "surfer": "1f3c4", "man-surfing": "1f3c4-200d-2642-fe0f", "surfing_man": "1f3c4-200d-2642-fe0f", "woman-surfing": "1f3c4-200d-2640-fe0f", "surfing_woman": "1f3c4-200d-2640-fe0f", "rowboat": "1f6a3", "man-rowing-boat": "1f6a3-200d-2642-fe0f", "rowing_man": "1f6a3-200d-2642-fe0f", "woman-rowing-boat": "1f6a3-200d-2640-fe0f", "rowing_woman": "1f6a3-200d-2640-fe0f", "swimmer": "1f3ca", "man-swimming": "1f3ca-200d-2642-fe0f", "swimming_man": "1f3ca-200d-2642-fe0f", "woman-swimming": "1f3ca-200d-2640-fe0f", "swimming_woman": "1f3ca-200d-2640-fe0f", "person_with_ball": "26f9-fe0f", "man-bouncing-ball": "26f9-fe0f-200d-2642-fe0f", "basketball_man": "26f9-fe0f-200d-2642-fe0f", "woman-bouncing-ball": "26f9-fe0f-200d-2640-fe0f", "basketball_woman": "26f9-fe0f-200d-2640-fe0f", "weight_lifter": "1f3cb-fe0f", "man-lifting-weights": "1f3cb-fe0f-200d-2642-fe0f", "weight_lifting_man": "1f3cb-fe0f-200d-2642-fe0f", "woman-lifting-weights": "1f3cb-fe0f-200d-2640-fe0f", "weight_lifting_woman": "1f3cb-fe0f-200d-2640-fe0f", "bicyclist": "1f6b4", "man-biking": "1f6b4-200d-2642-fe0f", "biking_man": "1f6b4-200d-2642-fe0f", "woman-biking": "1f6b4-200d-2640-fe0f", "biking_woman": "1f6b4-200d-2640-fe0f", "mountain_bicyclist": "1f6b5", "man-mountain-biking": "1f6b5-200d-2642-fe0f", "mountain_biking_man": "1f6b5-200d-2642-fe0f", "woman-mountain-biking": "1f6b5-200d-2640-fe0f", "mountain_biking_woman": "1f6b5-200d-2640-fe0f", "person_doing_cartwheel": "1f938", "man-cartwheeling": "1f938-200d-2642-fe0f", "man_cartwheeling": "1f938-200d-2642-fe0f", "woman-cartwheeling": "1f938-200d-2640-fe0f", "woman_cartwheeling": "1f938-200d-2640-fe0f", "wrestlers": "1f93c", "man-wrestling": "1f93c-200d-2642-fe0f", "men_wrestling": "1f93c-200d-2642-fe0f", "woman-wrestling": "1f93c-200d-2640-fe0f", "women_wrestling": "1f93c-200d-2640-fe0f", "water_polo": "1f93d", "man-playing-water-polo": "1f93d-200d-2642-fe0f", "man_playing_water_polo": "1f93d-200d-2642-fe0f", "woman-playing-water-polo": "1f93d-200d-2640-fe0f", "woman_playing_water_polo": "1f93d-200d-2640-fe0f", "handball": "1f93e", "man-playing-handball": "1f93e-200d-2642-fe0f", "man_playing_handball": "1f93e-200d-2642-fe0f", "woman-playing-handball": "1f93e-200d-2640-fe0f", "woman_playing_handball": "1f93e-200d-2640-fe0f", "juggling": "1f939", "man-juggling": "1f939-200d-2642-fe0f", "man_juggling": "1f939-200d-2642-fe0f", "woman-juggling": "1f939-200d-2640-fe0f", "woman_juggling": "1f939-200d-2640-fe0f", "person_in_lotus_position": "1f9d8", "man_in_lotus_position": "1f9d8-200d-2642-fe0f", "woman_in_lotus_position": "1f9d8-200d-2640-fe0f", "bath": "1f6c0", "sleeping_accommodation": "1f6cc", "sleeping_bed": "1f6cc", "people_holding_hands": "1f9d1-200d-1f91d-200d-1f9d1", "two_women_holding_hands": "1f46d", "women_holding_hands": "1f46d", "man_and_woman_holding_hands": "1f46b", "woman_and_man_holding_hands": "1f46b", "couple": "1f46b", "two_men_holding_hands": "1f46c", "men_holding_hands": "1f46c", "couplekiss": "1f48f", "woman-kiss-man": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f468", "couplekiss_man_woman": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f468", "man-kiss-man": "1f468-200d-2764-fe0f-200d-1f48b-200d-1f468", "couplekiss_man_man": "1f468-200d-2764-fe0f-200d-1f48b-200d-1f468", "woman-kiss-woman": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f469", "couplekiss_woman_woman": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f469", "couple_with_heart": "1f491", "woman-heart-man": "1f469-200d-2764-fe0f-200d-1f468", "couple_with_heart_woman_man": "1f469-200d-2764-fe0f-200d-1f468", "man-heart-man": "1f468-200d-2764-fe0f-200d-1f468", "couple_with_heart_man_man": "1f468-200d-2764-fe0f-200d-1f468", "woman-heart-woman": "1f469-200d-2764-fe0f-200d-1f469", "couple_with_heart_woman_woman": "1f469-200d-2764-fe0f-200d-1f469", "family": "1f46a", "man-woman-boy": "1f468-200d-1f469-200d-1f466", "family_man_woman_boy": "1f468-200d-1f469-200d-1f466", "man-woman-girl": "1f468-200d-1f469-200d-1f467", "family_man_woman_girl": "1f468-200d-1f469-200d-1f467", "man-woman-girl-boy": "1f468-200d-1f469-200d-1f467-200d-1f466", "family_man_woman_girl_boy": "1f468-200d-1f469-200d-1f467-200d-1f466", "man-woman-boy-boy": "1f468-200d-1f469-200d-1f466-200d-1f466", "family_man_woman_boy_boy": "1f468-200d-1f469-200d-1f466-200d-1f466", "man-woman-girl-girl": "1f468-200d-1f469-200d-1f467-200d-1f467", "family_man_woman_girl_girl": "1f468-200d-1f469-200d-1f467-200d-1f467", "man-man-boy": "1f468-200d-1f468-200d-1f466", "family_man_man_boy": "1f468-200d-1f468-200d-1f466", "man-man-girl": "1f468-200d-1f468-200d-1f467", "family_man_man_girl": "1f468-200d-1f468-200d-1f467", "man-man-girl-boy": "1f468-200d-1f468-200d-1f467-200d-1f466", "family_man_man_girl_boy": "1f468-200d-1f468-200d-1f467-200d-1f466", "man-man-boy-boy": "1f468-200d-1f468-200d-1f466-200d-1f466", "family_man_man_boy_boy": "1f468-200d-1f468-200d-1f466-200d-1f466", "man-man-girl-girl": "1f468-200d-1f468-200d-1f467-200d-1f467", "family_man_man_girl_girl": "1f468-200d-1f468-200d-1f467-200d-1f467", "woman-woman-boy": "1f469-200d-1f469-200d-1f466", "family_woman_woman_boy": "1f469-200d-1f469-200d-1f466", "woman-woman-girl": "1f469-200d-1f469-200d-1f467", "family_woman_woman_girl": "1f469-200d-1f469-200d-1f467", "woman-woman-girl-boy": "1f469-200d-1f469-200d-1f467-200d-1f466", "family_woman_woman_girl_boy": "1f469-200d-1f469-200d-1f467-200d-1f466", "woman-woman-boy-boy": "1f469-200d-1f469-200d-1f466-200d-1f466", "family_woman_woman_boy_boy": "1f469-200d-1f469-200d-1f466-200d-1f466", "woman-woman-girl-girl": "1f469-200d-1f469-200d-1f467-200d-1f467", "family_woman_woman_girl_girl": "1f469-200d-1f469-200d-1f467-200d-1f467", "man-boy": "1f468-200d-1f466", "family_man_boy": "1f468-200d-1f466", "man-boy-boy": "1f468-200d-1f466-200d-1f466", "family_man_boy_boy": "1f468-200d-1f466-200d-1f466", "man-girl": "1f468-200d-1f467", "family_man_girl": "1f468-200d-1f467", "man-girl-boy": "1f468-200d-1f467-200d-1f466", "family_man_girl_boy": "1f468-200d-1f467-200d-1f466", "man-girl-girl": "1f468-200d-1f467-200d-1f467", "family_man_girl_girl": "1f468-200d-1f467-200d-1f467", "woman-boy": "1f469-200d-1f466", "family_woman_boy": "1f469-200d-1f466", "woman-boy-boy": "1f469-200d-1f466-200d-1f466", "family_woman_boy_boy": "1f469-200d-1f466-200d-1f466", "woman-girl": "1f469-200d-1f467", "family_woman_girl": "1f469-200d-1f467", "woman-girl-boy": "1f469-200d-1f467-200d-1f466", "family_woman_girl_boy": "1f469-200d-1f467-200d-1f466", "woman-girl-girl": "1f469-200d-1f467-200d-1f467", "family_woman_girl_girl": "1f469-200d-1f467-200d-1f467", "speaking_head_in_silhouette": "1f5e3-fe0f", "speaking_head": "1f5e3-fe0f", "bust_in_silhouette": "1f464", "busts_in_silhouette": "1f465", "people_hugging": "1fac2", "footprints": "1f463", "skin-tone-2": "1f3fb", "skin-tone-3": "1f3fc", "skin-tone-4": "1f3fd", "skin-tone-5": "1f3fe", "skin-tone-6": "1f3ff", "monkey_face": "1f435", "monkey": "1f412", "gorilla": "1f98d", "orangutan": "1f9a7", "dog": "1f436", "dog2": "1f415", "guide_dog": "1f9ae", "service_dog": "1f415-200d-1f9ba", "poodle": "1f429", "wolf": "1f43a", "fox_face": "1f98a", "raccoon": "1f99d", "cat": "1f431", "cat2": "1f408", "black_cat": "1f408-200d-2b1b", "lion_face": "1f981", "lion": "1f981", "tiger": "1f42f", "tiger2": "1f405", "leopard": "1f406", "horse": "1f434", "racehorse": "1f40e", "unicorn_face": "1f984", "unicorn": "1f984", "zebra_face": "1f993", "deer": "1f98c", "bison": "1f9ac", "cow": "1f42e", "ox": "1f402", "water_buffalo": "1f403", "cow2": "1f404", "pig": "1f437", "pig2": "1f416", "boar": "1f417", "pig_nose": "1f43d", "ram": "1f40f", "sheep": "1f411", "goat": "1f410", "dromedary_camel": "1f42a", "camel": "1f42b", "llama": "1f999", "giraffe_face": "1f992", "elephant": "1f418", "mammoth": "1f9a3", "rhinoceros": "1f98f", "hippopotamus": "1f99b", "mouse": "1f42d", "mouse2": "1f401", "rat": "1f400", "hamster": "1f439", "rabbit": "1f430", "rabbit2": "1f407", "chipmunk": "1f43f-fe0f", "beaver": "1f9ab", "hedgehog": "1f994", "bat": "1f987", "bear": "1f43b", "polar_bear": "1f43b-200d-2744-fe0f", "koala": "1f428", "panda_face": "1f43c", "sloth": "1f9a5", "otter": "1f9a6", "skunk": "1f9a8", "kangaroo": "1f998", "badger": "1f9a1", "feet": "1f43e", "paw_prints": "1f43e", "turkey": "1f983", "chicken": "1f414", "rooster": "1f413", "hatching_chick": "1f423", "baby_chick": "1f424", "hatched_chick": "1f425", "bird": "1f426", "penguin": "1f427", "dove_of_peace": "1f54a-fe0f", "dove": "1f54a-fe0f", "eagle": "1f985", "duck": "1f986", "swan": "1f9a2", "owl": "1f989", "dodo": "1f9a4", "feather": "1fab6", "flamingo": "1f9a9", "peacock": "1f99a", "parrot": "1f99c", "frog": "1f438", "crocodile": "1f40a", "turtle": "1f422", "lizard": "1f98e", "snake": "1f40d", "dragon_face": "1f432", "dragon": "1f409", "sauropod": "1f995", "t-rex": "1f996", "whale": "1f433", "whale2": "1f40b", "dolphin": "1f42c", "flipper": "1f42c", "seal": "1f9ad", "fish": "1f41f", "tropical_fish": "1f420", "blowfish": "1f421", "shark": "1f988", "octopus": "1f419", "shell": "1f41a", "snail": "1f40c", "butterfly": "1f98b", "bug": "1f41b", "ant": "1f41c", "bee": "1f41d", "honeybee": "1f41d", "beetle": "1fab2", "ladybug": "1f41e", "lady_beetle": "1f41e", "cricket": "1f997", "cockroach": "1fab3", "spider": "1f577-fe0f", "spider_web": "1f578-fe0f", "scorpion": "1f982", "mosquito": "1f99f", "fly": "1fab0", "worm": "1fab1", "microbe": "1f9a0", "bouquet": "1f490", "cherry_blossom": "1f338", "white_flower": "1f4ae", "rosette": "1f3f5-fe0f", "rose": "1f339", "wilted_flower": "1f940", "hibiscus": "1f33a", "sunflower": "1f33b", "blossom": "1f33c", "tulip": "1f337", "seedling": "1f331", "potted_plant": "1fab4", "evergreen_tree": "1f332", "deciduous_tree": "1f333", "palm_tree": "1f334", "cactus": "1f335", "ear_of_rice": "1f33e", "herb": "1f33f", "shamrock": "2618-fe0f", "four_leaf_clover": "1f340", "maple_leaf": "1f341", "fallen_leaf": "1f342", "leaves": "1f343", "grapes": "1f347", "melon": "1f348", "watermelon": "1f349", "tangerine": "1f34a", "mandarin": "1f34a", "orange": "1f34a", "lemon": "1f34b", "banana": "1f34c", "pineapple": "1f34d", "mango": "1f96d", "apple": "1f34e", "green_apple": "1f34f", "pear": "1f350", "peach": "1f351", "cherries": "1f352", "strawberry": "1f353", "blueberries": "1fad0", "kiwifruit": "1f95d", "kiwi_fruit": "1f95d", "tomato": "1f345", "olive": "1fad2", "coconut": "1f965", "avocado": "1f951", "eggplant": "1f346", "potato": "1f954", "carrot": "1f955", "corn": "1f33d", "hot_pepper": "1f336-fe0f", "bell_pepper": "1fad1", "cucumber": "1f952", "leafy_green": "1f96c", "broccoli": "1f966", "garlic": "1f9c4", "onion": "1f9c5", "mushroom": "1f344", "peanuts": "1f95c", "chestnut": "1f330", "bread": "1f35e", "croissant": "1f950", "baguette_bread": "1f956", "flatbread": "1fad3", "pretzel": "1f968", "bagel": "1f96f", "pancakes": "1f95e", "waffle": "1f9c7", "cheese_wedge": "1f9c0", "cheese": "1f9c0", "meat_on_bone": "1f356", "poultry_leg": "1f357", "cut_of_meat": "1f969", "bacon": "1f953", "hamburger": "1f354", "fries": "1f35f", "pizza": "1f355", "hotdog": "1f32d", "sandwich": "1f96a", "taco": "1f32e", "burrito": "1f32f", "tamale": "1fad4", "stuffed_flatbread": "1f959", "falafel": "1f9c6", "egg": "1f95a", "fried_egg": "1f373", "cooking": "1f373", "shallow_pan_of_food": "1f958", "stew": "1f372", "fondue": "1fad5", "bowl_with_spoon": "1f963", "green_salad": "1f957", "popcorn": "1f37f", "butter": "1f9c8", "salt": "1f9c2", "canned_food": "1f96b", "bento": "1f371", "rice_cracker": "1f358", "rice_ball": "1f359", "rice": "1f35a", "curry": "1f35b", "ramen": "1f35c", "spaghetti": "1f35d", "sweet_potato": "1f360", "oden": "1f362", "sushi": "1f363", "fried_shrimp": "1f364", "fish_cake": "1f365", "moon_cake": "1f96e", "dango": "1f361", "dumpling": "1f95f", "fortune_cookie": "1f960", "takeout_box": "1f961", "crab": "1f980", "lobster": "1f99e", "shrimp": "1f990", "squid": "1f991", "oyster": "1f9aa", "icecream": "1f366", "shaved_ice": "1f367", "ice_cream": "1f368", "doughnut": "1f369", "cookie": "1f36a", "birthday": "1f382", "cake": "1f370", "cupcake": "1f9c1", "pie": "1f967", "chocolate_bar": "1f36b", "candy": "1f36c", "lollipop": "1f36d", "custard": "1f36e", "honey_pot": "1f36f", "baby_bottle": "1f37c", "glass_of_milk": "1f95b", "milk_glass": "1f95b", "coffee": "2615", "teapot": "1fad6", "tea": "1f375", "sake": "1f376", "champagne": "1f37e", "wine_glass": "1f377", "cocktail": "1f378", "tropical_drink": "1f379", "beer": "1f37a", "beers": "1f37b", "clinking_glasses": "1f942", "tumbler_glass": "1f943", "cup_with_straw": "1f964", "bubble_tea": "1f9cb", "beverage_box": "1f9c3", "mate_drink": "1f9c9", "ice_cube": "1f9ca", "chopsticks": "1f962", "knife_fork_plate": "1f37d-fe0f", "plate_with_cutlery": "1f37d-fe0f", "fork_and_knife": "1f374", "spoon": "1f944", "hocho": "1f52a", "knife": "1f52a", "amphora": "1f3fa", "earth_africa": "1f30d", "earth_americas": "1f30e", "earth_asia": "1f30f", "globe_with_meridians": "1f310", "world_map": "1f5fa-fe0f", "japan": "1f5fe", "compass": "1f9ed", "snow_capped_mountain": "1f3d4-fe0f", "mountain_snow": "1f3d4-fe0f", "mountain": "26f0-fe0f", "volcano": "1f30b", "mount_fuji": "1f5fb", "camping": "1f3d5-fe0f", "beach_with_umbrella": "1f3d6-fe0f", "beach_umbrella": "1f3d6-fe0f", "desert": "1f3dc-fe0f", "desert_island": "1f3dd-fe0f", "national_park": "1f3de-fe0f", "stadium": "1f3df-fe0f", "classical_building": "1f3db-fe0f", "building_construction": "1f3d7-fe0f", "bricks": "1f9f1", "rock": "1faa8", "wood": "1fab5", "hut": "1f6d6", "house_buildings": "1f3d8-fe0f", "houses": "1f3d8-fe0f", "derelict_house_building": "1f3da-fe0f", "derelict_house": "1f3da-fe0f", "house": "1f3e0", "house_with_garden": "1f3e1", "office": "1f3e2", "post_office": "1f3e3", "european_post_office": "1f3e4", "hospital": "1f3e5", "bank": "1f3e6", "hotel": "1f3e8", "love_hotel": "1f3e9", "convenience_store": "1f3ea", "school": "1f3eb", "department_store": "1f3ec", "factory": "1f3ed", "japanese_castle": "1f3ef", "european_castle": "1f3f0", "wedding": "1f492", "tokyo_tower": "1f5fc", "statue_of_liberty": "1f5fd", "church": "26ea", "mosque": "1f54c", "hindu_temple": "1f6d5", "synagogue": "1f54d", "shinto_shrine": "26e9-fe0f", "kaaba": "1f54b", "fountain": "26f2", "tent": "26fa", "foggy": "1f301", "night_with_stars": "1f303", "cityscape": "1f3d9-fe0f", "sunrise_over_mountains": "1f304", "sunrise": "1f305", "city_sunset": "1f306", "city_sunrise": "1f307", "bridge_at_night": "1f309", "hotsprings": "2668-fe0f", "carousel_horse": "1f3a0", "ferris_wheel": "1f3a1", "roller_coaster": "1f3a2", "barber": "1f488", "circus_tent": "1f3aa", "steam_locomotive": "1f682", "railway_car": "1f683", "bullettrain_side": "1f684", "bullettrain_front": "1f685", "train2": "1f686", "metro": "1f687", "light_rail": "1f688", "station": "1f689", "tram": "1f68a", "monorail": "1f69d", "mountain_railway": "1f69e", "train": "1f68b", "bus": "1f68c", "oncoming_bus": "1f68d", "trolleybus": "1f68e", "minibus": "1f690", "ambulance": "1f691", "fire_engine": "1f692", "police_car": "1f693", "oncoming_police_car": "1f694", "taxi": "1f695", "oncoming_taxi": "1f696", "car": "1f697", "red_car": "1f697", "oncoming_automobile": "1f698", "blue_car": "1f699", "pickup_truck": "1f6fb", "truck": "1f69a", "articulated_lorry": "1f69b", "tractor": "1f69c", "racing_car": "1f3ce-fe0f", "racing_motorcycle": "1f3cd-fe0f", "motorcycle": "1f3cd-fe0f", "motor_scooter": "1f6f5", "manual_wheelchair": "1f9bd", "motorized_wheelchair": "1f9bc", "auto_rickshaw": "1f6fa", "bike": "1f6b2", "scooter": "1f6f4", "kick_scooter": "1f6f4", "skateboard": "1f6f9", "roller_skate": "1f6fc", "busstop": "1f68f", "motorway": "1f6e3-fe0f", "railway_track": "1f6e4-fe0f", "oil_drum": "1f6e2-fe0f", "fuelpump": "26fd", "rotating_light": "1f6a8", "traffic_light": "1f6a5", "vertical_traffic_light": "1f6a6", "octagonal_sign": "1f6d1", "stop_sign": "1f6d1", "construction": "1f6a7", "anchor": "2693", "boat": "26f5", "sailboat": "26f5", "canoe": "1f6f6", "speedboat": "1f6a4", "passenger_ship": "1f6f3-fe0f", "ferry": "26f4-fe0f", "motor_boat": "1f6e5-fe0f", "ship": "1f6a2", "airplane": "2708-fe0f", "small_airplane": "1f6e9-fe0f", "airplane_departure": "1f6eb", "flight_departure": "1f6eb", "airplane_arriving": "1f6ec", "flight_arrival": "1f6ec", "parachute": "1fa82", "seat": "1f4ba", "helicopter": "1f681", "suspension_railway": "1f69f", "mountain_cableway": "1f6a0", "aerial_tramway": "1f6a1", "satellite": "1f6f0-fe0f", "artificial_satellite": "1f6f0-fe0f", "rocket": "1f680", "flying_saucer": "1f6f8", "bellhop_bell": "1f6ce-fe0f", "luggage": "1f9f3", "hourglass": "231b", "hourglass_flowing_sand": "23f3", "watch": "231a", "alarm_clock": "23f0", "stopwatch": "23f1-fe0f", "timer_clock": "23f2-fe0f", "mantelpiece_clock": "1f570-fe0f", "clock12": "1f55b", "clock1230": "1f567", "clock1": "1f550", "clock130": "1f55c", "clock2": "1f551", "clock230": "1f55d", "clock3": "1f552", "clock330": "1f55e", "clock4": "1f553", "clock430": "1f55f", "clock5": "1f554", "clock530": "1f560", "clock6": "1f555", "clock630": "1f561", "clock7": "1f556", "clock730": "1f562", "clock8": "1f557", "clock830": "1f563", "clock9": "1f558", "clock930": "1f564", "clock10": "1f559", "clock1030": "1f565", "clock11": "1f55a", "clock1130": "1f566", "new_moon": "1f311", "waxing_crescent_moon": "1f312", "first_quarter_moon": "1f313", "moon": "1f314", "waxing_gibbous_moon": "1f314", "full_moon": "1f315", "waning_gibbous_moon": "1f316", "last_quarter_moon": "1f317", "waning_crescent_moon": "1f318", "crescent_moon": "1f319", "new_moon_with_face": "1f31a", "first_quarter_moon_with_face": "1f31b", "last_quarter_moon_with_face": "1f31c", "thermometer": "1f321-fe0f", "sunny": "2600-fe0f", "full_moon_with_face": "1f31d", "sun_with_face": "1f31e", "ringed_planet": "1fa90", "star": "2b50", "star2": "1f31f", "stars": "1f320", "milky_way": "1f30c", "cloud": "2601-fe0f", "partly_sunny": "26c5", "thunder_cloud_and_rain": "26c8-fe0f", "cloud_with_lightning_and_rain": "26c8-fe0f", "mostly_sunny": "1f324-fe0f", "sun_small_cloud": "1f324-fe0f", "sun_behind_small_cloud": "1f324-fe0f", "barely_sunny": "1f325-fe0f", "sun_behind_cloud": "1f325-fe0f", "sun_behind_large_cloud": "1f325-fe0f", "partly_sunny_rain": "1f326-fe0f", "sun_behind_rain_cloud": "1f326-fe0f", "rain_cloud": "1f327-fe0f", "cloud_with_rain": "1f327-fe0f", "snow_cloud": "1f328-fe0f", "cloud_with_snow": "1f328-fe0f", "lightning": "1f329-fe0f", "lightning_cloud": "1f329-fe0f", "cloud_with_lightning": "1f329-fe0f", "tornado": "1f32a-fe0f", "tornado_cloud": "1f32a-fe0f", "fog": "1f32b-fe0f", "wind_blowing_face": "1f32c-fe0f", "wind_face": "1f32c-fe0f", "cyclone": "1f300", "rainbow": "1f308", "closed_umbrella": "1f302", "umbrella": "2602-fe0f", "open_umbrella": "2602-fe0f", "umbrella_with_rain_drops": "2614", "umbrella_on_ground": "26f1-fe0f", "parasol_on_ground": "26f1-fe0f", "zap": "26a1", "snowflake": "2744-fe0f", "snowman": "2603-fe0f", "snowman_with_snow": "2603-fe0f", "snowman_without_snow": "26c4", "comet": "2604-fe0f", "fire": "1f525", "droplet": "1f4a7", "ocean": "1f30a", "jack_o_lantern": "1f383", "christmas_tree": "1f384", "fireworks": "1f386", "sparkler": "1f387", "firecracker": "1f9e8", "sparkles": "2728", "balloon": "1f388", "tada": "1f389", "confetti_ball": "1f38a", "tanabata_tree": "1f38b", "bamboo": "1f38d", "dolls": "1f38e", "flags": "1f38f", "wind_chime": "1f390", "rice_scene": "1f391", "red_envelope": "1f9e7", "ribbon": "1f380", "gift": "1f381", "reminder_ribbon": "1f397-fe0f", "admission_tickets": "1f39f-fe0f", "tickets": "1f39f-fe0f", "ticket": "1f3ab", "medal": "1f396-fe0f", "medal_military": "1f396-fe0f", "trophy": "1f3c6", "sports_medal": "1f3c5", "medal_sports": "1f3c5", "first_place_medal": "1f947", "1st_place_medal": "1f947", "second_place_medal": "1f948", "2nd_place_medal": "1f948", "third_place_medal": "1f949", "3rd_place_medal": "1f949", "soccer": "26bd", "baseball": "26be", "softball": "1f94e", "basketball": "1f3c0", "volleyball": "1f3d0", "football": "1f3c8", "rugby_football": "1f3c9", "tennis": "1f3be", "flying_disc": "1f94f", "bowling": "1f3b3", "cricket_bat_and_ball": "1f3cf", "field_hockey_stick_and_ball": "1f3d1", "field_hockey": "1f3d1", "ice_hockey_stick_and_puck": "1f3d2", "ice_hockey": "1f3d2", "lacrosse": "1f94d", "table_tennis_paddle_and_ball": "1f3d3", "ping_pong": "1f3d3", "badminton_racquet_and_shuttlecock": "1f3f8", "badminton": "1f3f8", "boxing_glove": "1f94a", "martial_arts_uniform": "1f94b", "goal_net": "1f945", "golf": "26f3", "ice_skate": "26f8-fe0f", "fishing_pole_and_fish": "1f3a3", "diving_mask": "1f93f", "running_shirt_with_sash": "1f3bd", "ski": "1f3bf", "sled": "1f6f7", "curling_stone": "1f94c", "dart": "1f3af", "yo-yo": "1fa80", "kite": "1fa81", "8ball": "1f3b1", "crystal_ball": "1f52e", "magic_wand": "1fa84", "nazar_amulet": "1f9ff", "video_game": "1f3ae", "joystick": "1f579-fe0f", "slot_machine": "1f3b0", "game_die": "1f3b2", "jigsaw": "1f9e9", "teddy_bear": "1f9f8", "pinata": "1fa85", "nesting_dolls": "1fa86", "spades": "2660-fe0f", "hearts": "2665-fe0f", "diamonds": "2666-fe0f", "clubs": "2663-fe0f", "chess_pawn": "265f-fe0f", "black_joker": "1f0cf", "mahjong": "1f004", "flower_playing_cards": "1f3b4", "performing_arts": "1f3ad", "frame_with_picture": "1f5bc-fe0f", "framed_picture": "1f5bc-fe0f", "art": "1f3a8", "thread": "1f9f5", "sewing_needle": "1faa1", "yarn": "1f9f6", "knot": "1faa2", "eyeglasses": "1f453", "dark_sunglasses": "1f576-fe0f", "goggles": "1f97d", "lab_coat": "1f97c", "safety_vest": "1f9ba", "necktie": "1f454", "shirt": "1f455", "tshirt": "1f455", "jeans": "1f456", "scarf": "1f9e3", "gloves": "1f9e4", "coat": "1f9e5", "socks": "1f9e6", "dress": "1f457", "kimono": "1f458", "sari": "1f97b", "one-piece_swimsuit": "1fa71", "briefs": "1fa72", "shorts": "1fa73", "bikini": "1f459", "womans_clothes": "1f45a", "purse": "1f45b", "handbag": "1f45c", "pouch": "1f45d", "shopping_bags": "1f6cd-fe0f", "shopping": "1f6cd-fe0f", "school_satchel": "1f392", "thong_sandal": "1fa74", "mans_shoe": "1f45e", "shoe": "1f45e", "athletic_shoe": "1f45f", "hiking_boot": "1f97e", "womans_flat_shoe": "1f97f", "high_heel": "1f460", "sandal": "1f461", "ballet_shoes": "1fa70", "boot": "1f462", "crown": "1f451", "womans_hat": "1f452", "tophat": "1f3a9", "mortar_board": "1f393", "billed_cap": "1f9e2", "military_helmet": "1fa96", "helmet_with_white_cross": "26d1-fe0f", "rescue_worker_helmet": "26d1-fe0f", "prayer_beads": "1f4ff", "lipstick": "1f484", "ring": "1f48d", "gem": "1f48e", "mute": "1f507", "speaker": "1f508", "sound": "1f509", "loud_sound": "1f50a", "loudspeaker": "1f4e2", "mega": "1f4e3", "postal_horn": "1f4ef", "bell": "1f514", "no_bell": "1f515", "musical_score": "1f3bc", "musical_note": "1f3b5", "notes": "1f3b6", "studio_microphone": "1f399-fe0f", "level_slider": "1f39a-fe0f", "control_knobs": "1f39b-fe0f", "microphone": "1f3a4", "headphones": "1f3a7", "radio": "1f4fb", "saxophone": "1f3b7", "accordion": "1fa97", "guitar": "1f3b8", "musical_keyboard": "1f3b9", "trumpet": "1f3ba", "violin": "1f3bb", "banjo": "1fa95", "drum_with_drumsticks": "1f941", "drum": "1f941", "long_drum": "1fa98", "iphone": "1f4f1", "calling": "1f4f2", "phone": "260e-fe0f", "telephone": "260e-fe0f", "telephone_receiver": "1f4de", "pager": "1f4df", "fax": "1f4e0", "battery": "1f50b", "electric_plug": "1f50c", "computer": "1f4bb", "desktop_computer": "1f5a5-fe0f", "printer": "1f5a8-fe0f", "keyboard": "2328-fe0f", "three_button_mouse": "1f5b1-fe0f", "computer_mouse": "1f5b1-fe0f", "trackball": "1f5b2-fe0f", "minidisc": "1f4bd", "floppy_disk": "1f4be", "cd": "1f4bf", "dvd": "1f4c0", "abacus": "1f9ee", "movie_camera": "1f3a5", "film_frames": "1f39e-fe0f", "film_strip": "1f39e-fe0f", "film_projector": "1f4fd-fe0f", "clapper": "1f3ac", "tv": "1f4fa", "camera": "1f4f7", "camera_with_flash": "1f4f8", "camera_flash": "1f4f8", "video_camera": "1f4f9", "vhs": "1f4fc", "mag": "1f50d", "mag_right": "1f50e", "candle": "1f56f-fe0f", "bulb": "1f4a1", "flashlight": "1f526", "izakaya_lantern": "1f3ee", "lantern": "1f3ee", "diya_lamp": "1fa94", "notebook_with_decorative_cover": "1f4d4", "closed_book": "1f4d5", "book": "1f4d6", "open_book": "1f4d6", "green_book": "1f4d7", "blue_book": "1f4d8", "orange_book": "1f4d9", "books": "1f4da", "notebook": "1f4d3", "ledger": "1f4d2", "page_with_curl": "1f4c3", "scroll": "1f4dc", "page_facing_up": "1f4c4", "newspaper": "1f4f0", "rolled_up_newspaper": "1f5de-fe0f", "newspaper_roll": "1f5de-fe0f", "bookmark_tabs": "1f4d1", "bookmark": "1f516", "label": "1f3f7-fe0f", "moneybag": "1f4b0", "coin": "1fa99", "yen": "1f4b4", "dollar": "1f4b5", "euro": "1f4b6", "pound": "1f4b7", "money_with_wings": "1f4b8", "credit_card": "1f4b3", "receipt": "1f9fe", "chart": "1f4b9", "email": "2709-fe0f", "envelope": "2709-fe0f", "e-mail": "1f4e7", "incoming_envelope": "1f4e8", "envelope_with_arrow": "1f4e9", "outbox_tray": "1f4e4", "inbox_tray": "1f4e5", "package": "1f4e6", "mailbox": "1f4eb", "mailbox_closed": "1f4ea", "mailbox_with_mail": "1f4ec", "mailbox_with_no_mail": "1f4ed", "postbox": "1f4ee", "ballot_box_with_ballot": "1f5f3-fe0f", "ballot_box": "1f5f3-fe0f", "pencil2": "270f-fe0f", "black_nib": "2712-fe0f", "lower_left_fountain_pen": "1f58b-fe0f", "fountain_pen": "1f58b-fe0f", "lower_left_ballpoint_pen": "1f58a-fe0f", "pen": "1f58a-fe0f", "lower_left_paintbrush": "1f58c-fe0f", "paintbrush": "1f58c-fe0f", "lower_left_crayon": "1f58d-fe0f", "crayon": "1f58d-fe0f", "memo": "1f4dd", "pencil": "1f4dd", "briefcase": "1f4bc", "file_folder": "1f4c1", "open_file_folder": "1f4c2", "card_index_dividers": "1f5c2-fe0f", "date": "1f4c5", "calendar": "1f4c6", "spiral_note_pad": "1f5d2-fe0f", "spiral_notepad": "1f5d2-fe0f", "spiral_calendar_pad": "1f5d3-fe0f", "spiral_calendar": "1f5d3-fe0f", "card_index": "1f4c7", "chart_with_upwards_trend": "1f4c8", "chart_with_downwards_trend": "1f4c9", "bar_chart": "1f4ca", "clipboard": "1f4cb", "pushpin": "1f4cc", "round_pushpin": "1f4cd", "paperclip": "1f4ce", "linked_paperclips": "1f587-fe0f", "paperclips": "1f587-fe0f", "straight_ruler": "1f4cf", "triangular_ruler": "1f4d0", "scissors": "2702-fe0f", "card_file_box": "1f5c3-fe0f", "file_cabinet": "1f5c4-fe0f", "wastebasket": "1f5d1-fe0f", "lock": "1f512", "unlock": "1f513", "lock_with_ink_pen": "1f50f", "closed_lock_with_key": "1f510", "key": "1f511", "old_key": "1f5dd-fe0f", "hammer": "1f528", "axe": "1fa93", "pick": "26cf-fe0f", "hammer_and_pick": "2692-fe0f", "hammer_and_wrench": "1f6e0-fe0f", "dagger_knife": "1f5e1-fe0f", "dagger": "1f5e1-fe0f", "crossed_swords": "2694-fe0f", "gun": "1f52b", "boomerang": "1fa83", "bow_and_arrow": "1f3f9", "shield": "1f6e1-fe0f", "carpentry_saw": "1fa9a", "wrench": "1f527", "screwdriver": "1fa9b", "nut_and_bolt": "1f529", "gear": "2699-fe0f", "compression": "1f5dc-fe0f", "clamp": "1f5dc-fe0f", "scales": "2696-fe0f", "balance_scale": "2696-fe0f", "probing_cane": "1f9af", "link": "1f517", "chains": "26d3-fe0f", "hook": "1fa9d", "toolbox": "1f9f0", "magnet": "1f9f2", "ladder": "1fa9c", "alembic": "2697-fe0f", "test_tube": "1f9ea", "petri_dish": "1f9eb", "dna": "1f9ec", "microscope": "1f52c", "telescope": "1f52d", "satellite_antenna": "1f4e1", "syringe": "1f489", "drop_of_blood": "1fa78", "pill": "1f48a", "adhesive_bandage": "1fa79", "stethoscope": "1fa7a", "door": "1f6aa", "elevator": "1f6d7", "mirror": "1fa9e", "window": "1fa9f", "bed": "1f6cf-fe0f", "couch_and_lamp": "1f6cb-fe0f", "chair": "1fa91", "toilet": "1f6bd", "plunger": "1faa0", "shower": "1f6bf", "bathtub": "1f6c1", "mouse_trap": "1faa4", "razor": "1fa92", "lotion_bottle": "1f9f4", "safety_pin": "1f9f7", "broom": "1f9f9", "basket": "1f9fa", "roll_of_paper": "1f9fb", "bucket": "1faa3", "soap": "1f9fc", "toothbrush": "1faa5", "sponge": "1f9fd", "fire_extinguisher": "1f9ef", "shopping_trolley": "1f6d2", "shopping_cart": "1f6d2", "smoking": "1f6ac", "coffin": "26b0-fe0f", "headstone": "1faa6", "funeral_urn": "26b1-fe0f", "moyai": "1f5ff", "placard": "1faa7", "atm": "1f3e7", "put_litter_in_its_place": "1f6ae", "potable_water": "1f6b0", "wheelchair": "267f", "mens": "1f6b9", "womens": "1f6ba", "restroom": "1f6bb", "baby_symbol": "1f6bc", "wc": "1f6be", "passport_control": "1f6c2", "customs": "1f6c3", "baggage_claim": "1f6c4", "left_luggage": "1f6c5", "warning": "26a0-fe0f", "children_crossing": "1f6b8", "no_entry": "26d4", "no_entry_sign": "1f6ab", "no_bicycles": "1f6b3", "no_smoking": "1f6ad", "do_not_litter": "1f6af", "non-potable_water": "1f6b1", "no_pedestrians": "1f6b7", "no_mobile_phones": "1f4f5", "underage": "1f51e", "radioactive_sign": "2622-fe0f", "radioactive": "2622-fe0f", "biohazard_sign": "2623-fe0f", "biohazard": "2623-fe0f", "arrow_up": "2b06-fe0f", "arrow_upper_right": "2197-fe0f", "arrow_right": "27a1-fe0f", "arrow_lower_right": "2198-fe0f", "arrow_down": "2b07-fe0f", "arrow_lower_left": "2199-fe0f", "arrow_left": "2b05-fe0f", "arrow_upper_left": "2196-fe0f", "arrow_up_down": "2195-fe0f", "left_right_arrow": "2194-fe0f", "leftwards_arrow_with_hook": "21a9-fe0f", "arrow_right_hook": "21aa-fe0f", "arrow_heading_up": "2934-fe0f", "arrow_heading_down": "2935-fe0f", "arrows_clockwise": "1f503", "arrows_counterclockwise": "1f504", "back": "1f519", "end": "1f51a", "on": "1f51b", "soon": "1f51c", "top": "1f51d", "place_of_worship": "1f6d0", "atom_symbol": "269b-fe0f", "om_symbol": "1f549-fe0f", "om": "1f549-fe0f", "star_of_david": "2721-fe0f", "wheel_of_dharma": "2638-fe0f", "yin_yang": "262f-fe0f", "latin_cross": "271d-fe0f", "orthodox_cross": "2626-fe0f", "star_and_crescent": "262a-fe0f", "peace_symbol": "262e-fe0f", "menorah_with_nine_branches": "1f54e", "menorah": "1f54e", "six_pointed_star": "1f52f", "aries": "2648", "taurus": "2649", "gemini": "264a", "cancer": "264b", "leo": "264c", "virgo": "264d", "libra": "264e", "scorpius": "264f", "sagittarius": "2650", "capricorn": "2651", "aquarius": "2652", "pisces": "2653", "ophiuchus": "26ce", "twisted_rightwards_arrows": "1f500", "repeat": "1f501", "repeat_one": "1f502", "arrow_forward": "25b6-fe0f", "fast_forward": "23e9", "black_right_pointing_double_triangle_with_vertical_bar": "23ed-fe0f", "next_track_button": "23ed-fe0f", "black_right_pointing_triangle_with_double_vertical_bar": "23ef-fe0f", "play_or_pause_button": "23ef-fe0f", "arrow_backward": "25c0-fe0f", "rewind": "23ea", "black_left_pointing_double_triangle_with_vertical_bar": "23ee-fe0f", "previous_track_button": "23ee-fe0f", "arrow_up_small": "1f53c", "arrow_double_up": "23eb", "arrow_down_small": "1f53d", "arrow_double_down": "23ec", "double_vertical_bar": "23f8-fe0f", "pause_button": "23f8-fe0f", "black_square_for_stop": "23f9-fe0f", "stop_button": "23f9-fe0f", "black_circle_for_record": "23fa-fe0f", "record_button": "23fa-fe0f", "eject": "23cf-fe0f", "cinema": "1f3a6", "low_brightness": "1f505", "high_brightness": "1f506", "signal_strength": "1f4f6", "vibration_mode": "1f4f3", "mobile_phone_off": "1f4f4", "female_sign": "2640-fe0f", "male_sign": "2642-fe0f", "transgender_symbol": "26a7-fe0f", "heavy_multiplication_x": "2716-fe0f", "heavy_plus_sign": "2795", "heavy_minus_sign": "2796", "heavy_division_sign": "2797", "infinity": "267e-fe0f", "bangbang": "203c-fe0f", "interrobang": "2049-fe0f", "question": "2753", "grey_question": "2754", "grey_exclamation": "2755", "exclamation": "2757", "heavy_exclamation_mark": "2757", "wavy_dash": "3030-fe0f", "currency_exchange": "1f4b1", "heavy_dollar_sign": "1f4b2", "medical_symbol": "2695-fe0f", "staff_of_aesculapius": "2695-fe0f", "recycle": "267b-fe0f", "fleur_de_lis": "269c-fe0f", "trident": "1f531", "name_badge": "1f4db", "beginner": "1f530", "o": "2b55", "white_check_mark": "2705", "ballot_box_with_check": "2611-fe0f", "heavy_check_mark": "2714-fe0f", "x": "274c", "negative_squared_cross_mark": "274e", "curly_loop": "27b0", "loop": "27bf", "part_alternation_mark": "303d-fe0f", "eight_spoked_asterisk": "2733-fe0f", "eight_pointed_black_star": "2734-fe0f", "sparkle": "2747-fe0f", "copyright": "00a9-fe0f", "registered": "00ae-fe0f", "tm": "2122-fe0f", "hash": "0023-fe0f-20e3", "keycap_star": "002a-fe0f-20e3", "asterisk": "002a-fe0f-20e3", "zero": "0030-fe0f-20e3", "one": "0031-fe0f-20e3", "two": "0032-fe0f-20e3", "three": "0033-fe0f-20e3", "four": "0034-fe0f-20e3", "five": "0035-fe0f-20e3", "six": "0036-fe0f-20e3", "seven": "0037-fe0f-20e3", "eight": "0038-fe0f-20e3", "nine": "0039-fe0f-20e3", "keycap_ten": "1f51f", "capital_abcd": "1f520", "abcd": "1f521", "1234": "1f522", "symbols": "1f523", "abc": "1f524", "a": "1f170-fe0f", "ab": "1f18e", "b": "1f171-fe0f", "cl": "1f191", "cool": "1f192", "free": "1f193", "information_source": "2139-fe0f", "id": "1f194", "m": "24c2-fe0f", "new": "1f195", "ng": "1f196", "o2": "1f17e-fe0f", "ok": "1f197", "parking": "1f17f-fe0f", "sos": "1f198", "up": "1f199", "vs": "1f19a", "koko": "1f201", "sa": "1f202-fe0f", "u6708": "1f237-fe0f", "u6709": "1f236", "u6307": "1f22f", "ideograph_advantage": "1f250", "u5272": "1f239", "u7121": "1f21a", "u7981": "1f232", "accept": "1f251", "u7533": "1f238", "u5408": "1f234", "u7a7a": "1f233", "congratulations": "3297-fe0f", "secret": "3299-fe0f", "u55b6": "1f23a", "u6e80": "1f235", "red_circle": "1f534", "large_orange_circle": "1f7e0", "large_yellow_circle": "1f7e1", "large_green_circle": "1f7e2", "large_blue_circle": "1f535", "large_purple_circle": "1f7e3", "large_brown_circle": "1f7e4", "black_circle": "26ab", "white_circle": "26aa", "large_red_square": "1f7e5", "large_orange_square": "1f7e7", "large_yellow_square": "1f7e8", "large_green_square": "1f7e9", "large_blue_square": "1f7e6", "large_purple_square": "1f7ea", "large_brown_square": "1f7eb", "black_large_square": "2b1b", "white_large_square": "2b1c", "black_medium_square": "25fc-fe0f", "white_medium_square": "25fb-fe0f", "black_medium_small_square": "25fe", "white_medium_small_square": "25fd", "black_small_square": "25aa-fe0f", "white_small_square": "25ab-fe0f", "large_orange_diamond": "1f536", "large_blue_diamond": "1f537", "small_orange_diamond": "1f538", "small_blue_diamond": "1f539", "small_red_triangle": "1f53a", "small_red_triangle_down": "1f53b", "diamond_shape_with_a_dot_inside": "1f4a0", "radio_button": "1f518", "white_square_button": "1f533", "black_square_button": "1f532", "checkered_flag": "1f3c1", "triangular_flag_on_post": "1f6a9", "crossed_flags": "1f38c", "waving_black_flag": "1f3f4", "black_flag": "1f3f4", "waving_white_flag": "1f3f3-fe0f", "white_flag": "1f3f3-fe0f", "rainbow-flag": "1f3f3-fe0f-200d-1f308", "rainbow_flag": "1f3f3-fe0f-200d-1f308", "transgender_flag": "1f3f3-fe0f-200d-26a7-fe0f", "pirate_flag": "1f3f4-200d-2620-fe0f", "flag-ac": "1f1e6-1f1e8", "flag-ad": "1f1e6-1f1e9", "andorra": "1f1e6-1f1e9", "flag-ae": "1f1e6-1f1ea", "united_arab_emirates": "1f1e6-1f1ea", "flag-af": "1f1e6-1f1eb", "afghanistan": "1f1e6-1f1eb", "flag-ag": "1f1e6-1f1ec", "antigua_barbuda": "1f1e6-1f1ec", "flag-ai": "1f1e6-1f1ee", "anguilla": "1f1e6-1f1ee", "flag-al": "1f1e6-1f1f1", "albania": "1f1e6-1f1f1", "flag-am": "1f1e6-1f1f2", "armenia": "1f1e6-1f1f2", "flag-ao": "1f1e6-1f1f4", "angola": "1f1e6-1f1f4", "flag-aq": "1f1e6-1f1f6", "antarctica": "1f1e6-1f1f6", "flag-ar": "1f1e6-1f1f7", "argentina": "1f1e6-1f1f7", "flag-as": "1f1e6-1f1f8", "american_samoa": "1f1e6-1f1f8", "flag-at": "1f1e6-1f1f9", "austria": "1f1e6-1f1f9", "flag-au": "1f1e6-1f1fa", "australia": "1f1e6-1f1fa", "flag-aw": "1f1e6-1f1fc", "aruba": "1f1e6-1f1fc", "flag-ax": "1f1e6-1f1fd", "aland_islands": "1f1e6-1f1fd", "flag-az": "1f1e6-1f1ff", "azerbaijan": "1f1e6-1f1ff", "flag-ba": "1f1e7-1f1e6", "bosnia_herzegovina": "1f1e7-1f1e6", "flag-bb": "1f1e7-1f1e7", "barbados": "1f1e7-1f1e7", "flag-bd": "1f1e7-1f1e9", "bangladesh": "1f1e7-1f1e9", "flag-be": "1f1e7-1f1ea", "belgium": "1f1e7-1f1ea", "flag-bf": "1f1e7-1f1eb", "burkina_faso": "1f1e7-1f1eb", "flag-bg": "1f1e7-1f1ec", "bulgaria": "1f1e7-1f1ec", "flag-bh": "1f1e7-1f1ed", "bahrain": "1f1e7-1f1ed", "flag-bi": "1f1e7-1f1ee", "burundi": "1f1e7-1f1ee", "flag-bj": "1f1e7-1f1ef", "benin": "1f1e7-1f1ef", "flag-bl": "1f1e7-1f1f1", "st_barthelemy": "1f1e7-1f1f1", "flag-bm": "1f1e7-1f1f2", "bermuda": "1f1e7-1f1f2", "flag-bn": "1f1e7-1f1f3", "brunei": "1f1e7-1f1f3", "flag-bo": "1f1e7-1f1f4", "bolivia": "1f1e7-1f1f4", "flag-bq": "1f1e7-1f1f6", "caribbean_netherlands": "1f1e7-1f1f6", "flag-br": "1f1e7-1f1f7", "brazil": "1f1e7-1f1f7", "flag-bs": "1f1e7-1f1f8", "bahamas": "1f1e7-1f1f8", "flag-bt": "1f1e7-1f1f9", "bhutan": "1f1e7-1f1f9", "flag-bv": "1f1e7-1f1fb", "flag-bw": "1f1e7-1f1fc", "botswana": "1f1e7-1f1fc", "flag-by": "1f1e7-1f1fe", "belarus": "1f1e7-1f1fe", "flag-bz": "1f1e7-1f1ff", "belize": "1f1e7-1f1ff", "flag-ca": "1f1e8-1f1e6", "ca": "1f1e8-1f1e6", "canada": "1f1e8-1f1e6", "flag-cc": "1f1e8-1f1e8", "cocos_islands": "1f1e8-1f1e8", "flag-cd": "1f1e8-1f1e9", "congo_kinshasa": "1f1e8-1f1e9", "flag-cf": "1f1e8-1f1eb", "central_african_republic": "1f1e8-1f1eb", "flag-cg": "1f1e8-1f1ec", "congo_brazzaville": "1f1e8-1f1ec", "flag-ch": "1f1e8-1f1ed", "switzerland": "1f1e8-1f1ed", "flag-ci": "1f1e8-1f1ee", "cote_divoire": "1f1e8-1f1ee", "flag-ck": "1f1e8-1f1f0", "cook_islands": "1f1e8-1f1f0", "flag-cl": "1f1e8-1f1f1", "chile": "1f1e8-1f1f1", "flag-cm": "1f1e8-1f1f2", "cameroon": "1f1e8-1f1f2", "cn": "1f1e8-1f1f3", "flag-cn": "1f1e8-1f1f3", "flag-co": "1f1e8-1f1f4", "colombia": "1f1e8-1f1f4", "flag-cp": "1f1e8-1f1f5", "flag-cr": "1f1e8-1f1f7", "costa_rica": "1f1e8-1f1f7", "flag-cu": "1f1e8-1f1fa", "cuba": "1f1e8-1f1fa", "flag-cv": "1f1e8-1f1fb", "cape_verde": "1f1e8-1f1fb", "flag-cw": "1f1e8-1f1fc", "curacao": "1f1e8-1f1fc", "flag-cx": "1f1e8-1f1fd", "christmas_island": "1f1e8-1f1fd", "flag-cy": "1f1e8-1f1fe", "cyprus": "1f1e8-1f1fe", "flag-cz": "1f1e8-1f1ff", "czech_republic": "1f1e8-1f1ff", "de": "1f1e9-1f1ea", "flag-de": "1f1e9-1f1ea", "flag-dg": "1f1e9-1f1ec", "flag-dj": "1f1e9-1f1ef", "djibouti": "1f1e9-1f1ef", "flag-dk": "1f1e9-1f1f0", "denmark": "1f1e9-1f1f0", "flag-dm": "1f1e9-1f1f2", "dominica": "1f1e9-1f1f2", "flag-do": "1f1e9-1f1f4", "dominican_republic": "1f1e9-1f1f4", "flag-dz": "1f1e9-1f1ff", "algeria": "1f1e9-1f1ff", "flag-ea": "1f1ea-1f1e6", "flag-ec": "1f1ea-1f1e8", "ecuador": "1f1ea-1f1e8", "flag-ee": "1f1ea-1f1ea", "estonia": "1f1ea-1f1ea", "flag-eg": "1f1ea-1f1ec", "egypt": "1f1ea-1f1ec", "flag-eh": "1f1ea-1f1ed", "western_sahara": "1f1ea-1f1ed", "flag-er": "1f1ea-1f1f7", "eritrea": "1f1ea-1f1f7", "es": "1f1ea-1f1f8", "flag-es": "1f1ea-1f1f8", "flag-et": "1f1ea-1f1f9", "ethiopia": "1f1ea-1f1f9", "flag-eu": "1f1ea-1f1fa", "eu": "1f1ea-1f1fa", "european_union": "1f1ea-1f1fa", "flag-fi": "1f1eb-1f1ee", "finland": "1f1eb-1f1ee", "flag-fj": "1f1eb-1f1ef", "fiji": "1f1eb-1f1ef", "flag-fk": "1f1eb-1f1f0", "falkland_islands": "1f1eb-1f1f0", "flag-fm": "1f1eb-1f1f2", "micronesia": "1f1eb-1f1f2", "flag-fo": "1f1eb-1f1f4", "faroe_islands": "1f1eb-1f1f4", "fr": "1f1eb-1f1f7", "flag-fr": "1f1eb-1f1f7", "flag-ga": "1f1ec-1f1e6", "gabon": "1f1ec-1f1e6", "gb": "1f1ec-1f1e7", "uk": "1f1ec-1f1e7", "flag-gb": "1f1ec-1f1e7", "flag-gd": "1f1ec-1f1e9", "grenada": "1f1ec-1f1e9", "flag-ge": "1f1ec-1f1ea", "georgia": "1f1ec-1f1ea", "flag-gf": "1f1ec-1f1eb", "french_guiana": "1f1ec-1f1eb", "flag-gg": "1f1ec-1f1ec", "guernsey": "1f1ec-1f1ec", "flag-gh": "1f1ec-1f1ed", "ghana": "1f1ec-1f1ed", "flag-gi": "1f1ec-1f1ee", "gibraltar": "1f1ec-1f1ee", "flag-gl": "1f1ec-1f1f1", "greenland": "1f1ec-1f1f1", "flag-gm": "1f1ec-1f1f2", "gambia": "1f1ec-1f1f2", "flag-gn": "1f1ec-1f1f3", "guinea": "1f1ec-1f1f3", "flag-gp": "1f1ec-1f1f5", "guadeloupe": "1f1ec-1f1f5", "flag-gq": "1f1ec-1f1f6", "equatorial_guinea": "1f1ec-1f1f6", "flag-gr": "1f1ec-1f1f7", "greece": "1f1ec-1f1f7", "flag-gs": "1f1ec-1f1f8", "south_georgia_south_sandwich_islands": "1f1ec-1f1f8", "flag-gt": "1f1ec-1f1f9", "guatemala": "1f1ec-1f1f9", "flag-gu": "1f1ec-1f1fa", "guam": "1f1ec-1f1fa", "flag-gw": "1f1ec-1f1fc", "guinea_bissau": "1f1ec-1f1fc", "flag-gy": "1f1ec-1f1fe", "guyana": "1f1ec-1f1fe", "flag-hk": "1f1ed-1f1f0", "hong_kong": "1f1ed-1f1f0", "flag-hm": "1f1ed-1f1f2", "flag-hn": "1f1ed-1f1f3", "honduras": "1f1ed-1f1f3", "flag-hr": "1f1ed-1f1f7", "croatia": "1f1ed-1f1f7", "flag-ht": "1f1ed-1f1f9", "haiti": "1f1ed-1f1f9", "flag-hu": "1f1ed-1f1fa", "hungary": "1f1ed-1f1fa", "flag-ic": "1f1ee-1f1e8", "canary_islands": "1f1ee-1f1e8", "flag-id": "1f1ee-1f1e9", "indonesia": "1f1ee-1f1e9", "flag-ie": "1f1ee-1f1ea", "ireland": "1f1ee-1f1ea", "flag-il": "1f1ee-1f1f1", "israel": "1f1ee-1f1f1", "flag-im": "1f1ee-1f1f2", "isle_of_man": "1f1ee-1f1f2", "flag-in": "1f1ee-1f1f3", "india": "1f1ee-1f1f3", "flag-io": "1f1ee-1f1f4", "british_indian_ocean_territory": "1f1ee-1f1f4", "flag-iq": "1f1ee-1f1f6", "iraq": "1f1ee-1f1f6", "flag-ir": "1f1ee-1f1f7", "iran": "1f1ee-1f1f7", "flag-is": "1f1ee-1f1f8", "iceland": "1f1ee-1f1f8", "it": "1f1ee-1f1f9", "flag-it": "1f1ee-1f1f9", "flag-je": "1f1ef-1f1ea", "jersey": "1f1ef-1f1ea", "flag-jm": "1f1ef-1f1f2", "jamaica": "1f1ef-1f1f2", "flag-jo": "1f1ef-1f1f4", "jordan": "1f1ef-1f1f4", "jp": "1f1ef-1f1f5", "flag-jp": "1f1ef-1f1f5", "flag-ke": "1f1f0-1f1ea", "kenya": "1f1f0-1f1ea", "flag-kg": "1f1f0-1f1ec", "kyrgyzstan": "1f1f0-1f1ec", "flag-kh": "1f1f0-1f1ed", "cambodia": "1f1f0-1f1ed", "flag-ki": "1f1f0-1f1ee", "kiribati": "1f1f0-1f1ee", "flag-km": "1f1f0-1f1f2", "comoros": "1f1f0-1f1f2", "flag-kn": "1f1f0-1f1f3", "st_kitts_nevis": "1f1f0-1f1f3", "flag-kp": "1f1f0-1f1f5", "north_korea": "1f1f0-1f1f5", "kr": "1f1f0-1f1f7", "flag-kr": "1f1f0-1f1f7", "flag-kw": "1f1f0-1f1fc", "kuwait": "1f1f0-1f1fc", "flag-ky": "1f1f0-1f1fe", "cayman_islands": "1f1f0-1f1fe", "flag-kz": "1f1f0-1f1ff", "kazakhstan": "1f1f0-1f1ff", "flag-la": "1f1f1-1f1e6", "laos": "1f1f1-1f1e6", "flag-lb": "1f1f1-1f1e7", "lebanon": "1f1f1-1f1e7", "flag-lc": "1f1f1-1f1e8", "st_lucia": "1f1f1-1f1e8", "flag-li": "1f1f1-1f1ee", "liechtenstein": "1f1f1-1f1ee", "flag-lk": "1f1f1-1f1f0", "sri_lanka": "1f1f1-1f1f0", "flag-lr": "1f1f1-1f1f7", "liberia": "1f1f1-1f1f7", "flag-ls": "1f1f1-1f1f8", "lesotho": "1f1f1-1f1f8", "flag-lt": "1f1f1-1f1f9", "lithuania": "1f1f1-1f1f9", "flag-lu": "1f1f1-1f1fa", "luxembourg": "1f1f1-1f1fa", "flag-lv": "1f1f1-1f1fb", "latvia": "1f1f1-1f1fb", "flag-ly": "1f1f1-1f1fe", "libya": "1f1f1-1f1fe", "flag-ma": "1f1f2-1f1e6", "morocco": "1f1f2-1f1e6", "flag-mc": "1f1f2-1f1e8", "monaco": "1f1f2-1f1e8", "flag-md": "1f1f2-1f1e9", "moldova": "1f1f2-1f1e9", "flag-me": "1f1f2-1f1ea", "montenegro": "1f1f2-1f1ea", "flag-mf": "1f1f2-1f1eb", "flag-mg": "1f1f2-1f1ec", "madagascar": "1f1f2-1f1ec", "flag-mh": "1f1f2-1f1ed", "marshall_islands": "1f1f2-1f1ed", "flag-mk": "1f1f2-1f1f0", "macedonia": "1f1f2-1f1f0", "flag-ml": "1f1f2-1f1f1", "mali": "1f1f2-1f1f1", "flag-mm": "1f1f2-1f1f2", "myanmar": "1f1f2-1f1f2", "flag-mn": "1f1f2-1f1f3", "mongolia": "1f1f2-1f1f3", "flag-mo": "1f1f2-1f1f4", "macau": "1f1f2-1f1f4", "flag-mp": "1f1f2-1f1f5", "northern_mariana_islands": "1f1f2-1f1f5", "flag-mq": "1f1f2-1f1f6", "martinique": "1f1f2-1f1f6", "flag-mr": "1f1f2-1f1f7", "mauritania": "1f1f2-1f1f7", "flag-ms": "1f1f2-1f1f8", "montserrat": "1f1f2-1f1f8", "flag-mt": "1f1f2-1f1f9", "malta": "1f1f2-1f1f9", "flag-mu": "1f1f2-1f1fa", "mauritius": "1f1f2-1f1fa", "flag-mv": "1f1f2-1f1fb", "maldives": "1f1f2-1f1fb", "flag-mw": "1f1f2-1f1fc", "malawi": "1f1f2-1f1fc", "flag-mx": "1f1f2-1f1fd", "mexico": "1f1f2-1f1fd", "flag-my": "1f1f2-1f1fe", "malaysia": "1f1f2-1f1fe", "flag-mz": "1f1f2-1f1ff", "mozambique": "1f1f2-1f1ff", "flag-na": "1f1f3-1f1e6", "namibia": "1f1f3-1f1e6", "flag-nc": "1f1f3-1f1e8", "new_caledonia": "1f1f3-1f1e8", "flag-ne": "1f1f3-1f1ea", "niger": "1f1f3-1f1ea", "flag-nf": "1f1f3-1f1eb", "norfolk_island": "1f1f3-1f1eb", "flag-ng": "1f1f3-1f1ec", "nigeria": "1f1f3-1f1ec", "flag-ni": "1f1f3-1f1ee", "nicaragua": "1f1f3-1f1ee", "flag-nl": "1f1f3-1f1f1", "netherlands": "1f1f3-1f1f1", "flag-no": "1f1f3-1f1f4", "norway": "1f1f3-1f1f4", "flag-np": "1f1f3-1f1f5", "nepal": "1f1f3-1f1f5", "flag-nr": "1f1f3-1f1f7", "nauru": "1f1f3-1f1f7", "flag-nu": "1f1f3-1f1fa", "niue": "1f1f3-1f1fa", "flag-nz": "1f1f3-1f1ff", "new_zealand": "1f1f3-1f1ff", "flag-om": "1f1f4-1f1f2", "oman": "1f1f4-1f1f2", "flag-pa": "1f1f5-1f1e6", "panama": "1f1f5-1f1e6", "flag-pe": "1f1f5-1f1ea", "peru": "1f1f5-1f1ea", "flag-pf": "1f1f5-1f1eb", "french_polynesia": "1f1f5-1f1eb", "flag-pg": "1f1f5-1f1ec", "papua_new_guinea": "1f1f5-1f1ec", "flag-ph": "1f1f5-1f1ed", "philippines": "1f1f5-1f1ed", "flag-pk": "1f1f5-1f1f0", "pakistan": "1f1f5-1f1f0", "pk": "1f1f5-1f1f0", "flag-pl": "1f1f5-1f1f1", "poland": "1f1f5-1f1f1", "flag-pm": "1f1f5-1f1f2", "st_pierre_miquelon": "1f1f5-1f1f2", "flag-pn": "1f1f5-1f1f3", "pitcairn_islands": "1f1f5-1f1f3", "flag-pr": "1f1f5-1f1f7", "puerto_rico": "1f1f5-1f1f7", "flag-ps": "1f1f5-1f1f8", "palestinian_territories": "1f1f5-1f1f8", "flag-pt": "1f1f5-1f1f9", "portugal": "1f1f5-1f1f9", "flag-pw": "1f1f5-1f1fc", "palau": "1f1f5-1f1fc", "flag-py": "1f1f5-1f1fe", "paraguay": "1f1f5-1f1fe", "flag-qa": "1f1f6-1f1e6", "qatar": "1f1f6-1f1e6", "flag-re": "1f1f7-1f1ea", "reunion": "1f1f7-1f1ea", "flag-ro": "1f1f7-1f1f4", "romania": "1f1f7-1f1f4", "flag-rs": "1f1f7-1f1f8", "serbia": "1f1f7-1f1f8", "ru": "1f1f7-1f1fa", "flag-ru": "1f1f7-1f1fa", "flag-rw": "1f1f7-1f1fc", "rwanda": "1f1f7-1f1fc", "flag-sa": "1f1f8-1f1e6", "saudi_arabia": "1f1f8-1f1e6", "flag-sb": "1f1f8-1f1e7", "solomon_islands": "1f1f8-1f1e7", "flag-sc": "1f1f8-1f1e8", "seychelles": "1f1f8-1f1e8", "flag-sd": "1f1f8-1f1e9", "sudan": "1f1f8-1f1e9", "flag-se": "1f1f8-1f1ea", "sweden": "1f1f8-1f1ea", "flag-sg": "1f1f8-1f1ec", "singapore": "1f1f8-1f1ec", "flag-sh": "1f1f8-1f1ed", "st_helena": "1f1f8-1f1ed", "flag-si": "1f1f8-1f1ee", "slovenia": "1f1f8-1f1ee", "flag-sj": "1f1f8-1f1ef", "flag-sk": "1f1f8-1f1f0", "slovakia": "1f1f8-1f1f0", "flag-sl": "1f1f8-1f1f1", "sierra_leone": "1f1f8-1f1f1", "flag-sm": "1f1f8-1f1f2", "san_marino": "1f1f8-1f1f2", "flag-sn": "1f1f8-1f1f3", "senegal": "1f1f8-1f1f3", "flag-so": "1f1f8-1f1f4", "somalia": "1f1f8-1f1f4", "flag-sr": "1f1f8-1f1f7", "suriname": "1f1f8-1f1f7", "flag-ss": "1f1f8-1f1f8", "south_sudan": "1f1f8-1f1f8", "flag-st": "1f1f8-1f1f9", "sao_tome_principe": "1f1f8-1f1f9", "flag-sv": "1f1f8-1f1fb", "el_salvador": "1f1f8-1f1fb", "flag-sx": "1f1f8-1f1fd", "sint_maarten": "1f1f8-1f1fd", "flag-sy": "1f1f8-1f1fe", "syria": "1f1f8-1f1fe", "flag-sz": "1f1f8-1f1ff", "swaziland": "1f1f8-1f1ff", "flag-ta": "1f1f9-1f1e6", "flag-tc": "1f1f9-1f1e8", "turks_caicos_islands": "1f1f9-1f1e8", "flag-td": "1f1f9-1f1e9", "chad": "1f1f9-1f1e9", "flag-tf": "1f1f9-1f1eb", "french_southern_territories": "1f1f9-1f1eb", "flag-tg": "1f1f9-1f1ec", "togo": "1f1f9-1f1ec", "flag-th": "1f1f9-1f1ed", "thailand": "1f1f9-1f1ed", "flag-tj": "1f1f9-1f1ef", "tajikistan": "1f1f9-1f1ef", "flag-tk": "1f1f9-1f1f0", "tokelau": "1f1f9-1f1f0", "flag-tl": "1f1f9-1f1f1", "timor_leste": "1f1f9-1f1f1", "flag-tm": "1f1f9-1f1f2", "turkmenistan": "1f1f9-1f1f2", "flag-tn": "1f1f9-1f1f3", "tunisia": "1f1f9-1f1f3", "flag-to": "1f1f9-1f1f4", "tonga": "1f1f9-1f1f4", "flag-tr": "1f1f9-1f1f7", "tr": "1f1f9-1f1f7", "flag-tt": "1f1f9-1f1f9", "trinidad_tobago": "1f1f9-1f1f9", "flag-tv": "1f1f9-1f1fb", "tuvalu": "1f1f9-1f1fb", "flag-tw": "1f1f9-1f1fc", "taiwan": "1f1f9-1f1fc", "flag-tz": "1f1f9-1f1ff", "tanzania": "1f1f9-1f1ff", "flag-ua": "1f1fa-1f1e6", "ukraine": "1f1fa-1f1e6", "flag-ug": "1f1fa-1f1ec", "uganda": "1f1fa-1f1ec", "flag-um": "1f1fa-1f1f2", "flag-un": "1f1fa-1f1f3", "us": "1f1fa-1f1f8", "flag-us": "1f1fa-1f1f8", "flag-uy": "1f1fa-1f1fe", "uruguay": "1f1fa-1f1fe", "flag-uz": "1f1fa-1f1ff", "uzbekistan": "1f1fa-1f1ff", "flag-va": "1f1fb-1f1e6", "vatican_city": "1f1fb-1f1e6", "flag-vc": "1f1fb-1f1e8", "st_vincent_grenadines": "1f1fb-1f1e8", "flag-ve": "1f1fb-1f1ea", "venezuela": "1f1fb-1f1ea", "flag-vg": "1f1fb-1f1ec", "british_virgin_islands": "1f1fb-1f1ec", "flag-vi": "1f1fb-1f1ee", "us_virgin_islands": "1f1fb-1f1ee", "flag-vn": "1f1fb-1f1f3", "vietnam": "1f1fb-1f1f3", "flag-vu": "1f1fb-1f1fa", "vanuatu": "1f1fb-1f1fa", "flag-wf": "1f1fc-1f1eb", "wallis_futuna": "1f1fc-1f1eb", "flag-ws": "1f1fc-1f1f8", "samoa": "1f1fc-1f1f8", "flag-xk": "1f1fd-1f1f0", "kosovo": "1f1fd-1f1f0", "flag-ye": "1f1fe-1f1ea", "yemen": "1f1fe-1f1ea", "flag-yt": "1f1fe-1f1f9", "mayotte": "1f1fe-1f1f9", "flag-za": "1f1ff-1f1e6", "south_africa": "1f1ff-1f1e6", "za": "1f1ff-1f1e6", "flag-zm": "1f1ff-1f1f2", "zambia": "1f1ff-1f1f2", "flag-zw": "1f1ff-1f1fc", "zimbabwe": "1f1ff-1f1fc", "flag-england": "1f3f4-e0067-e0062-e0065-e006e-e0067-e007f", "flag-scotland": "1f3f4-e0067-e0062-e0073-e0063-e0074-e007f", "flag-wales": "1f3f4-e0067-e0062-e0077-e006c-e0073-e007f", "santa_light_skin_tone": "1f385-1f3fb", "santa_medium_light_skin_tone": "1f385-1f3fc", "santa_medium_skin_tone": "1f385-1f3fd", "santa_medium_dark_skin_tone": "1f385-1f3fe", "santa_dark_skin_tone": "1f385-1f3ff", "snowboarder_light_skin_tone": "1f3c2-1f3fb", "snowboarder_medium_light_skin_tone": "1f3c2-1f3fc", "snowboarder_medium_skin_tone": "1f3c2-1f3fd", "snowboarder_medium_dark_skin_tone": "1f3c2-1f3fe", "snowboarder_dark_skin_tone": "1f3c2-1f3ff", "woman-running_light_skin_tone": "1f3c3-1f3fb-200d-2640-fe0f", "running_woman_light_skin_tone": "1f3c3-1f3fb-200d-2640-fe0f", "woman-running_medium_light_skin_tone": "1f3c3-1f3fc-200d-2640-fe0f", "running_woman_medium_light_skin_tone": "1f3c3-1f3fc-200d-2640-fe0f", "woman-running_medium_skin_tone": "1f3c3-1f3fd-200d-2640-fe0f", "running_woman_medium_skin_tone": "1f3c3-1f3fd-200d-2640-fe0f", "woman-running_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2640-fe0f", "running_woman_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2640-fe0f", "woman-running_dark_skin_tone": "1f3c3-1f3ff-200d-2640-fe0f", "running_woman_dark_skin_tone": "1f3c3-1f3ff-200d-2640-fe0f", "man-running_light_skin_tone": "1f3c3-1f3fb-200d-2642-fe0f", "running_man_light_skin_tone": "1f3c3-1f3fb-200d-2642-fe0f", "man-running_medium_light_skin_tone": "1f3c3-1f3fc-200d-2642-fe0f", "running_man_medium_light_skin_tone": "1f3c3-1f3fc-200d-2642-fe0f", "man-running_medium_skin_tone": "1f3c3-1f3fd-200d-2642-fe0f", "running_man_medium_skin_tone": "1f3c3-1f3fd-200d-2642-fe0f", "man-running_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2642-fe0f", "running_man_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2642-fe0f", "man-running_dark_skin_tone": "1f3c3-1f3ff-200d-2642-fe0f", "running_man_dark_skin_tone": "1f3c3-1f3ff-200d-2642-fe0f", "runner_light_skin_tone": "1f3c3-1f3fb", "running_light_skin_tone": "1f3c3-1f3fb", "runner_medium_light_skin_tone": "1f3c3-1f3fc", "running_medium_light_skin_tone": "1f3c3-1f3fc", "runner_medium_skin_tone": "1f3c3-1f3fd", "running_medium_skin_tone": "1f3c3-1f3fd", "runner_medium_dark_skin_tone": "1f3c3-1f3fe", "running_medium_dark_skin_tone": "1f3c3-1f3fe", "runner_dark_skin_tone": "1f3c3-1f3ff", "running_dark_skin_tone": "1f3c3-1f3ff", "woman-surfing_light_skin_tone": "1f3c4-1f3fb-200d-2640-fe0f", "surfing_woman_light_skin_tone": "1f3c4-1f3fb-200d-2640-fe0f", "woman-surfing_medium_light_skin_tone": "1f3c4-1f3fc-200d-2640-fe0f", "surfing_woman_medium_light_skin_tone": "1f3c4-1f3fc-200d-2640-fe0f", "woman-surfing_medium_skin_tone": "1f3c4-1f3fd-200d-2640-fe0f", "surfing_woman_medium_skin_tone": "1f3c4-1f3fd-200d-2640-fe0f", "woman-surfing_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2640-fe0f", "surfing_woman_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2640-fe0f", "woman-surfing_dark_skin_tone": "1f3c4-1f3ff-200d-2640-fe0f", "surfing_woman_dark_skin_tone": "1f3c4-1f3ff-200d-2640-fe0f", "man-surfing_light_skin_tone": "1f3c4-1f3fb-200d-2642-fe0f", "surfing_man_light_skin_tone": "1f3c4-1f3fb-200d-2642-fe0f", "man-surfing_medium_light_skin_tone": "1f3c4-1f3fc-200d-2642-fe0f", "surfing_man_medium_light_skin_tone": "1f3c4-1f3fc-200d-2642-fe0f", "man-surfing_medium_skin_tone": "1f3c4-1f3fd-200d-2642-fe0f", "surfing_man_medium_skin_tone": "1f3c4-1f3fd-200d-2642-fe0f", "man-surfing_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2642-fe0f", "surfing_man_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2642-fe0f", "man-surfing_dark_skin_tone": "1f3c4-1f3ff-200d-2642-fe0f", "surfing_man_dark_skin_tone": "1f3c4-1f3ff-200d-2642-fe0f", "surfer_light_skin_tone": "1f3c4-1f3fb", "surfer_medium_light_skin_tone": "1f3c4-1f3fc", "surfer_medium_skin_tone": "1f3c4-1f3fd", "surfer_medium_dark_skin_tone": "1f3c4-1f3fe", "surfer_dark_skin_tone": "1f3c4-1f3ff", "horse_racing_light_skin_tone": "1f3c7-1f3fb", "horse_racing_medium_light_skin_tone": "1f3c7-1f3fc", "horse_racing_medium_skin_tone": "1f3c7-1f3fd", "horse_racing_medium_dark_skin_tone": "1f3c7-1f3fe", "horse_racing_dark_skin_tone": "1f3c7-1f3ff", "woman-swimming_light_skin_tone": "1f3ca-1f3fb-200d-2640-fe0f", "swimming_woman_light_skin_tone": "1f3ca-1f3fb-200d-2640-fe0f", "woman-swimming_medium_light_skin_tone": "1f3ca-1f3fc-200d-2640-fe0f", "swimming_woman_medium_light_skin_tone": "1f3ca-1f3fc-200d-2640-fe0f", "woman-swimming_medium_skin_tone": "1f3ca-1f3fd-200d-2640-fe0f", "swimming_woman_medium_skin_tone": "1f3ca-1f3fd-200d-2640-fe0f", "woman-swimming_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2640-fe0f", "swimming_woman_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2640-fe0f", "woman-swimming_dark_skin_tone": "1f3ca-1f3ff-200d-2640-fe0f", "swimming_woman_dark_skin_tone": "1f3ca-1f3ff-200d-2640-fe0f", "man-swimming_light_skin_tone": "1f3ca-1f3fb-200d-2642-fe0f", "swimming_man_light_skin_tone": "1f3ca-1f3fb-200d-2642-fe0f", "man-swimming_medium_light_skin_tone": "1f3ca-1f3fc-200d-2642-fe0f", "swimming_man_medium_light_skin_tone": "1f3ca-1f3fc-200d-2642-fe0f", "man-swimming_medium_skin_tone": "1f3ca-1f3fd-200d-2642-fe0f", "swimming_man_medium_skin_tone": "1f3ca-1f3fd-200d-2642-fe0f", "man-swimming_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2642-fe0f", "swimming_man_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2642-fe0f", "man-swimming_dark_skin_tone": "1f3ca-1f3ff-200d-2642-fe0f", "swimming_man_dark_skin_tone": "1f3ca-1f3ff-200d-2642-fe0f", "swimmer_light_skin_tone": "1f3ca-1f3fb", "swimmer_medium_light_skin_tone": "1f3ca-1f3fc", "swimmer_medium_skin_tone": "1f3ca-1f3fd", "swimmer_medium_dark_skin_tone": "1f3ca-1f3fe", "swimmer_dark_skin_tone": "1f3ca-1f3ff", "woman-lifting-weights_light_skin_tone": "1f3cb-1f3fb-200d-2640-fe0f", "weight_lifting_woman_light_skin_tone": "1f3cb-1f3fb-200d-2640-fe0f", "woman-lifting-weights_medium_light_skin_tone": "1f3cb-1f3fc-200d-2640-fe0f", "weight_lifting_woman_medium_light_skin_tone": "1f3cb-1f3fc-200d-2640-fe0f", "woman-lifting-weights_medium_skin_tone": "1f3cb-1f3fd-200d-2640-fe0f", "weight_lifting_woman_medium_skin_tone": "1f3cb-1f3fd-200d-2640-fe0f", "woman-lifting-weights_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2640-fe0f", "weight_lifting_woman_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2640-fe0f", "woman-lifting-weights_dark_skin_tone": "1f3cb-1f3ff-200d-2640-fe0f", "weight_lifting_woman_dark_skin_tone": "1f3cb-1f3ff-200d-2640-fe0f", "man-lifting-weights_light_skin_tone": "1f3cb-1f3fb-200d-2642-fe0f", "weight_lifting_man_light_skin_tone": "1f3cb-1f3fb-200d-2642-fe0f", "man-lifting-weights_medium_light_skin_tone": "1f3cb-1f3fc-200d-2642-fe0f", "weight_lifting_man_medium_light_skin_tone": "1f3cb-1f3fc-200d-2642-fe0f", "man-lifting-weights_medium_skin_tone": "1f3cb-1f3fd-200d-2642-fe0f", "weight_lifting_man_medium_skin_tone": "1f3cb-1f3fd-200d-2642-fe0f", "man-lifting-weights_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2642-fe0f", "weight_lifting_man_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2642-fe0f", "man-lifting-weights_dark_skin_tone": "1f3cb-1f3ff-200d-2642-fe0f", "weight_lifting_man_dark_skin_tone": "1f3cb-1f3ff-200d-2642-fe0f", "weight_lifter_light_skin_tone": "1f3cb-1f3fb", "weight_lifter_medium_light_skin_tone": "1f3cb-1f3fc", "weight_lifter_medium_skin_tone": "1f3cb-1f3fd", "weight_lifter_medium_dark_skin_tone": "1f3cb-1f3fe", "weight_lifter_dark_skin_tone": "1f3cb-1f3ff", "woman-golfing_light_skin_tone": "1f3cc-1f3fb-200d-2640-fe0f", "golfing_woman_light_skin_tone": "1f3cc-1f3fb-200d-2640-fe0f", "woman-golfing_medium_light_skin_tone": "1f3cc-1f3fc-200d-2640-fe0f", "golfing_woman_medium_light_skin_tone": "1f3cc-1f3fc-200d-2640-fe0f", "woman-golfing_medium_skin_tone": "1f3cc-1f3fd-200d-2640-fe0f", "golfing_woman_medium_skin_tone": "1f3cc-1f3fd-200d-2640-fe0f", "woman-golfing_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2640-fe0f", "golfing_woman_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2640-fe0f", "woman-golfing_dark_skin_tone": "1f3cc-1f3ff-200d-2640-fe0f", "golfing_woman_dark_skin_tone": "1f3cc-1f3ff-200d-2640-fe0f", "man-golfing_light_skin_tone": "1f3cc-1f3fb-200d-2642-fe0f", "golfing_man_light_skin_tone": "1f3cc-1f3fb-200d-2642-fe0f", "man-golfing_medium_light_skin_tone": "1f3cc-1f3fc-200d-2642-fe0f", "golfing_man_medium_light_skin_tone": "1f3cc-1f3fc-200d-2642-fe0f", "man-golfing_medium_skin_tone": "1f3cc-1f3fd-200d-2642-fe0f", "golfing_man_medium_skin_tone": "1f3cc-1f3fd-200d-2642-fe0f", "man-golfing_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2642-fe0f", "golfing_man_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2642-fe0f", "man-golfing_dark_skin_tone": "1f3cc-1f3ff-200d-2642-fe0f", "golfing_man_dark_skin_tone": "1f3cc-1f3ff-200d-2642-fe0f", "golfer_light_skin_tone": "1f3cc-1f3fb", "golfer_medium_light_skin_tone": "1f3cc-1f3fc", "golfer_medium_skin_tone": "1f3cc-1f3fd", "golfer_medium_dark_skin_tone": "1f3cc-1f3fe", "golfer_dark_skin_tone": "1f3cc-1f3ff", "ear_light_skin_tone": "1f442-1f3fb", "ear_medium_light_skin_tone": "1f442-1f3fc", "ear_medium_skin_tone": "1f442-1f3fd", "ear_medium_dark_skin_tone": "1f442-1f3fe", "ear_dark_skin_tone": "1f442-1f3ff", "nose_light_skin_tone": "1f443-1f3fb", "nose_medium_light_skin_tone": "1f443-1f3fc", "nose_medium_skin_tone": "1f443-1f3fd", "nose_medium_dark_skin_tone": "1f443-1f3fe", "nose_dark_skin_tone": "1f443-1f3ff", "point_up_2_light_skin_tone": "1f446-1f3fb", "point_up_2_medium_light_skin_tone": "1f446-1f3fc", "point_up_2_medium_skin_tone": "1f446-1f3fd", "point_up_2_medium_dark_skin_tone": "1f446-1f3fe", "point_up_2_dark_skin_tone": "1f446-1f3ff", "point_down_light_skin_tone": "1f447-1f3fb", "point_down_medium_light_skin_tone": "1f447-1f3fc", "point_down_medium_skin_tone": "1f447-1f3fd", "point_down_medium_dark_skin_tone": "1f447-1f3fe", "point_down_dark_skin_tone": "1f447-1f3ff", "point_left_light_skin_tone": "1f448-1f3fb", "point_left_medium_light_skin_tone": "1f448-1f3fc", "point_left_medium_skin_tone": "1f448-1f3fd", "point_left_medium_dark_skin_tone": "1f448-1f3fe", "point_left_dark_skin_tone": "1f448-1f3ff", "point_right_light_skin_tone": "1f449-1f3fb", "point_right_medium_light_skin_tone": "1f449-1f3fc", "point_right_medium_skin_tone": "1f449-1f3fd", "point_right_medium_dark_skin_tone": "1f449-1f3fe", "point_right_dark_skin_tone": "1f449-1f3ff", "facepunch_light_skin_tone": "1f44a-1f3fb", "punch_light_skin_tone": "1f44a-1f3fb", "fist_oncoming_light_skin_tone": "1f44a-1f3fb", "facepunch_medium_light_skin_tone": "1f44a-1f3fc", "punch_medium_light_skin_tone": "1f44a-1f3fc", "fist_oncoming_medium_light_skin_tone": "1f44a-1f3fc", "facepunch_medium_skin_tone": "1f44a-1f3fd", "punch_medium_skin_tone": "1f44a-1f3fd", "fist_oncoming_medium_skin_tone": "1f44a-1f3fd", "facepunch_medium_dark_skin_tone": "1f44a-1f3fe", "punch_medium_dark_skin_tone": "1f44a-1f3fe", "fist_oncoming_medium_dark_skin_tone": "1f44a-1f3fe", "facepunch_dark_skin_tone": "1f44a-1f3ff", "punch_dark_skin_tone": "1f44a-1f3ff", "fist_oncoming_dark_skin_tone": "1f44a-1f3ff", "wave_light_skin_tone": "1f44b-1f3fb", "wave_medium_light_skin_tone": "1f44b-1f3fc", "wave_medium_skin_tone": "1f44b-1f3fd", "wave_medium_dark_skin_tone": "1f44b-1f3fe", "wave_dark_skin_tone": "1f44b-1f3ff", "ok_hand_light_skin_tone": "1f44c-1f3fb", "ok_hand_medium_light_skin_tone": "1f44c-1f3fc", "ok_hand_medium_skin_tone": "1f44c-1f3fd", "ok_hand_medium_dark_skin_tone": "1f44c-1f3fe", "ok_hand_dark_skin_tone": "1f44c-1f3ff", "+1_light_skin_tone": "1f44d-1f3fb", "thumbsup_light_skin_tone": "1f44d-1f3fb", "+1_medium_light_skin_tone": "1f44d-1f3fc", "thumbsup_medium_light_skin_tone": "1f44d-1f3fc", "+1_medium_skin_tone": "1f44d-1f3fd", "thumbsup_medium_skin_tone": "1f44d-1f3fd", "+1_medium_dark_skin_tone": "1f44d-1f3fe", "thumbsup_medium_dark_skin_tone": "1f44d-1f3fe", "+1_dark_skin_tone": "1f44d-1f3ff", "thumbsup_dark_skin_tone": "1f44d-1f3ff", "-1_light_skin_tone": "1f44e-1f3fb", "thumbsdown_light_skin_tone": "1f44e-1f3fb", "-1_medium_light_skin_tone": "1f44e-1f3fc", "thumbsdown_medium_light_skin_tone": "1f44e-1f3fc", "-1_medium_skin_tone": "1f44e-1f3fd", "thumbsdown_medium_skin_tone": "1f44e-1f3fd", "-1_medium_dark_skin_tone": "1f44e-1f3fe", "thumbsdown_medium_dark_skin_tone": "1f44e-1f3fe", "-1_dark_skin_tone": "1f44e-1f3ff", "thumbsdown_dark_skin_tone": "1f44e-1f3ff", "clap_light_skin_tone": "1f44f-1f3fb", "clap_medium_light_skin_tone": "1f44f-1f3fc", "clap_medium_skin_tone": "1f44f-1f3fd", "clap_medium_dark_skin_tone": "1f44f-1f3fe", "clap_dark_skin_tone": "1f44f-1f3ff", "open_hands_light_skin_tone": "1f450-1f3fb", "open_hands_medium_light_skin_tone": "1f450-1f3fc", "open_hands_medium_skin_tone": "1f450-1f3fd", "open_hands_medium_dark_skin_tone": "1f450-1f3fe", "open_hands_dark_skin_tone": "1f450-1f3ff", "boy_light_skin_tone": "1f466-1f3fb", "boy_medium_light_skin_tone": "1f466-1f3fc", "boy_medium_skin_tone": "1f466-1f3fd", "boy_medium_dark_skin_tone": "1f466-1f3fe", "boy_dark_skin_tone": "1f466-1f3ff", "girl_light_skin_tone": "1f467-1f3fb", "girl_medium_light_skin_tone": "1f467-1f3fc", "girl_medium_skin_tone": "1f467-1f3fd", "girl_medium_dark_skin_tone": "1f467-1f3fe", "girl_dark_skin_tone": "1f467-1f3ff", "male-farmer_light_skin_tone": "1f468-1f3fb-200d-1f33e", "man_farmer_light_skin_tone": "1f468-1f3fb-200d-1f33e", "male-farmer_medium_light_skin_tone": "1f468-1f3fc-200d-1f33e", "man_farmer_medium_light_skin_tone": "1f468-1f3fc-200d-1f33e", "male-farmer_medium_skin_tone": "1f468-1f3fd-200d-1f33e", "man_farmer_medium_skin_tone": "1f468-1f3fd-200d-1f33e", "male-farmer_medium_dark_skin_tone": "1f468-1f3fe-200d-1f33e", "man_farmer_medium_dark_skin_tone": "1f468-1f3fe-200d-1f33e", "male-farmer_dark_skin_tone": "1f468-1f3ff-200d-1f33e", "man_farmer_dark_skin_tone": "1f468-1f3ff-200d-1f33e", "male-cook_light_skin_tone": "1f468-1f3fb-200d-1f373", "man_cook_light_skin_tone": "1f468-1f3fb-200d-1f373", "male-cook_medium_light_skin_tone": "1f468-1f3fc-200d-1f373", "man_cook_medium_light_skin_tone": "1f468-1f3fc-200d-1f373", "male-cook_medium_skin_tone": "1f468-1f3fd-200d-1f373", "man_cook_medium_skin_tone": "1f468-1f3fd-200d-1f373", "male-cook_medium_dark_skin_tone": "1f468-1f3fe-200d-1f373", "man_cook_medium_dark_skin_tone": "1f468-1f3fe-200d-1f373", "male-cook_dark_skin_tone": "1f468-1f3ff-200d-1f373", "man_cook_dark_skin_tone": "1f468-1f3ff-200d-1f373", "man_feeding_baby_light_skin_tone": "1f468-1f3fb-200d-1f37c", "man_feeding_baby_medium_light_skin_tone": "1f468-1f3fc-200d-1f37c", "man_feeding_baby_medium_skin_tone": "1f468-1f3fd-200d-1f37c", "man_feeding_baby_medium_dark_skin_tone": "1f468-1f3fe-200d-1f37c", "man_feeding_baby_dark_skin_tone": "1f468-1f3ff-200d-1f37c", "male-student_light_skin_tone": "1f468-1f3fb-200d-1f393", "man_student_light_skin_tone": "1f468-1f3fb-200d-1f393", "male-student_medium_light_skin_tone": "1f468-1f3fc-200d-1f393", "man_student_medium_light_skin_tone": "1f468-1f3fc-200d-1f393", "male-student_medium_skin_tone": "1f468-1f3fd-200d-1f393", "man_student_medium_skin_tone": "1f468-1f3fd-200d-1f393", "male-student_medium_dark_skin_tone": "1f468-1f3fe-200d-1f393", "man_student_medium_dark_skin_tone": "1f468-1f3fe-200d-1f393", "male-student_dark_skin_tone": "1f468-1f3ff-200d-1f393", "man_student_dark_skin_tone": "1f468-1f3ff-200d-1f393", "male-singer_light_skin_tone": "1f468-1f3fb-200d-1f3a4", "man_singer_light_skin_tone": "1f468-1f3fb-200d-1f3a4", "male-singer_medium_light_skin_tone": "1f468-1f3fc-200d-1f3a4", "man_singer_medium_light_skin_tone": "1f468-1f3fc-200d-1f3a4", "male-singer_medium_skin_tone": "1f468-1f3fd-200d-1f3a4", "man_singer_medium_skin_tone": "1f468-1f3fd-200d-1f3a4", "male-singer_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3a4", "man_singer_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3a4", "male-singer_dark_skin_tone": "1f468-1f3ff-200d-1f3a4", "man_singer_dark_skin_tone": "1f468-1f3ff-200d-1f3a4", "male-artist_light_skin_tone": "1f468-1f3fb-200d-1f3a8", "man_artist_light_skin_tone": "1f468-1f3fb-200d-1f3a8", "male-artist_medium_light_skin_tone": "1f468-1f3fc-200d-1f3a8", "man_artist_medium_light_skin_tone": "1f468-1f3fc-200d-1f3a8", "male-artist_medium_skin_tone": "1f468-1f3fd-200d-1f3a8", "man_artist_medium_skin_tone": "1f468-1f3fd-200d-1f3a8", "male-artist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3a8", "man_artist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3a8", "male-artist_dark_skin_tone": "1f468-1f3ff-200d-1f3a8", "man_artist_dark_skin_tone": "1f468-1f3ff-200d-1f3a8", "male-teacher_light_skin_tone": "1f468-1f3fb-200d-1f3eb", "man_teacher_light_skin_tone": "1f468-1f3fb-200d-1f3eb", "male-teacher_medium_light_skin_tone": "1f468-1f3fc-200d-1f3eb", "man_teacher_medium_light_skin_tone": "1f468-1f3fc-200d-1f3eb", "male-teacher_medium_skin_tone": "1f468-1f3fd-200d-1f3eb", "man_teacher_medium_skin_tone": "1f468-1f3fd-200d-1f3eb", "male-teacher_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3eb", "man_teacher_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3eb", "male-teacher_dark_skin_tone": "1f468-1f3ff-200d-1f3eb", "man_teacher_dark_skin_tone": "1f468-1f3ff-200d-1f3eb", "male-factory-worker_light_skin_tone": "1f468-1f3fb-200d-1f3ed", "man_factory_worker_light_skin_tone": "1f468-1f3fb-200d-1f3ed", "male-factory-worker_medium_light_skin_tone": "1f468-1f3fc-200d-1f3ed", "man_factory_worker_medium_light_skin_tone": "1f468-1f3fc-200d-1f3ed", "male-factory-worker_medium_skin_tone": "1f468-1f3fd-200d-1f3ed", "man_factory_worker_medium_skin_tone": "1f468-1f3fd-200d-1f3ed", "male-factory-worker_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3ed", "man_factory_worker_medium_dark_skin_tone": "1f468-1f3fe-200d-1f3ed", "male-factory-worker_dark_skin_tone": "1f468-1f3ff-200d-1f3ed", "man_factory_worker_dark_skin_tone": "1f468-1f3ff-200d-1f3ed", "male-technologist_light_skin_tone": "1f468-1f3fb-200d-1f4bb", "man_technologist_light_skin_tone": "1f468-1f3fb-200d-1f4bb", "male-technologist_medium_light_skin_tone": "1f468-1f3fc-200d-1f4bb", "man_technologist_medium_light_skin_tone": "1f468-1f3fc-200d-1f4bb", "male-technologist_medium_skin_tone": "1f468-1f3fd-200d-1f4bb", "man_technologist_medium_skin_tone": "1f468-1f3fd-200d-1f4bb", "male-technologist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f4bb", "man_technologist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f4bb", "male-technologist_dark_skin_tone": "1f468-1f3ff-200d-1f4bb", "man_technologist_dark_skin_tone": "1f468-1f3ff-200d-1f4bb", "male-office-worker_light_skin_tone": "1f468-1f3fb-200d-1f4bc", "man_office_worker_light_skin_tone": "1f468-1f3fb-200d-1f4bc", "male-office-worker_medium_light_skin_tone": "1f468-1f3fc-200d-1f4bc", "man_office_worker_medium_light_skin_tone": "1f468-1f3fc-200d-1f4bc", "male-office-worker_medium_skin_tone": "1f468-1f3fd-200d-1f4bc", "man_office_worker_medium_skin_tone": "1f468-1f3fd-200d-1f4bc", "male-office-worker_medium_dark_skin_tone": "1f468-1f3fe-200d-1f4bc", "man_office_worker_medium_dark_skin_tone": "1f468-1f3fe-200d-1f4bc", "male-office-worker_dark_skin_tone": "1f468-1f3ff-200d-1f4bc", "man_office_worker_dark_skin_tone": "1f468-1f3ff-200d-1f4bc", "male-mechanic_light_skin_tone": "1f468-1f3fb-200d-1f527", "man_mechanic_light_skin_tone": "1f468-1f3fb-200d-1f527", "male-mechanic_medium_light_skin_tone": "1f468-1f3fc-200d-1f527", "man_mechanic_medium_light_skin_tone": "1f468-1f3fc-200d-1f527", "male-mechanic_medium_skin_tone": "1f468-1f3fd-200d-1f527", "man_mechanic_medium_skin_tone": "1f468-1f3fd-200d-1f527", "male-mechanic_medium_dark_skin_tone": "1f468-1f3fe-200d-1f527", "man_mechanic_medium_dark_skin_tone": "1f468-1f3fe-200d-1f527", "male-mechanic_dark_skin_tone": "1f468-1f3ff-200d-1f527", "man_mechanic_dark_skin_tone": "1f468-1f3ff-200d-1f527", "male-scientist_light_skin_tone": "1f468-1f3fb-200d-1f52c", "man_scientist_light_skin_tone": "1f468-1f3fb-200d-1f52c", "male-scientist_medium_light_skin_tone": "1f468-1f3fc-200d-1f52c", "man_scientist_medium_light_skin_tone": "1f468-1f3fc-200d-1f52c", "male-scientist_medium_skin_tone": "1f468-1f3fd-200d-1f52c", "man_scientist_medium_skin_tone": "1f468-1f3fd-200d-1f52c", "male-scientist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f52c", "man_scientist_medium_dark_skin_tone": "1f468-1f3fe-200d-1f52c", "male-scientist_dark_skin_tone": "1f468-1f3ff-200d-1f52c", "man_scientist_dark_skin_tone": "1f468-1f3ff-200d-1f52c", "male-astronaut_light_skin_tone": "1f468-1f3fb-200d-1f680", "man_astronaut_light_skin_tone": "1f468-1f3fb-200d-1f680", "male-astronaut_medium_light_skin_tone": "1f468-1f3fc-200d-1f680", "man_astronaut_medium_light_skin_tone": "1f468-1f3fc-200d-1f680", "male-astronaut_medium_skin_tone": "1f468-1f3fd-200d-1f680", "man_astronaut_medium_skin_tone": "1f468-1f3fd-200d-1f680", "male-astronaut_medium_dark_skin_tone": "1f468-1f3fe-200d-1f680", "man_astronaut_medium_dark_skin_tone": "1f468-1f3fe-200d-1f680", "male-astronaut_dark_skin_tone": "1f468-1f3ff-200d-1f680", "man_astronaut_dark_skin_tone": "1f468-1f3ff-200d-1f680", "male-firefighter_light_skin_tone": "1f468-1f3fb-200d-1f692", "man_firefighter_light_skin_tone": "1f468-1f3fb-200d-1f692", "male-firefighter_medium_light_skin_tone": "1f468-1f3fc-200d-1f692", "man_firefighter_medium_light_skin_tone": "1f468-1f3fc-200d-1f692", "male-firefighter_medium_skin_tone": "1f468-1f3fd-200d-1f692", "man_firefighter_medium_skin_tone": "1f468-1f3fd-200d-1f692", "male-firefighter_medium_dark_skin_tone": "1f468-1f3fe-200d-1f692", "man_firefighter_medium_dark_skin_tone": "1f468-1f3fe-200d-1f692", "male-firefighter_dark_skin_tone": "1f468-1f3ff-200d-1f692", "man_firefighter_dark_skin_tone": "1f468-1f3ff-200d-1f692", "man_with_probing_cane_light_skin_tone": "1f468-1f3fb-200d-1f9af", "man_with_probing_cane_medium_light_skin_tone": "1f468-1f3fc-200d-1f9af", "man_with_probing_cane_medium_skin_tone": "1f468-1f3fd-200d-1f9af", "man_with_probing_cane_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9af", "man_with_probing_cane_dark_skin_tone": "1f468-1f3ff-200d-1f9af", "red_haired_man_light_skin_tone": "1f468-1f3fb-200d-1f9b0", "red_haired_man_medium_light_skin_tone": "1f468-1f3fc-200d-1f9b0", "red_haired_man_medium_skin_tone": "1f468-1f3fd-200d-1f9b0", "red_haired_man_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9b0", "red_haired_man_dark_skin_tone": "1f468-1f3ff-200d-1f9b0", "curly_haired_man_light_skin_tone": "1f468-1f3fb-200d-1f9b1", "curly_haired_man_medium_light_skin_tone": "1f468-1f3fc-200d-1f9b1", "curly_haired_man_medium_skin_tone": "1f468-1f3fd-200d-1f9b1", "curly_haired_man_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9b1", "curly_haired_man_dark_skin_tone": "1f468-1f3ff-200d-1f9b1", "bald_man_light_skin_tone": "1f468-1f3fb-200d-1f9b2", "bald_man_medium_light_skin_tone": "1f468-1f3fc-200d-1f9b2", "bald_man_medium_skin_tone": "1f468-1f3fd-200d-1f9b2", "bald_man_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9b2", "bald_man_dark_skin_tone": "1f468-1f3ff-200d-1f9b2", "white_haired_man_light_skin_tone": "1f468-1f3fb-200d-1f9b3", "white_haired_man_medium_light_skin_tone": "1f468-1f3fc-200d-1f9b3", "white_haired_man_medium_skin_tone": "1f468-1f3fd-200d-1f9b3", "white_haired_man_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9b3", "white_haired_man_dark_skin_tone": "1f468-1f3ff-200d-1f9b3", "man_in_motorized_wheelchair_light_skin_tone": "1f468-1f3fb-200d-1f9bc", "man_in_motorized_wheelchair_medium_light_skin_tone": "1f468-1f3fc-200d-1f9bc", "man_in_motorized_wheelchair_medium_skin_tone": "1f468-1f3fd-200d-1f9bc", "man_in_motorized_wheelchair_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9bc", "man_in_motorized_wheelchair_dark_skin_tone": "1f468-1f3ff-200d-1f9bc", "man_in_manual_wheelchair_light_skin_tone": "1f468-1f3fb-200d-1f9bd", "man_in_manual_wheelchair_medium_light_skin_tone": "1f468-1f3fc-200d-1f9bd", "man_in_manual_wheelchair_medium_skin_tone": "1f468-1f3fd-200d-1f9bd", "man_in_manual_wheelchair_medium_dark_skin_tone": "1f468-1f3fe-200d-1f9bd", "man_in_manual_wheelchair_dark_skin_tone": "1f468-1f3ff-200d-1f9bd", "male-doctor_light_skin_tone": "1f468-1f3fb-200d-2695-fe0f", "man_health_worker_light_skin_tone": "1f468-1f3fb-200d-2695-fe0f", "male-doctor_medium_light_skin_tone": "1f468-1f3fc-200d-2695-fe0f", "man_health_worker_medium_light_skin_tone": "1f468-1f3fc-200d-2695-fe0f", "male-doctor_medium_skin_tone": "1f468-1f3fd-200d-2695-fe0f", "man_health_worker_medium_skin_tone": "1f468-1f3fd-200d-2695-fe0f", "male-doctor_medium_dark_skin_tone": "1f468-1f3fe-200d-2695-fe0f", "man_health_worker_medium_dark_skin_tone": "1f468-1f3fe-200d-2695-fe0f", "male-doctor_dark_skin_tone": "1f468-1f3ff-200d-2695-fe0f", "man_health_worker_dark_skin_tone": "1f468-1f3ff-200d-2695-fe0f", "male-judge_light_skin_tone": "1f468-1f3fb-200d-2696-fe0f", "man_judge_light_skin_tone": "1f468-1f3fb-200d-2696-fe0f", "male-judge_medium_light_skin_tone": "1f468-1f3fc-200d-2696-fe0f", "man_judge_medium_light_skin_tone": "1f468-1f3fc-200d-2696-fe0f", "male-judge_medium_skin_tone": "1f468-1f3fd-200d-2696-fe0f", "man_judge_medium_skin_tone": "1f468-1f3fd-200d-2696-fe0f", "male-judge_medium_dark_skin_tone": "1f468-1f3fe-200d-2696-fe0f", "man_judge_medium_dark_skin_tone": "1f468-1f3fe-200d-2696-fe0f", "male-judge_dark_skin_tone": "1f468-1f3ff-200d-2696-fe0f", "man_judge_dark_skin_tone": "1f468-1f3ff-200d-2696-fe0f", "male-pilot_light_skin_tone": "1f468-1f3fb-200d-2708-fe0f", "man_pilot_light_skin_tone": "1f468-1f3fb-200d-2708-fe0f", "male-pilot_medium_light_skin_tone": "1f468-1f3fc-200d-2708-fe0f", "man_pilot_medium_light_skin_tone": "1f468-1f3fc-200d-2708-fe0f", "male-pilot_medium_skin_tone": "1f468-1f3fd-200d-2708-fe0f", "man_pilot_medium_skin_tone": "1f468-1f3fd-200d-2708-fe0f", "male-pilot_medium_dark_skin_tone": "1f468-1f3fe-200d-2708-fe0f", "man_pilot_medium_dark_skin_tone": "1f468-1f3fe-200d-2708-fe0f", "male-pilot_dark_skin_tone": "1f468-1f3ff-200d-2708-fe0f", "man_pilot_dark_skin_tone": "1f468-1f3ff-200d-2708-fe0f", "man_light_skin_tone": "1f468-1f3fb", "man_medium_light_skin_tone": "1f468-1f3fc", "man_medium_skin_tone": "1f468-1f3fd", "man_medium_dark_skin_tone": "1f468-1f3fe", "man_dark_skin_tone": "1f468-1f3ff", "female-farmer_light_skin_tone": "1f469-1f3fb-200d-1f33e", "woman_farmer_light_skin_tone": "1f469-1f3fb-200d-1f33e", "female-farmer_medium_light_skin_tone": "1f469-1f3fc-200d-1f33e", "woman_farmer_medium_light_skin_tone": "1f469-1f3fc-200d-1f33e", "female-farmer_medium_skin_tone": "1f469-1f3fd-200d-1f33e", "woman_farmer_medium_skin_tone": "1f469-1f3fd-200d-1f33e", "female-farmer_medium_dark_skin_tone": "1f469-1f3fe-200d-1f33e", "woman_farmer_medium_dark_skin_tone": "1f469-1f3fe-200d-1f33e", "female-farmer_dark_skin_tone": "1f469-1f3ff-200d-1f33e", "woman_farmer_dark_skin_tone": "1f469-1f3ff-200d-1f33e", "female-cook_light_skin_tone": "1f469-1f3fb-200d-1f373", "woman_cook_light_skin_tone": "1f469-1f3fb-200d-1f373", "female-cook_medium_light_skin_tone": "1f469-1f3fc-200d-1f373", "woman_cook_medium_light_skin_tone": "1f469-1f3fc-200d-1f373", "female-cook_medium_skin_tone": "1f469-1f3fd-200d-1f373", "woman_cook_medium_skin_tone": "1f469-1f3fd-200d-1f373", "female-cook_medium_dark_skin_tone": "1f469-1f3fe-200d-1f373", "woman_cook_medium_dark_skin_tone": "1f469-1f3fe-200d-1f373", "female-cook_dark_skin_tone": "1f469-1f3ff-200d-1f373", "woman_cook_dark_skin_tone": "1f469-1f3ff-200d-1f373", "woman_feeding_baby_light_skin_tone": "1f469-1f3fb-200d-1f37c", "woman_feeding_baby_medium_light_skin_tone": "1f469-1f3fc-200d-1f37c", "woman_feeding_baby_medium_skin_tone": "1f469-1f3fd-200d-1f37c", "woman_feeding_baby_medium_dark_skin_tone": "1f469-1f3fe-200d-1f37c", "woman_feeding_baby_dark_skin_tone": "1f469-1f3ff-200d-1f37c", "female-student_light_skin_tone": "1f469-1f3fb-200d-1f393", "woman_student_light_skin_tone": "1f469-1f3fb-200d-1f393", "female-student_medium_light_skin_tone": "1f469-1f3fc-200d-1f393", "woman_student_medium_light_skin_tone": "1f469-1f3fc-200d-1f393", "female-student_medium_skin_tone": "1f469-1f3fd-200d-1f393", "woman_student_medium_skin_tone": "1f469-1f3fd-200d-1f393", "female-student_medium_dark_skin_tone": "1f469-1f3fe-200d-1f393", "woman_student_medium_dark_skin_tone": "1f469-1f3fe-200d-1f393", "female-student_dark_skin_tone": "1f469-1f3ff-200d-1f393", "woman_student_dark_skin_tone": "1f469-1f3ff-200d-1f393", "female-singer_light_skin_tone": "1f469-1f3fb-200d-1f3a4", "woman_singer_light_skin_tone": "1f469-1f3fb-200d-1f3a4", "female-singer_medium_light_skin_tone": "1f469-1f3fc-200d-1f3a4", "woman_singer_medium_light_skin_tone": "1f469-1f3fc-200d-1f3a4", "female-singer_medium_skin_tone": "1f469-1f3fd-200d-1f3a4", "woman_singer_medium_skin_tone": "1f469-1f3fd-200d-1f3a4", "female-singer_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3a4", "woman_singer_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3a4", "female-singer_dark_skin_tone": "1f469-1f3ff-200d-1f3a4", "woman_singer_dark_skin_tone": "1f469-1f3ff-200d-1f3a4", "female-artist_light_skin_tone": "1f469-1f3fb-200d-1f3a8", "woman_artist_light_skin_tone": "1f469-1f3fb-200d-1f3a8", "female-artist_medium_light_skin_tone": "1f469-1f3fc-200d-1f3a8", "woman_artist_medium_light_skin_tone": "1f469-1f3fc-200d-1f3a8", "female-artist_medium_skin_tone": "1f469-1f3fd-200d-1f3a8", "woman_artist_medium_skin_tone": "1f469-1f3fd-200d-1f3a8", "female-artist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3a8", "woman_artist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3a8", "female-artist_dark_skin_tone": "1f469-1f3ff-200d-1f3a8", "woman_artist_dark_skin_tone": "1f469-1f3ff-200d-1f3a8", "female-teacher_light_skin_tone": "1f469-1f3fb-200d-1f3eb", "woman_teacher_light_skin_tone": "1f469-1f3fb-200d-1f3eb", "female-teacher_medium_light_skin_tone": "1f469-1f3fc-200d-1f3eb", "woman_teacher_medium_light_skin_tone": "1f469-1f3fc-200d-1f3eb", "female-teacher_medium_skin_tone": "1f469-1f3fd-200d-1f3eb", "woman_teacher_medium_skin_tone": "1f469-1f3fd-200d-1f3eb", "female-teacher_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3eb", "woman_teacher_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3eb", "female-teacher_dark_skin_tone": "1f469-1f3ff-200d-1f3eb", "woman_teacher_dark_skin_tone": "1f469-1f3ff-200d-1f3eb", "female-factory-worker_light_skin_tone": "1f469-1f3fb-200d-1f3ed", "woman_factory_worker_light_skin_tone": "1f469-1f3fb-200d-1f3ed", "female-factory-worker_medium_light_skin_tone": "1f469-1f3fc-200d-1f3ed", "woman_factory_worker_medium_light_skin_tone": "1f469-1f3fc-200d-1f3ed", "female-factory-worker_medium_skin_tone": "1f469-1f3fd-200d-1f3ed", "woman_factory_worker_medium_skin_tone": "1f469-1f3fd-200d-1f3ed", "female-factory-worker_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3ed", "woman_factory_worker_medium_dark_skin_tone": "1f469-1f3fe-200d-1f3ed", "female-factory-worker_dark_skin_tone": "1f469-1f3ff-200d-1f3ed", "woman_factory_worker_dark_skin_tone": "1f469-1f3ff-200d-1f3ed", "female-technologist_light_skin_tone": "1f469-1f3fb-200d-1f4bb", "woman_technologist_light_skin_tone": "1f469-1f3fb-200d-1f4bb", "female-technologist_medium_light_skin_tone": "1f469-1f3fc-200d-1f4bb", "woman_technologist_medium_light_skin_tone": "1f469-1f3fc-200d-1f4bb", "female-technologist_medium_skin_tone": "1f469-1f3fd-200d-1f4bb", "woman_technologist_medium_skin_tone": "1f469-1f3fd-200d-1f4bb", "female-technologist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f4bb", "woman_technologist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f4bb", "female-technologist_dark_skin_tone": "1f469-1f3ff-200d-1f4bb", "woman_technologist_dark_skin_tone": "1f469-1f3ff-200d-1f4bb", "female-office-worker_light_skin_tone": "1f469-1f3fb-200d-1f4bc", "woman_office_worker_light_skin_tone": "1f469-1f3fb-200d-1f4bc", "female-office-worker_medium_light_skin_tone": "1f469-1f3fc-200d-1f4bc", "woman_office_worker_medium_light_skin_tone": "1f469-1f3fc-200d-1f4bc", "female-office-worker_medium_skin_tone": "1f469-1f3fd-200d-1f4bc", "woman_office_worker_medium_skin_tone": "1f469-1f3fd-200d-1f4bc", "female-office-worker_medium_dark_skin_tone": "1f469-1f3fe-200d-1f4bc", "woman_office_worker_medium_dark_skin_tone": "1f469-1f3fe-200d-1f4bc", "female-office-worker_dark_skin_tone": "1f469-1f3ff-200d-1f4bc", "woman_office_worker_dark_skin_tone": "1f469-1f3ff-200d-1f4bc", "female-mechanic_light_skin_tone": "1f469-1f3fb-200d-1f527", "woman_mechanic_light_skin_tone": "1f469-1f3fb-200d-1f527", "female-mechanic_medium_light_skin_tone": "1f469-1f3fc-200d-1f527", "woman_mechanic_medium_light_skin_tone": "1f469-1f3fc-200d-1f527", "female-mechanic_medium_skin_tone": "1f469-1f3fd-200d-1f527", "woman_mechanic_medium_skin_tone": "1f469-1f3fd-200d-1f527", "female-mechanic_medium_dark_skin_tone": "1f469-1f3fe-200d-1f527", "woman_mechanic_medium_dark_skin_tone": "1f469-1f3fe-200d-1f527", "female-mechanic_dark_skin_tone": "1f469-1f3ff-200d-1f527", "woman_mechanic_dark_skin_tone": "1f469-1f3ff-200d-1f527", "female-scientist_light_skin_tone": "1f469-1f3fb-200d-1f52c", "woman_scientist_light_skin_tone": "1f469-1f3fb-200d-1f52c", "female-scientist_medium_light_skin_tone": "1f469-1f3fc-200d-1f52c", "woman_scientist_medium_light_skin_tone": "1f469-1f3fc-200d-1f52c", "female-scientist_medium_skin_tone": "1f469-1f3fd-200d-1f52c", "woman_scientist_medium_skin_tone": "1f469-1f3fd-200d-1f52c", "female-scientist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f52c", "woman_scientist_medium_dark_skin_tone": "1f469-1f3fe-200d-1f52c", "female-scientist_dark_skin_tone": "1f469-1f3ff-200d-1f52c", "woman_scientist_dark_skin_tone": "1f469-1f3ff-200d-1f52c", "female-astronaut_light_skin_tone": "1f469-1f3fb-200d-1f680", "woman_astronaut_light_skin_tone": "1f469-1f3fb-200d-1f680", "female-astronaut_medium_light_skin_tone": "1f469-1f3fc-200d-1f680", "woman_astronaut_medium_light_skin_tone": "1f469-1f3fc-200d-1f680", "female-astronaut_medium_skin_tone": "1f469-1f3fd-200d-1f680", "woman_astronaut_medium_skin_tone": "1f469-1f3fd-200d-1f680", "female-astronaut_medium_dark_skin_tone": "1f469-1f3fe-200d-1f680", "woman_astronaut_medium_dark_skin_tone": "1f469-1f3fe-200d-1f680", "female-astronaut_dark_skin_tone": "1f469-1f3ff-200d-1f680", "woman_astronaut_dark_skin_tone": "1f469-1f3ff-200d-1f680", "female-firefighter_light_skin_tone": "1f469-1f3fb-200d-1f692", "woman_firefighter_light_skin_tone": "1f469-1f3fb-200d-1f692", "female-firefighter_medium_light_skin_tone": "1f469-1f3fc-200d-1f692", "woman_firefighter_medium_light_skin_tone": "1f469-1f3fc-200d-1f692", "female-firefighter_medium_skin_tone": "1f469-1f3fd-200d-1f692", "woman_firefighter_medium_skin_tone": "1f469-1f3fd-200d-1f692", "female-firefighter_medium_dark_skin_tone": "1f469-1f3fe-200d-1f692", "woman_firefighter_medium_dark_skin_tone": "1f469-1f3fe-200d-1f692", "female-firefighter_dark_skin_tone": "1f469-1f3ff-200d-1f692", "woman_firefighter_dark_skin_tone": "1f469-1f3ff-200d-1f692", "woman_with_probing_cane_light_skin_tone": "1f469-1f3fb-200d-1f9af", "woman_with_probing_cane_medium_light_skin_tone": "1f469-1f3fc-200d-1f9af", "woman_with_probing_cane_medium_skin_tone": "1f469-1f3fd-200d-1f9af", "woman_with_probing_cane_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9af", "woman_with_probing_cane_dark_skin_tone": "1f469-1f3ff-200d-1f9af", "red_haired_woman_light_skin_tone": "1f469-1f3fb-200d-1f9b0", "red_haired_woman_medium_light_skin_tone": "1f469-1f3fc-200d-1f9b0", "red_haired_woman_medium_skin_tone": "1f469-1f3fd-200d-1f9b0", "red_haired_woman_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9b0", "red_haired_woman_dark_skin_tone": "1f469-1f3ff-200d-1f9b0", "curly_haired_woman_light_skin_tone": "1f469-1f3fb-200d-1f9b1", "curly_haired_woman_medium_light_skin_tone": "1f469-1f3fc-200d-1f9b1", "curly_haired_woman_medium_skin_tone": "1f469-1f3fd-200d-1f9b1", "curly_haired_woman_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9b1", "curly_haired_woman_dark_skin_tone": "1f469-1f3ff-200d-1f9b1", "bald_woman_light_skin_tone": "1f469-1f3fb-200d-1f9b2", "bald_woman_medium_light_skin_tone": "1f469-1f3fc-200d-1f9b2", "bald_woman_medium_skin_tone": "1f469-1f3fd-200d-1f9b2", "bald_woman_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9b2", "bald_woman_dark_skin_tone": "1f469-1f3ff-200d-1f9b2", "white_haired_woman_light_skin_tone": "1f469-1f3fb-200d-1f9b3", "white_haired_woman_medium_light_skin_tone": "1f469-1f3fc-200d-1f9b3", "white_haired_woman_medium_skin_tone": "1f469-1f3fd-200d-1f9b3", "white_haired_woman_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9b3", "white_haired_woman_dark_skin_tone": "1f469-1f3ff-200d-1f9b3", "woman_in_motorized_wheelchair_light_skin_tone": "1f469-1f3fb-200d-1f9bc", "woman_in_motorized_wheelchair_medium_light_skin_tone": "1f469-1f3fc-200d-1f9bc", "woman_in_motorized_wheelchair_medium_skin_tone": "1f469-1f3fd-200d-1f9bc", "woman_in_motorized_wheelchair_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9bc", "woman_in_motorized_wheelchair_dark_skin_tone": "1f469-1f3ff-200d-1f9bc", "woman_in_manual_wheelchair_light_skin_tone": "1f469-1f3fb-200d-1f9bd", "woman_in_manual_wheelchair_medium_light_skin_tone": "1f469-1f3fc-200d-1f9bd", "woman_in_manual_wheelchair_medium_skin_tone": "1f469-1f3fd-200d-1f9bd", "woman_in_manual_wheelchair_medium_dark_skin_tone": "1f469-1f3fe-200d-1f9bd", "woman_in_manual_wheelchair_dark_skin_tone": "1f469-1f3ff-200d-1f9bd", "female-doctor_light_skin_tone": "1f469-1f3fb-200d-2695-fe0f", "woman_health_worker_light_skin_tone": "1f469-1f3fb-200d-2695-fe0f", "female-doctor_medium_light_skin_tone": "1f469-1f3fc-200d-2695-fe0f", "woman_health_worker_medium_light_skin_tone": "1f469-1f3fc-200d-2695-fe0f", "female-doctor_medium_skin_tone": "1f469-1f3fd-200d-2695-fe0f", "woman_health_worker_medium_skin_tone": "1f469-1f3fd-200d-2695-fe0f", "female-doctor_medium_dark_skin_tone": "1f469-1f3fe-200d-2695-fe0f", "woman_health_worker_medium_dark_skin_tone": "1f469-1f3fe-200d-2695-fe0f", "female-doctor_dark_skin_tone": "1f469-1f3ff-200d-2695-fe0f", "woman_health_worker_dark_skin_tone": "1f469-1f3ff-200d-2695-fe0f", "female-judge_light_skin_tone": "1f469-1f3fb-200d-2696-fe0f", "woman_judge_light_skin_tone": "1f469-1f3fb-200d-2696-fe0f", "female-judge_medium_light_skin_tone": "1f469-1f3fc-200d-2696-fe0f", "woman_judge_medium_light_skin_tone": "1f469-1f3fc-200d-2696-fe0f", "female-judge_medium_skin_tone": "1f469-1f3fd-200d-2696-fe0f", "woman_judge_medium_skin_tone": "1f469-1f3fd-200d-2696-fe0f", "female-judge_medium_dark_skin_tone": "1f469-1f3fe-200d-2696-fe0f", "woman_judge_medium_dark_skin_tone": "1f469-1f3fe-200d-2696-fe0f", "female-judge_dark_skin_tone": "1f469-1f3ff-200d-2696-fe0f", "woman_judge_dark_skin_tone": "1f469-1f3ff-200d-2696-fe0f", "female-pilot_light_skin_tone": "1f469-1f3fb-200d-2708-fe0f", "woman_pilot_light_skin_tone": "1f469-1f3fb-200d-2708-fe0f", "female-pilot_medium_light_skin_tone": "1f469-1f3fc-200d-2708-fe0f", "woman_pilot_medium_light_skin_tone": "1f469-1f3fc-200d-2708-fe0f", "female-pilot_medium_skin_tone": "1f469-1f3fd-200d-2708-fe0f", "woman_pilot_medium_skin_tone": "1f469-1f3fd-200d-2708-fe0f", "female-pilot_medium_dark_skin_tone": "1f469-1f3fe-200d-2708-fe0f", "woman_pilot_medium_dark_skin_tone": "1f469-1f3fe-200d-2708-fe0f", "female-pilot_dark_skin_tone": "1f469-1f3ff-200d-2708-fe0f", "woman_pilot_dark_skin_tone": "1f469-1f3ff-200d-2708-fe0f", "woman_light_skin_tone": "1f469-1f3fb", "woman_medium_light_skin_tone": "1f469-1f3fc", "woman_medium_skin_tone": "1f469-1f3fd", "woman_medium_dark_skin_tone": "1f469-1f3fe", "woman_dark_skin_tone": "1f469-1f3ff", "man_and_woman_holding_hands_light_skin_tone": "1f46b-1f3fb", "woman_and_man_holding_hands_light_skin_tone": "1f46b-1f3fb", "couple_light_skin_tone": "1f46b-1f3fb", "man_and_woman_holding_hands_medium_light_skin_tone": "1f46b-1f3fc", "woman_and_man_holding_hands_medium_light_skin_tone": "1f46b-1f3fc", "couple_medium_light_skin_tone": "1f46b-1f3fc", "man_and_woman_holding_hands_medium_skin_tone": "1f46b-1f3fd", "woman_and_man_holding_hands_medium_skin_tone": "1f46b-1f3fd", "couple_medium_skin_tone": "1f46b-1f3fd", "man_and_woman_holding_hands_medium_dark_skin_tone": "1f46b-1f3fe", "woman_and_man_holding_hands_medium_dark_skin_tone": "1f46b-1f3fe", "couple_medium_dark_skin_tone": "1f46b-1f3fe", "man_and_woman_holding_hands_dark_skin_tone": "1f46b-1f3ff", "woman_and_man_holding_hands_dark_skin_tone": "1f46b-1f3ff", "couple_dark_skin_tone": "1f46b-1f3ff", "man_and_woman_holding_hands_light_skin_tone_medium_light_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc", "woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc", "couple_light_skin_tone_medium_light_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc", "man_and_woman_holding_hands_light_skin_tone_medium_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd", "woman_and_man_holding_hands_light_skin_tone_medium_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd", "couple_light_skin_tone_medium_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd", "man_and_woman_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe", "woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe", "couple_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe", "man_and_woman_holding_hands_light_skin_tone_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff", "woman_and_man_holding_hands_light_skin_tone_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff", "couple_light_skin_tone_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff", "man_and_woman_holding_hands_medium_light_skin_tone_light_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb", "woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb", "couple_medium_light_skin_tone_light_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb", "man_and_woman_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd", "woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd", "couple_medium_light_skin_tone_medium_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd", "man_and_woman_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe", "woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe", "couple_medium_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe", "man_and_woman_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff", "woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff", "couple_medium_light_skin_tone_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff", "man_and_woman_holding_hands_medium_skin_tone_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb", "woman_and_man_holding_hands_medium_skin_tone_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb", "couple_medium_skin_tone_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb", "man_and_woman_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc", "woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc", "couple_medium_skin_tone_medium_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc", "man_and_woman_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe", "woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe", "couple_medium_skin_tone_medium_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe", "man_and_woman_holding_hands_medium_skin_tone_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff", "woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff", "couple_medium_skin_tone_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff", "man_and_woman_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb", "woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb", "couple_medium_dark_skin_tone_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb", "man_and_woman_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc", "woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc", "couple_medium_dark_skin_tone_medium_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc", "man_and_woman_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd", "woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd", "couple_medium_dark_skin_tone_medium_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd", "man_and_woman_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff", "woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff", "couple_medium_dark_skin_tone_dark_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff", "man_and_woman_holding_hands_dark_skin_tone_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb", "woman_and_man_holding_hands_dark_skin_tone_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb", "couple_dark_skin_tone_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb", "man_and_woman_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc", "woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc", "couple_dark_skin_tone_medium_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc", "man_and_woman_holding_hands_dark_skin_tone_medium_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd", "woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd", "couple_dark_skin_tone_medium_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd", "man_and_woman_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe", "woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe", "couple_dark_skin_tone_medium_dark_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe", "two_men_holding_hands_light_skin_tone": "1f46c-1f3fb", "men_holding_hands_light_skin_tone": "1f46c-1f3fb", "two_men_holding_hands_medium_light_skin_tone": "1f46c-1f3fc", "men_holding_hands_medium_light_skin_tone": "1f46c-1f3fc", "two_men_holding_hands_medium_skin_tone": "1f46c-1f3fd", "men_holding_hands_medium_skin_tone": "1f46c-1f3fd", "two_men_holding_hands_medium_dark_skin_tone": "1f46c-1f3fe", "men_holding_hands_medium_dark_skin_tone": "1f46c-1f3fe", "two_men_holding_hands_dark_skin_tone": "1f46c-1f3ff", "men_holding_hands_dark_skin_tone": "1f46c-1f3ff", "two_men_holding_hands_light_skin_tone_medium_light_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc", "men_holding_hands_light_skin_tone_medium_light_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc", "two_men_holding_hands_light_skin_tone_medium_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd", "men_holding_hands_light_skin_tone_medium_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd", "two_men_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe", "men_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe", "two_men_holding_hands_light_skin_tone_dark_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff", "men_holding_hands_light_skin_tone_dark_skin_tone": "1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff", "two_men_holding_hands_medium_light_skin_tone_light_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb", "men_holding_hands_medium_light_skin_tone_light_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb", "two_men_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd", "men_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd", "two_men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe", "men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe", "two_men_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff", "men_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff", "two_men_holding_hands_medium_skin_tone_light_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb", "men_holding_hands_medium_skin_tone_light_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb", "two_men_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc", "men_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc", "two_men_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe", "men_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe", "two_men_holding_hands_medium_skin_tone_dark_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff", "men_holding_hands_medium_skin_tone_dark_skin_tone": "1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff", "two_men_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb", "men_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb", "two_men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc", "men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc", "two_men_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd", "men_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd", "two_men_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff", "men_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff", "two_men_holding_hands_dark_skin_tone_light_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb", "men_holding_hands_dark_skin_tone_light_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb", "two_men_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc", "men_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc", "two_men_holding_hands_dark_skin_tone_medium_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd", "men_holding_hands_dark_skin_tone_medium_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd", "two_men_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe", "men_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe", "two_women_holding_hands_light_skin_tone": "1f46d-1f3fb", "women_holding_hands_light_skin_tone": "1f46d-1f3fb", "two_women_holding_hands_medium_light_skin_tone": "1f46d-1f3fc", "women_holding_hands_medium_light_skin_tone": "1f46d-1f3fc", "two_women_holding_hands_medium_skin_tone": "1f46d-1f3fd", "women_holding_hands_medium_skin_tone": "1f46d-1f3fd", "two_women_holding_hands_medium_dark_skin_tone": "1f46d-1f3fe", "women_holding_hands_medium_dark_skin_tone": "1f46d-1f3fe", "two_women_holding_hands_dark_skin_tone": "1f46d-1f3ff", "women_holding_hands_dark_skin_tone": "1f46d-1f3ff", "two_women_holding_hands_light_skin_tone_medium_light_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc", "women_holding_hands_light_skin_tone_medium_light_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc", "two_women_holding_hands_light_skin_tone_medium_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd", "women_holding_hands_light_skin_tone_medium_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd", "two_women_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe", "women_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe", "two_women_holding_hands_light_skin_tone_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff", "women_holding_hands_light_skin_tone_dark_skin_tone": "1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff", "two_women_holding_hands_medium_light_skin_tone_light_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb", "women_holding_hands_medium_light_skin_tone_light_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb", "two_women_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd", "women_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd", "two_women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe", "women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe", "two_women_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff", "women_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff", "two_women_holding_hands_medium_skin_tone_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb", "women_holding_hands_medium_skin_tone_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb", "two_women_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc", "women_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc", "two_women_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe", "women_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe", "two_women_holding_hands_medium_skin_tone_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff", "women_holding_hands_medium_skin_tone_dark_skin_tone": "1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff", "two_women_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb", "women_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb", "two_women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc", "women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc", "two_women_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd", "women_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd", "two_women_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff", "women_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff", "two_women_holding_hands_dark_skin_tone_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb", "women_holding_hands_dark_skin_tone_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb", "two_women_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc", "women_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc", "two_women_holding_hands_dark_skin_tone_medium_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd", "women_holding_hands_dark_skin_tone_medium_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd", "two_women_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe", "women_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe", "female-police-officer_light_skin_tone": "1f46e-1f3fb-200d-2640-fe0f", "policewoman_light_skin_tone": "1f46e-1f3fb-200d-2640-fe0f", "female-police-officer_medium_light_skin_tone": "1f46e-1f3fc-200d-2640-fe0f", "policewoman_medium_light_skin_tone": "1f46e-1f3fc-200d-2640-fe0f", "female-police-officer_medium_skin_tone": "1f46e-1f3fd-200d-2640-fe0f", "policewoman_medium_skin_tone": "1f46e-1f3fd-200d-2640-fe0f", "female-police-officer_medium_dark_skin_tone": "1f46e-1f3fe-200d-2640-fe0f", "policewoman_medium_dark_skin_tone": "1f46e-1f3fe-200d-2640-fe0f", "female-police-officer_dark_skin_tone": "1f46e-1f3ff-200d-2640-fe0f", "policewoman_dark_skin_tone": "1f46e-1f3ff-200d-2640-fe0f", "male-police-officer_light_skin_tone": "1f46e-1f3fb-200d-2642-fe0f", "policeman_light_skin_tone": "1f46e-1f3fb-200d-2642-fe0f", "male-police-officer_medium_light_skin_tone": "1f46e-1f3fc-200d-2642-fe0f", "policeman_medium_light_skin_tone": "1f46e-1f3fc-200d-2642-fe0f", "male-police-officer_medium_skin_tone": "1f46e-1f3fd-200d-2642-fe0f", "policeman_medium_skin_tone": "1f46e-1f3fd-200d-2642-fe0f", "male-police-officer_medium_dark_skin_tone": "1f46e-1f3fe-200d-2642-fe0f", "policeman_medium_dark_skin_tone": "1f46e-1f3fe-200d-2642-fe0f", "male-police-officer_dark_skin_tone": "1f46e-1f3ff-200d-2642-fe0f", "policeman_dark_skin_tone": "1f46e-1f3ff-200d-2642-fe0f", "cop_light_skin_tone": "1f46e-1f3fb", "cop_medium_light_skin_tone": "1f46e-1f3fc", "cop_medium_skin_tone": "1f46e-1f3fd", "cop_medium_dark_skin_tone": "1f46e-1f3fe", "cop_dark_skin_tone": "1f46e-1f3ff", "woman_with_veil_light_skin_tone": "1f470-1f3fb-200d-2640-fe0f", "woman_with_veil_medium_light_skin_tone": "1f470-1f3fc-200d-2640-fe0f", "woman_with_veil_medium_skin_tone": "1f470-1f3fd-200d-2640-fe0f", "woman_with_veil_medium_dark_skin_tone": "1f470-1f3fe-200d-2640-fe0f", "woman_with_veil_dark_skin_tone": "1f470-1f3ff-200d-2640-fe0f", "man_with_veil_light_skin_tone": "1f470-1f3fb-200d-2642-fe0f", "man_with_veil_medium_light_skin_tone": "1f470-1f3fc-200d-2642-fe0f", "man_with_veil_medium_skin_tone": "1f470-1f3fd-200d-2642-fe0f", "man_with_veil_medium_dark_skin_tone": "1f470-1f3fe-200d-2642-fe0f", "man_with_veil_dark_skin_tone": "1f470-1f3ff-200d-2642-fe0f", "bride_with_veil_light_skin_tone": "1f470-1f3fb", "bride_with_veil_medium_light_skin_tone": "1f470-1f3fc", "bride_with_veil_medium_skin_tone": "1f470-1f3fd", "bride_with_veil_medium_dark_skin_tone": "1f470-1f3fe", "bride_with_veil_dark_skin_tone": "1f470-1f3ff", "blond-haired-woman_light_skin_tone": "1f471-1f3fb-200d-2640-fe0f", "blonde_woman_light_skin_tone": "1f471-1f3fb-200d-2640-fe0f", "blond-haired-woman_medium_light_skin_tone": "1f471-1f3fc-200d-2640-fe0f", "blonde_woman_medium_light_skin_tone": "1f471-1f3fc-200d-2640-fe0f", "blond-haired-woman_medium_skin_tone": "1f471-1f3fd-200d-2640-fe0f", "blonde_woman_medium_skin_tone": "1f471-1f3fd-200d-2640-fe0f", "blond-haired-woman_medium_dark_skin_tone": "1f471-1f3fe-200d-2640-fe0f", "blonde_woman_medium_dark_skin_tone": "1f471-1f3fe-200d-2640-fe0f", "blond-haired-woman_dark_skin_tone": "1f471-1f3ff-200d-2640-fe0f", "blonde_woman_dark_skin_tone": "1f471-1f3ff-200d-2640-fe0f", "blond-haired-man_light_skin_tone": "1f471-1f3fb-200d-2642-fe0f", "blonde_man_light_skin_tone": "1f471-1f3fb-200d-2642-fe0f", "blond-haired-man_medium_light_skin_tone": "1f471-1f3fc-200d-2642-fe0f", "blonde_man_medium_light_skin_tone": "1f471-1f3fc-200d-2642-fe0f", "blond-haired-man_medium_skin_tone": "1f471-1f3fd-200d-2642-fe0f", "blonde_man_medium_skin_tone": "1f471-1f3fd-200d-2642-fe0f", "blond-haired-man_medium_dark_skin_tone": "1f471-1f3fe-200d-2642-fe0f", "blonde_man_medium_dark_skin_tone": "1f471-1f3fe-200d-2642-fe0f", "blond-haired-man_dark_skin_tone": "1f471-1f3ff-200d-2642-fe0f", "blonde_man_dark_skin_tone": "1f471-1f3ff-200d-2642-fe0f", "person_with_blond_hair_light_skin_tone": "1f471-1f3fb", "person_with_blond_hair_medium_light_skin_tone": "1f471-1f3fc", "person_with_blond_hair_medium_skin_tone": "1f471-1f3fd", "person_with_blond_hair_medium_dark_skin_tone": "1f471-1f3fe", "person_with_blond_hair_dark_skin_tone": "1f471-1f3ff", "man_with_gua_pi_mao_light_skin_tone": "1f472-1f3fb", "man_with_gua_pi_mao_medium_light_skin_tone": "1f472-1f3fc", "man_with_gua_pi_mao_medium_skin_tone": "1f472-1f3fd", "man_with_gua_pi_mao_medium_dark_skin_tone": "1f472-1f3fe", "man_with_gua_pi_mao_dark_skin_tone": "1f472-1f3ff", "woman-wearing-turban_light_skin_tone": "1f473-1f3fb-200d-2640-fe0f", "woman_with_turban_light_skin_tone": "1f473-1f3fb-200d-2640-fe0f", "woman-wearing-turban_medium_light_skin_tone": "1f473-1f3fc-200d-2640-fe0f", "woman_with_turban_medium_light_skin_tone": "1f473-1f3fc-200d-2640-fe0f", "woman-wearing-turban_medium_skin_tone": "1f473-1f3fd-200d-2640-fe0f", "woman_with_turban_medium_skin_tone": "1f473-1f3fd-200d-2640-fe0f", "woman-wearing-turban_medium_dark_skin_tone": "1f473-1f3fe-200d-2640-fe0f", "woman_with_turban_medium_dark_skin_tone": "1f473-1f3fe-200d-2640-fe0f", "woman-wearing-turban_dark_skin_tone": "1f473-1f3ff-200d-2640-fe0f", "woman_with_turban_dark_skin_tone": "1f473-1f3ff-200d-2640-fe0f", "man-wearing-turban_light_skin_tone": "1f473-1f3fb-200d-2642-fe0f", "man-wearing-turban_medium_light_skin_tone": "1f473-1f3fc-200d-2642-fe0f", "man-wearing-turban_medium_skin_tone": "1f473-1f3fd-200d-2642-fe0f", "man-wearing-turban_medium_dark_skin_tone": "1f473-1f3fe-200d-2642-fe0f", "man-wearing-turban_dark_skin_tone": "1f473-1f3ff-200d-2642-fe0f", "man_with_turban_light_skin_tone": "1f473-1f3fb", "man_with_turban_medium_light_skin_tone": "1f473-1f3fc", "man_with_turban_medium_skin_tone": "1f473-1f3fd", "man_with_turban_medium_dark_skin_tone": "1f473-1f3fe", "man_with_turban_dark_skin_tone": "1f473-1f3ff", "older_man_light_skin_tone": "1f474-1f3fb", "older_man_medium_light_skin_tone": "1f474-1f3fc", "older_man_medium_skin_tone": "1f474-1f3fd", "older_man_medium_dark_skin_tone": "1f474-1f3fe", "older_man_dark_skin_tone": "1f474-1f3ff", "older_woman_light_skin_tone": "1f475-1f3fb", "older_woman_medium_light_skin_tone": "1f475-1f3fc", "older_woman_medium_skin_tone": "1f475-1f3fd", "older_woman_medium_dark_skin_tone": "1f475-1f3fe", "older_woman_dark_skin_tone": "1f475-1f3ff", "baby_light_skin_tone": "1f476-1f3fb", "baby_medium_light_skin_tone": "1f476-1f3fc", "baby_medium_skin_tone": "1f476-1f3fd", "baby_medium_dark_skin_tone": "1f476-1f3fe", "baby_dark_skin_tone": "1f476-1f3ff", "female-construction-worker_light_skin_tone": "1f477-1f3fb-200d-2640-fe0f", "construction_worker_woman_light_skin_tone": "1f477-1f3fb-200d-2640-fe0f", "female-construction-worker_medium_light_skin_tone": "1f477-1f3fc-200d-2640-fe0f", "construction_worker_woman_medium_light_skin_tone": "1f477-1f3fc-200d-2640-fe0f", "female-construction-worker_medium_skin_tone": "1f477-1f3fd-200d-2640-fe0f", "construction_worker_woman_medium_skin_tone": "1f477-1f3fd-200d-2640-fe0f", "female-construction-worker_medium_dark_skin_tone": "1f477-1f3fe-200d-2640-fe0f", "construction_worker_woman_medium_dark_skin_tone": "1f477-1f3fe-200d-2640-fe0f", "female-construction-worker_dark_skin_tone": "1f477-1f3ff-200d-2640-fe0f", "construction_worker_woman_dark_skin_tone": "1f477-1f3ff-200d-2640-fe0f", "male-construction-worker_light_skin_tone": "1f477-1f3fb-200d-2642-fe0f", "construction_worker_man_light_skin_tone": "1f477-1f3fb-200d-2642-fe0f", "male-construction-worker_medium_light_skin_tone": "1f477-1f3fc-200d-2642-fe0f", "construction_worker_man_medium_light_skin_tone": "1f477-1f3fc-200d-2642-fe0f", "male-construction-worker_medium_skin_tone": "1f477-1f3fd-200d-2642-fe0f", "construction_worker_man_medium_skin_tone": "1f477-1f3fd-200d-2642-fe0f", "male-construction-worker_medium_dark_skin_tone": "1f477-1f3fe-200d-2642-fe0f", "construction_worker_man_medium_dark_skin_tone": "1f477-1f3fe-200d-2642-fe0f", "male-construction-worker_dark_skin_tone": "1f477-1f3ff-200d-2642-fe0f", "construction_worker_man_dark_skin_tone": "1f477-1f3ff-200d-2642-fe0f", "construction_worker_light_skin_tone": "1f477-1f3fb", "construction_worker_medium_light_skin_tone": "1f477-1f3fc", "construction_worker_medium_skin_tone": "1f477-1f3fd", "construction_worker_medium_dark_skin_tone": "1f477-1f3fe", "construction_worker_dark_skin_tone": "1f477-1f3ff", "princess_light_skin_tone": "1f478-1f3fb", "princess_medium_light_skin_tone": "1f478-1f3fc", "princess_medium_skin_tone": "1f478-1f3fd", "princess_medium_dark_skin_tone": "1f478-1f3fe", "princess_dark_skin_tone": "1f478-1f3ff", "angel_light_skin_tone": "1f47c-1f3fb", "angel_medium_light_skin_tone": "1f47c-1f3fc", "angel_medium_skin_tone": "1f47c-1f3fd", "angel_medium_dark_skin_tone": "1f47c-1f3fe", "angel_dark_skin_tone": "1f47c-1f3ff", "woman-tipping-hand_light_skin_tone": "1f481-1f3fb-200d-2640-fe0f", "tipping_hand_woman_light_skin_tone": "1f481-1f3fb-200d-2640-fe0f", "woman-tipping-hand_medium_light_skin_tone": "1f481-1f3fc-200d-2640-fe0f", "tipping_hand_woman_medium_light_skin_tone": "1f481-1f3fc-200d-2640-fe0f", "woman-tipping-hand_medium_skin_tone": "1f481-1f3fd-200d-2640-fe0f", "tipping_hand_woman_medium_skin_tone": "1f481-1f3fd-200d-2640-fe0f", "woman-tipping-hand_medium_dark_skin_tone": "1f481-1f3fe-200d-2640-fe0f", "tipping_hand_woman_medium_dark_skin_tone": "1f481-1f3fe-200d-2640-fe0f", "woman-tipping-hand_dark_skin_tone": "1f481-1f3ff-200d-2640-fe0f", "tipping_hand_woman_dark_skin_tone": "1f481-1f3ff-200d-2640-fe0f", "man-tipping-hand_light_skin_tone": "1f481-1f3fb-200d-2642-fe0f", "tipping_hand_man_light_skin_tone": "1f481-1f3fb-200d-2642-fe0f", "man-tipping-hand_medium_light_skin_tone": "1f481-1f3fc-200d-2642-fe0f", "tipping_hand_man_medium_light_skin_tone": "1f481-1f3fc-200d-2642-fe0f", "man-tipping-hand_medium_skin_tone": "1f481-1f3fd-200d-2642-fe0f", "tipping_hand_man_medium_skin_tone": "1f481-1f3fd-200d-2642-fe0f", "man-tipping-hand_medium_dark_skin_tone": "1f481-1f3fe-200d-2642-fe0f", "tipping_hand_man_medium_dark_skin_tone": "1f481-1f3fe-200d-2642-fe0f", "man-tipping-hand_dark_skin_tone": "1f481-1f3ff-200d-2642-fe0f", "tipping_hand_man_dark_skin_tone": "1f481-1f3ff-200d-2642-fe0f", "information_desk_person_light_skin_tone": "1f481-1f3fb", "information_desk_person_medium_light_skin_tone": "1f481-1f3fc", "information_desk_person_medium_skin_tone": "1f481-1f3fd", "information_desk_person_medium_dark_skin_tone": "1f481-1f3fe", "information_desk_person_dark_skin_tone": "1f481-1f3ff", "female-guard_light_skin_tone": "1f482-1f3fb-200d-2640-fe0f", "guardswoman_light_skin_tone": "1f482-1f3fb-200d-2640-fe0f", "female-guard_medium_light_skin_tone": "1f482-1f3fc-200d-2640-fe0f", "guardswoman_medium_light_skin_tone": "1f482-1f3fc-200d-2640-fe0f", "female-guard_medium_skin_tone": "1f482-1f3fd-200d-2640-fe0f", "guardswoman_medium_skin_tone": "1f482-1f3fd-200d-2640-fe0f", "female-guard_medium_dark_skin_tone": "1f482-1f3fe-200d-2640-fe0f", "guardswoman_medium_dark_skin_tone": "1f482-1f3fe-200d-2640-fe0f", "female-guard_dark_skin_tone": "1f482-1f3ff-200d-2640-fe0f", "guardswoman_dark_skin_tone": "1f482-1f3ff-200d-2640-fe0f", "male-guard_light_skin_tone": "1f482-1f3fb-200d-2642-fe0f", "male-guard_medium_light_skin_tone": "1f482-1f3fc-200d-2642-fe0f", "male-guard_medium_skin_tone": "1f482-1f3fd-200d-2642-fe0f", "male-guard_medium_dark_skin_tone": "1f482-1f3fe-200d-2642-fe0f", "male-guard_dark_skin_tone": "1f482-1f3ff-200d-2642-fe0f", "guardsman_light_skin_tone": "1f482-1f3fb", "guardsman_medium_light_skin_tone": "1f482-1f3fc", "guardsman_medium_skin_tone": "1f482-1f3fd", "guardsman_medium_dark_skin_tone": "1f482-1f3fe", "guardsman_dark_skin_tone": "1f482-1f3ff", "dancer_light_skin_tone": "1f483-1f3fb", "dancer_medium_light_skin_tone": "1f483-1f3fc", "dancer_medium_skin_tone": "1f483-1f3fd", "dancer_medium_dark_skin_tone": "1f483-1f3fe", "dancer_dark_skin_tone": "1f483-1f3ff", "nail_care_light_skin_tone": "1f485-1f3fb", "nail_care_medium_light_skin_tone": "1f485-1f3fc", "nail_care_medium_skin_tone": "1f485-1f3fd", "nail_care_medium_dark_skin_tone": "1f485-1f3fe", "nail_care_dark_skin_tone": "1f485-1f3ff", "woman-getting-massage_light_skin_tone": "1f486-1f3fb-200d-2640-fe0f", "massage_woman_light_skin_tone": "1f486-1f3fb-200d-2640-fe0f", "woman-getting-massage_medium_light_skin_tone": "1f486-1f3fc-200d-2640-fe0f", "massage_woman_medium_light_skin_tone": "1f486-1f3fc-200d-2640-fe0f", "woman-getting-massage_medium_skin_tone": "1f486-1f3fd-200d-2640-fe0f", "massage_woman_medium_skin_tone": "1f486-1f3fd-200d-2640-fe0f", "woman-getting-massage_medium_dark_skin_tone": "1f486-1f3fe-200d-2640-fe0f", "massage_woman_medium_dark_skin_tone": "1f486-1f3fe-200d-2640-fe0f", "woman-getting-massage_dark_skin_tone": "1f486-1f3ff-200d-2640-fe0f", "massage_woman_dark_skin_tone": "1f486-1f3ff-200d-2640-fe0f", "man-getting-massage_light_skin_tone": "1f486-1f3fb-200d-2642-fe0f", "massage_man_light_skin_tone": "1f486-1f3fb-200d-2642-fe0f", "man-getting-massage_medium_light_skin_tone": "1f486-1f3fc-200d-2642-fe0f", "massage_man_medium_light_skin_tone": "1f486-1f3fc-200d-2642-fe0f", "man-getting-massage_medium_skin_tone": "1f486-1f3fd-200d-2642-fe0f", "massage_man_medium_skin_tone": "1f486-1f3fd-200d-2642-fe0f", "man-getting-massage_medium_dark_skin_tone": "1f486-1f3fe-200d-2642-fe0f", "massage_man_medium_dark_skin_tone": "1f486-1f3fe-200d-2642-fe0f", "man-getting-massage_dark_skin_tone": "1f486-1f3ff-200d-2642-fe0f", "massage_man_dark_skin_tone": "1f486-1f3ff-200d-2642-fe0f", "massage_light_skin_tone": "1f486-1f3fb", "massage_medium_light_skin_tone": "1f486-1f3fc", "massage_medium_skin_tone": "1f486-1f3fd", "massage_medium_dark_skin_tone": "1f486-1f3fe", "massage_dark_skin_tone": "1f486-1f3ff", "woman-getting-haircut_light_skin_tone": "1f487-1f3fb-200d-2640-fe0f", "haircut_woman_light_skin_tone": "1f487-1f3fb-200d-2640-fe0f", "woman-getting-haircut_medium_light_skin_tone": "1f487-1f3fc-200d-2640-fe0f", "haircut_woman_medium_light_skin_tone": "1f487-1f3fc-200d-2640-fe0f", "woman-getting-haircut_medium_skin_tone": "1f487-1f3fd-200d-2640-fe0f", "haircut_woman_medium_skin_tone": "1f487-1f3fd-200d-2640-fe0f", "woman-getting-haircut_medium_dark_skin_tone": "1f487-1f3fe-200d-2640-fe0f", "haircut_woman_medium_dark_skin_tone": "1f487-1f3fe-200d-2640-fe0f", "woman-getting-haircut_dark_skin_tone": "1f487-1f3ff-200d-2640-fe0f", "haircut_woman_dark_skin_tone": "1f487-1f3ff-200d-2640-fe0f", "man-getting-haircut_light_skin_tone": "1f487-1f3fb-200d-2642-fe0f", "haircut_man_light_skin_tone": "1f487-1f3fb-200d-2642-fe0f", "man-getting-haircut_medium_light_skin_tone": "1f487-1f3fc-200d-2642-fe0f", "haircut_man_medium_light_skin_tone": "1f487-1f3fc-200d-2642-fe0f", "man-getting-haircut_medium_skin_tone": "1f487-1f3fd-200d-2642-fe0f", "haircut_man_medium_skin_tone": "1f487-1f3fd-200d-2642-fe0f", "man-getting-haircut_medium_dark_skin_tone": "1f487-1f3fe-200d-2642-fe0f", "haircut_man_medium_dark_skin_tone": "1f487-1f3fe-200d-2642-fe0f", "man-getting-haircut_dark_skin_tone": "1f487-1f3ff-200d-2642-fe0f", "haircut_man_dark_skin_tone": "1f487-1f3ff-200d-2642-fe0f", "haircut_light_skin_tone": "1f487-1f3fb", "haircut_medium_light_skin_tone": "1f487-1f3fc", "haircut_medium_skin_tone": "1f487-1f3fd", "haircut_medium_dark_skin_tone": "1f487-1f3fe", "haircut_dark_skin_tone": "1f487-1f3ff", "muscle_light_skin_tone": "1f4aa-1f3fb", "muscle_medium_light_skin_tone": "1f4aa-1f3fc", "muscle_medium_skin_tone": "1f4aa-1f3fd", "muscle_medium_dark_skin_tone": "1f4aa-1f3fe", "muscle_dark_skin_tone": "1f4aa-1f3ff", "man_in_business_suit_levitating_light_skin_tone": "1f574-1f3fb", "business_suit_levitating_light_skin_tone": "1f574-1f3fb", "man_in_business_suit_levitating_medium_light_skin_tone": "1f574-1f3fc", "business_suit_levitating_medium_light_skin_tone": "1f574-1f3fc", "man_in_business_suit_levitating_medium_skin_tone": "1f574-1f3fd", "business_suit_levitating_medium_skin_tone": "1f574-1f3fd", "man_in_business_suit_levitating_medium_dark_skin_tone": "1f574-1f3fe", "business_suit_levitating_medium_dark_skin_tone": "1f574-1f3fe", "man_in_business_suit_levitating_dark_skin_tone": "1f574-1f3ff", "business_suit_levitating_dark_skin_tone": "1f574-1f3ff", "female-detective_light_skin_tone": "1f575-1f3fb-200d-2640-fe0f", "female_detective_light_skin_tone": "1f575-1f3fb-200d-2640-fe0f", "female-detective_medium_light_skin_tone": "1f575-1f3fc-200d-2640-fe0f", "female_detective_medium_light_skin_tone": "1f575-1f3fc-200d-2640-fe0f", "female-detective_medium_skin_tone": "1f575-1f3fd-200d-2640-fe0f", "female_detective_medium_skin_tone": "1f575-1f3fd-200d-2640-fe0f", "female-detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2640-fe0f", "female_detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2640-fe0f", "female-detective_dark_skin_tone": "1f575-1f3ff-200d-2640-fe0f", "female_detective_dark_skin_tone": "1f575-1f3ff-200d-2640-fe0f", "male-detective_light_skin_tone": "1f575-1f3fb-200d-2642-fe0f", "male_detective_light_skin_tone": "1f575-1f3fb-200d-2642-fe0f", "male-detective_medium_light_skin_tone": "1f575-1f3fc-200d-2642-fe0f", "male_detective_medium_light_skin_tone": "1f575-1f3fc-200d-2642-fe0f", "male-detective_medium_skin_tone": "1f575-1f3fd-200d-2642-fe0f", "male_detective_medium_skin_tone": "1f575-1f3fd-200d-2642-fe0f", "male-detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2642-fe0f", "male_detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2642-fe0f", "male-detective_dark_skin_tone": "1f575-1f3ff-200d-2642-fe0f", "male_detective_dark_skin_tone": "1f575-1f3ff-200d-2642-fe0f", "sleuth_or_spy_light_skin_tone": "1f575-1f3fb", "sleuth_or_spy_medium_light_skin_tone": "1f575-1f3fc", "sleuth_or_spy_medium_skin_tone": "1f575-1f3fd", "sleuth_or_spy_medium_dark_skin_tone": "1f575-1f3fe", "sleuth_or_spy_dark_skin_tone": "1f575-1f3ff", "man_dancing_light_skin_tone": "1f57a-1f3fb", "man_dancing_medium_light_skin_tone": "1f57a-1f3fc", "man_dancing_medium_skin_tone": "1f57a-1f3fd", "man_dancing_medium_dark_skin_tone": "1f57a-1f3fe", "man_dancing_dark_skin_tone": "1f57a-1f3ff", "raised_hand_with_fingers_splayed_light_skin_tone": "1f590-1f3fb", "raised_hand_with_fingers_splayed_medium_light_skin_tone": "1f590-1f3fc", "raised_hand_with_fingers_splayed_medium_skin_tone": "1f590-1f3fd", "raised_hand_with_fingers_splayed_medium_dark_skin_tone": "1f590-1f3fe", "raised_hand_with_fingers_splayed_dark_skin_tone": "1f590-1f3ff", "middle_finger_light_skin_tone": "1f595-1f3fb", "reversed_hand_with_middle_finger_extended_light_skin_tone": "1f595-1f3fb", "middle_finger_medium_light_skin_tone": "1f595-1f3fc", "reversed_hand_with_middle_finger_extended_medium_light_skin_tone": "1f595-1f3fc", "middle_finger_medium_skin_tone": "1f595-1f3fd", "reversed_hand_with_middle_finger_extended_medium_skin_tone": "1f595-1f3fd", "middle_finger_medium_dark_skin_tone": "1f595-1f3fe", "reversed_hand_with_middle_finger_extended_medium_dark_skin_tone": "1f595-1f3fe", "middle_finger_dark_skin_tone": "1f595-1f3ff", "reversed_hand_with_middle_finger_extended_dark_skin_tone": "1f595-1f3ff", "spock-hand_light_skin_tone": "1f596-1f3fb", "vulcan_salute_light_skin_tone": "1f596-1f3fb", "spock-hand_medium_light_skin_tone": "1f596-1f3fc", "vulcan_salute_medium_light_skin_tone": "1f596-1f3fc", "spock-hand_medium_skin_tone": "1f596-1f3fd", "vulcan_salute_medium_skin_tone": "1f596-1f3fd", "spock-hand_medium_dark_skin_tone": "1f596-1f3fe", "vulcan_salute_medium_dark_skin_tone": "1f596-1f3fe", "spock-hand_dark_skin_tone": "1f596-1f3ff", "vulcan_salute_dark_skin_tone": "1f596-1f3ff", "woman-gesturing-no_light_skin_tone": "1f645-1f3fb-200d-2640-fe0f", "no_good_woman_light_skin_tone": "1f645-1f3fb-200d-2640-fe0f", "woman-gesturing-no_medium_light_skin_tone": "1f645-1f3fc-200d-2640-fe0f", "no_good_woman_medium_light_skin_tone": "1f645-1f3fc-200d-2640-fe0f", "woman-gesturing-no_medium_skin_tone": "1f645-1f3fd-200d-2640-fe0f", "no_good_woman_medium_skin_tone": "1f645-1f3fd-200d-2640-fe0f", "woman-gesturing-no_medium_dark_skin_tone": "1f645-1f3fe-200d-2640-fe0f", "no_good_woman_medium_dark_skin_tone": "1f645-1f3fe-200d-2640-fe0f", "woman-gesturing-no_dark_skin_tone": "1f645-1f3ff-200d-2640-fe0f", "no_good_woman_dark_skin_tone": "1f645-1f3ff-200d-2640-fe0f", "man-gesturing-no_light_skin_tone": "1f645-1f3fb-200d-2642-fe0f", "no_good_man_light_skin_tone": "1f645-1f3fb-200d-2642-fe0f", "man-gesturing-no_medium_light_skin_tone": "1f645-1f3fc-200d-2642-fe0f", "no_good_man_medium_light_skin_tone": "1f645-1f3fc-200d-2642-fe0f", "man-gesturing-no_medium_skin_tone": "1f645-1f3fd-200d-2642-fe0f", "no_good_man_medium_skin_tone": "1f645-1f3fd-200d-2642-fe0f", "man-gesturing-no_medium_dark_skin_tone": "1f645-1f3fe-200d-2642-fe0f", "no_good_man_medium_dark_skin_tone": "1f645-1f3fe-200d-2642-fe0f", "man-gesturing-no_dark_skin_tone": "1f645-1f3ff-200d-2642-fe0f", "no_good_man_dark_skin_tone": "1f645-1f3ff-200d-2642-fe0f", "no_good_light_skin_tone": "1f645-1f3fb", "no_good_medium_light_skin_tone": "1f645-1f3fc", "no_good_medium_skin_tone": "1f645-1f3fd", "no_good_medium_dark_skin_tone": "1f645-1f3fe", "no_good_dark_skin_tone": "1f645-1f3ff", "woman-gesturing-ok_light_skin_tone": "1f646-1f3fb-200d-2640-fe0f", "woman-gesturing-ok_medium_light_skin_tone": "1f646-1f3fc-200d-2640-fe0f", "woman-gesturing-ok_medium_skin_tone": "1f646-1f3fd-200d-2640-fe0f", "woman-gesturing-ok_medium_dark_skin_tone": "1f646-1f3fe-200d-2640-fe0f", "woman-gesturing-ok_dark_skin_tone": "1f646-1f3ff-200d-2640-fe0f", "man-gesturing-ok_light_skin_tone": "1f646-1f3fb-200d-2642-fe0f", "ok_man_light_skin_tone": "1f646-1f3fb-200d-2642-fe0f", "man-gesturing-ok_medium_light_skin_tone": "1f646-1f3fc-200d-2642-fe0f", "ok_man_medium_light_skin_tone": "1f646-1f3fc-200d-2642-fe0f", "man-gesturing-ok_medium_skin_tone": "1f646-1f3fd-200d-2642-fe0f", "ok_man_medium_skin_tone": "1f646-1f3fd-200d-2642-fe0f", "man-gesturing-ok_medium_dark_skin_tone": "1f646-1f3fe-200d-2642-fe0f", "ok_man_medium_dark_skin_tone": "1f646-1f3fe-200d-2642-fe0f", "man-gesturing-ok_dark_skin_tone": "1f646-1f3ff-200d-2642-fe0f", "ok_man_dark_skin_tone": "1f646-1f3ff-200d-2642-fe0f", "ok_woman_light_skin_tone": "1f646-1f3fb", "ok_woman_medium_light_skin_tone": "1f646-1f3fc", "ok_woman_medium_skin_tone": "1f646-1f3fd", "ok_woman_medium_dark_skin_tone": "1f646-1f3fe", "ok_woman_dark_skin_tone": "1f646-1f3ff", "woman-bowing_light_skin_tone": "1f647-1f3fb-200d-2640-fe0f", "bowing_woman_light_skin_tone": "1f647-1f3fb-200d-2640-fe0f", "woman-bowing_medium_light_skin_tone": "1f647-1f3fc-200d-2640-fe0f", "bowing_woman_medium_light_skin_tone": "1f647-1f3fc-200d-2640-fe0f", "woman-bowing_medium_skin_tone": "1f647-1f3fd-200d-2640-fe0f", "bowing_woman_medium_skin_tone": "1f647-1f3fd-200d-2640-fe0f", "woman-bowing_medium_dark_skin_tone": "1f647-1f3fe-200d-2640-fe0f", "bowing_woman_medium_dark_skin_tone": "1f647-1f3fe-200d-2640-fe0f", "woman-bowing_dark_skin_tone": "1f647-1f3ff-200d-2640-fe0f", "bowing_woman_dark_skin_tone": "1f647-1f3ff-200d-2640-fe0f", "man-bowing_light_skin_tone": "1f647-1f3fb-200d-2642-fe0f", "bowing_man_light_skin_tone": "1f647-1f3fb-200d-2642-fe0f", "man-bowing_medium_light_skin_tone": "1f647-1f3fc-200d-2642-fe0f", "bowing_man_medium_light_skin_tone": "1f647-1f3fc-200d-2642-fe0f", "man-bowing_medium_skin_tone": "1f647-1f3fd-200d-2642-fe0f", "bowing_man_medium_skin_tone": "1f647-1f3fd-200d-2642-fe0f", "man-bowing_medium_dark_skin_tone": "1f647-1f3fe-200d-2642-fe0f", "bowing_man_medium_dark_skin_tone": "1f647-1f3fe-200d-2642-fe0f", "man-bowing_dark_skin_tone": "1f647-1f3ff-200d-2642-fe0f", "bowing_man_dark_skin_tone": "1f647-1f3ff-200d-2642-fe0f", "bow_light_skin_tone": "1f647-1f3fb", "bow_medium_light_skin_tone": "1f647-1f3fc", "bow_medium_skin_tone": "1f647-1f3fd", "bow_medium_dark_skin_tone": "1f647-1f3fe", "bow_dark_skin_tone": "1f647-1f3ff", "woman-raising-hand_light_skin_tone": "1f64b-1f3fb-200d-2640-fe0f", "raising_hand_woman_light_skin_tone": "1f64b-1f3fb-200d-2640-fe0f", "woman-raising-hand_medium_light_skin_tone": "1f64b-1f3fc-200d-2640-fe0f", "raising_hand_woman_medium_light_skin_tone": "1f64b-1f3fc-200d-2640-fe0f", "woman-raising-hand_medium_skin_tone": "1f64b-1f3fd-200d-2640-fe0f", "raising_hand_woman_medium_skin_tone": "1f64b-1f3fd-200d-2640-fe0f", "woman-raising-hand_medium_dark_skin_tone": "1f64b-1f3fe-200d-2640-fe0f", "raising_hand_woman_medium_dark_skin_tone": "1f64b-1f3fe-200d-2640-fe0f", "woman-raising-hand_dark_skin_tone": "1f64b-1f3ff-200d-2640-fe0f", "raising_hand_woman_dark_skin_tone": "1f64b-1f3ff-200d-2640-fe0f", "man-raising-hand_light_skin_tone": "1f64b-1f3fb-200d-2642-fe0f", "raising_hand_man_light_skin_tone": "1f64b-1f3fb-200d-2642-fe0f", "man-raising-hand_medium_light_skin_tone": "1f64b-1f3fc-200d-2642-fe0f", "raising_hand_man_medium_light_skin_tone": "1f64b-1f3fc-200d-2642-fe0f", "man-raising-hand_medium_skin_tone": "1f64b-1f3fd-200d-2642-fe0f", "raising_hand_man_medium_skin_tone": "1f64b-1f3fd-200d-2642-fe0f", "man-raising-hand_medium_dark_skin_tone": "1f64b-1f3fe-200d-2642-fe0f", "raising_hand_man_medium_dark_skin_tone": "1f64b-1f3fe-200d-2642-fe0f", "man-raising-hand_dark_skin_tone": "1f64b-1f3ff-200d-2642-fe0f", "raising_hand_man_dark_skin_tone": "1f64b-1f3ff-200d-2642-fe0f", "raising_hand_light_skin_tone": "1f64b-1f3fb", "raising_hand_medium_light_skin_tone": "1f64b-1f3fc", "raising_hand_medium_skin_tone": "1f64b-1f3fd", "raising_hand_medium_dark_skin_tone": "1f64b-1f3fe", "raising_hand_dark_skin_tone": "1f64b-1f3ff", "raised_hands_light_skin_tone": "1f64c-1f3fb", "raised_hands_medium_light_skin_tone": "1f64c-1f3fc", "raised_hands_medium_skin_tone": "1f64c-1f3fd", "raised_hands_medium_dark_skin_tone": "1f64c-1f3fe", "raised_hands_dark_skin_tone": "1f64c-1f3ff", "woman-frowning_light_skin_tone": "1f64d-1f3fb-200d-2640-fe0f", "frowning_woman_light_skin_tone": "1f64d-1f3fb-200d-2640-fe0f", "woman-frowning_medium_light_skin_tone": "1f64d-1f3fc-200d-2640-fe0f", "frowning_woman_medium_light_skin_tone": "1f64d-1f3fc-200d-2640-fe0f", "woman-frowning_medium_skin_tone": "1f64d-1f3fd-200d-2640-fe0f", "frowning_woman_medium_skin_tone": "1f64d-1f3fd-200d-2640-fe0f", "woman-frowning_medium_dark_skin_tone": "1f64d-1f3fe-200d-2640-fe0f", "frowning_woman_medium_dark_skin_tone": "1f64d-1f3fe-200d-2640-fe0f", "woman-frowning_dark_skin_tone": "1f64d-1f3ff-200d-2640-fe0f", "frowning_woman_dark_skin_tone": "1f64d-1f3ff-200d-2640-fe0f", "man-frowning_light_skin_tone": "1f64d-1f3fb-200d-2642-fe0f", "frowning_man_light_skin_tone": "1f64d-1f3fb-200d-2642-fe0f", "man-frowning_medium_light_skin_tone": "1f64d-1f3fc-200d-2642-fe0f", "frowning_man_medium_light_skin_tone": "1f64d-1f3fc-200d-2642-fe0f", "man-frowning_medium_skin_tone": "1f64d-1f3fd-200d-2642-fe0f", "frowning_man_medium_skin_tone": "1f64d-1f3fd-200d-2642-fe0f", "man-frowning_medium_dark_skin_tone": "1f64d-1f3fe-200d-2642-fe0f", "frowning_man_medium_dark_skin_tone": "1f64d-1f3fe-200d-2642-fe0f", "man-frowning_dark_skin_tone": "1f64d-1f3ff-200d-2642-fe0f", "frowning_man_dark_skin_tone": "1f64d-1f3ff-200d-2642-fe0f", "person_frowning_light_skin_tone": "1f64d-1f3fb", "person_frowning_medium_light_skin_tone": "1f64d-1f3fc", "person_frowning_medium_skin_tone": "1f64d-1f3fd", "person_frowning_medium_dark_skin_tone": "1f64d-1f3fe", "person_frowning_dark_skin_tone": "1f64d-1f3ff", "woman-pouting_light_skin_tone": "1f64e-1f3fb-200d-2640-fe0f", "pouting_woman_light_skin_tone": "1f64e-1f3fb-200d-2640-fe0f", "woman-pouting_medium_light_skin_tone": "1f64e-1f3fc-200d-2640-fe0f", "pouting_woman_medium_light_skin_tone": "1f64e-1f3fc-200d-2640-fe0f", "woman-pouting_medium_skin_tone": "1f64e-1f3fd-200d-2640-fe0f", "pouting_woman_medium_skin_tone": "1f64e-1f3fd-200d-2640-fe0f", "woman-pouting_medium_dark_skin_tone": "1f64e-1f3fe-200d-2640-fe0f", "pouting_woman_medium_dark_skin_tone": "1f64e-1f3fe-200d-2640-fe0f", "woman-pouting_dark_skin_tone": "1f64e-1f3ff-200d-2640-fe0f", "pouting_woman_dark_skin_tone": "1f64e-1f3ff-200d-2640-fe0f", "man-pouting_light_skin_tone": "1f64e-1f3fb-200d-2642-fe0f", "pouting_man_light_skin_tone": "1f64e-1f3fb-200d-2642-fe0f", "man-pouting_medium_light_skin_tone": "1f64e-1f3fc-200d-2642-fe0f", "pouting_man_medium_light_skin_tone": "1f64e-1f3fc-200d-2642-fe0f", "man-pouting_medium_skin_tone": "1f64e-1f3fd-200d-2642-fe0f", "pouting_man_medium_skin_tone": "1f64e-1f3fd-200d-2642-fe0f", "man-pouting_medium_dark_skin_tone": "1f64e-1f3fe-200d-2642-fe0f", "pouting_man_medium_dark_skin_tone": "1f64e-1f3fe-200d-2642-fe0f", "man-pouting_dark_skin_tone": "1f64e-1f3ff-200d-2642-fe0f", "pouting_man_dark_skin_tone": "1f64e-1f3ff-200d-2642-fe0f", "person_with_pouting_face_light_skin_tone": "1f64e-1f3fb", "person_with_pouting_face_medium_light_skin_tone": "1f64e-1f3fc", "person_with_pouting_face_medium_skin_tone": "1f64e-1f3fd", "person_with_pouting_face_medium_dark_skin_tone": "1f64e-1f3fe", "person_with_pouting_face_dark_skin_tone": "1f64e-1f3ff", "pray_light_skin_tone": "1f64f-1f3fb", "pray_medium_light_skin_tone": "1f64f-1f3fc", "pray_medium_skin_tone": "1f64f-1f3fd", "pray_medium_dark_skin_tone": "1f64f-1f3fe", "pray_dark_skin_tone": "1f64f-1f3ff", "woman-rowing-boat_light_skin_tone": "1f6a3-1f3fb-200d-2640-fe0f", "rowing_woman_light_skin_tone": "1f6a3-1f3fb-200d-2640-fe0f", "woman-rowing-boat_medium_light_skin_tone": "1f6a3-1f3fc-200d-2640-fe0f", "rowing_woman_medium_light_skin_tone": "1f6a3-1f3fc-200d-2640-fe0f", "woman-rowing-boat_medium_skin_tone": "1f6a3-1f3fd-200d-2640-fe0f", "rowing_woman_medium_skin_tone": "1f6a3-1f3fd-200d-2640-fe0f", "woman-rowing-boat_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2640-fe0f", "rowing_woman_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2640-fe0f", "woman-rowing-boat_dark_skin_tone": "1f6a3-1f3ff-200d-2640-fe0f", "rowing_woman_dark_skin_tone": "1f6a3-1f3ff-200d-2640-fe0f", "man-rowing-boat_light_skin_tone": "1f6a3-1f3fb-200d-2642-fe0f", "rowing_man_light_skin_tone": "1f6a3-1f3fb-200d-2642-fe0f", "man-rowing-boat_medium_light_skin_tone": "1f6a3-1f3fc-200d-2642-fe0f", "rowing_man_medium_light_skin_tone": "1f6a3-1f3fc-200d-2642-fe0f", "man-rowing-boat_medium_skin_tone": "1f6a3-1f3fd-200d-2642-fe0f", "rowing_man_medium_skin_tone": "1f6a3-1f3fd-200d-2642-fe0f", "man-rowing-boat_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2642-fe0f", "rowing_man_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2642-fe0f", "man-rowing-boat_dark_skin_tone": "1f6a3-1f3ff-200d-2642-fe0f", "rowing_man_dark_skin_tone": "1f6a3-1f3ff-200d-2642-fe0f", "rowboat_light_skin_tone": "1f6a3-1f3fb", "rowboat_medium_light_skin_tone": "1f6a3-1f3fc", "rowboat_medium_skin_tone": "1f6a3-1f3fd", "rowboat_medium_dark_skin_tone": "1f6a3-1f3fe", "rowboat_dark_skin_tone": "1f6a3-1f3ff", "woman-biking_light_skin_tone": "1f6b4-1f3fb-200d-2640-fe0f", "biking_woman_light_skin_tone": "1f6b4-1f3fb-200d-2640-fe0f", "woman-biking_medium_light_skin_tone": "1f6b4-1f3fc-200d-2640-fe0f", "biking_woman_medium_light_skin_tone": "1f6b4-1f3fc-200d-2640-fe0f", "woman-biking_medium_skin_tone": "1f6b4-1f3fd-200d-2640-fe0f", "biking_woman_medium_skin_tone": "1f6b4-1f3fd-200d-2640-fe0f", "woman-biking_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2640-fe0f", "biking_woman_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2640-fe0f", "woman-biking_dark_skin_tone": "1f6b4-1f3ff-200d-2640-fe0f", "biking_woman_dark_skin_tone": "1f6b4-1f3ff-200d-2640-fe0f", "man-biking_light_skin_tone": "1f6b4-1f3fb-200d-2642-fe0f", "biking_man_light_skin_tone": "1f6b4-1f3fb-200d-2642-fe0f", "man-biking_medium_light_skin_tone": "1f6b4-1f3fc-200d-2642-fe0f", "biking_man_medium_light_skin_tone": "1f6b4-1f3fc-200d-2642-fe0f", "man-biking_medium_skin_tone": "1f6b4-1f3fd-200d-2642-fe0f", "biking_man_medium_skin_tone": "1f6b4-1f3fd-200d-2642-fe0f", "man-biking_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2642-fe0f", "biking_man_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2642-fe0f", "man-biking_dark_skin_tone": "1f6b4-1f3ff-200d-2642-fe0f", "biking_man_dark_skin_tone": "1f6b4-1f3ff-200d-2642-fe0f", "bicyclist_light_skin_tone": "1f6b4-1f3fb", "bicyclist_medium_light_skin_tone": "1f6b4-1f3fc", "bicyclist_medium_skin_tone": "1f6b4-1f3fd", "bicyclist_medium_dark_skin_tone": "1f6b4-1f3fe", "bicyclist_dark_skin_tone": "1f6b4-1f3ff", "woman-mountain-biking_light_skin_tone": "1f6b5-1f3fb-200d-2640-fe0f", "mountain_biking_woman_light_skin_tone": "1f6b5-1f3fb-200d-2640-fe0f", "woman-mountain-biking_medium_light_skin_tone": "1f6b5-1f3fc-200d-2640-fe0f", "mountain_biking_woman_medium_light_skin_tone": "1f6b5-1f3fc-200d-2640-fe0f", "woman-mountain-biking_medium_skin_tone": "1f6b5-1f3fd-200d-2640-fe0f", "mountain_biking_woman_medium_skin_tone": "1f6b5-1f3fd-200d-2640-fe0f", "woman-mountain-biking_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2640-fe0f", "mountain_biking_woman_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2640-fe0f", "woman-mountain-biking_dark_skin_tone": "1f6b5-1f3ff-200d-2640-fe0f", "mountain_biking_woman_dark_skin_tone": "1f6b5-1f3ff-200d-2640-fe0f", "man-mountain-biking_light_skin_tone": "1f6b5-1f3fb-200d-2642-fe0f", "mountain_biking_man_light_skin_tone": "1f6b5-1f3fb-200d-2642-fe0f", "man-mountain-biking_medium_light_skin_tone": "1f6b5-1f3fc-200d-2642-fe0f", "mountain_biking_man_medium_light_skin_tone": "1f6b5-1f3fc-200d-2642-fe0f", "man-mountain-biking_medium_skin_tone": "1f6b5-1f3fd-200d-2642-fe0f", "mountain_biking_man_medium_skin_tone": "1f6b5-1f3fd-200d-2642-fe0f", "man-mountain-biking_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2642-fe0f", "mountain_biking_man_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2642-fe0f", "man-mountain-biking_dark_skin_tone": "1f6b5-1f3ff-200d-2642-fe0f", "mountain_biking_man_dark_skin_tone": "1f6b5-1f3ff-200d-2642-fe0f", "mountain_bicyclist_light_skin_tone": "1f6b5-1f3fb", "mountain_bicyclist_medium_light_skin_tone": "1f6b5-1f3fc", "mountain_bicyclist_medium_skin_tone": "1f6b5-1f3fd", "mountain_bicyclist_medium_dark_skin_tone": "1f6b5-1f3fe", "mountain_bicyclist_dark_skin_tone": "1f6b5-1f3ff", "woman-walking_light_skin_tone": "1f6b6-1f3fb-200d-2640-fe0f", "walking_woman_light_skin_tone": "1f6b6-1f3fb-200d-2640-fe0f", "woman-walking_medium_light_skin_tone": "1f6b6-1f3fc-200d-2640-fe0f", "walking_woman_medium_light_skin_tone": "1f6b6-1f3fc-200d-2640-fe0f", "woman-walking_medium_skin_tone": "1f6b6-1f3fd-200d-2640-fe0f", "walking_woman_medium_skin_tone": "1f6b6-1f3fd-200d-2640-fe0f", "woman-walking_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2640-fe0f", "walking_woman_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2640-fe0f", "woman-walking_dark_skin_tone": "1f6b6-1f3ff-200d-2640-fe0f", "walking_woman_dark_skin_tone": "1f6b6-1f3ff-200d-2640-fe0f", "man-walking_light_skin_tone": "1f6b6-1f3fb-200d-2642-fe0f", "walking_man_light_skin_tone": "1f6b6-1f3fb-200d-2642-fe0f", "man-walking_medium_light_skin_tone": "1f6b6-1f3fc-200d-2642-fe0f", "walking_man_medium_light_skin_tone": "1f6b6-1f3fc-200d-2642-fe0f", "man-walking_medium_skin_tone": "1f6b6-1f3fd-200d-2642-fe0f", "walking_man_medium_skin_tone": "1f6b6-1f3fd-200d-2642-fe0f", "man-walking_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2642-fe0f", "walking_man_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2642-fe0f", "man-walking_dark_skin_tone": "1f6b6-1f3ff-200d-2642-fe0f", "walking_man_dark_skin_tone": "1f6b6-1f3ff-200d-2642-fe0f", "walking_light_skin_tone": "1f6b6-1f3fb", "walking_medium_light_skin_tone": "1f6b6-1f3fc", "walking_medium_skin_tone": "1f6b6-1f3fd", "walking_medium_dark_skin_tone": "1f6b6-1f3fe", "walking_dark_skin_tone": "1f6b6-1f3ff", "bath_light_skin_tone": "1f6c0-1f3fb", "bath_medium_light_skin_tone": "1f6c0-1f3fc", "bath_medium_skin_tone": "1f6c0-1f3fd", "bath_medium_dark_skin_tone": "1f6c0-1f3fe", "bath_dark_skin_tone": "1f6c0-1f3ff", "sleeping_accommodation_light_skin_tone": "1f6cc-1f3fb", "sleeping_accommodation_medium_light_skin_tone": "1f6cc-1f3fc", "sleeping_accommodation_medium_skin_tone": "1f6cc-1f3fd", "sleeping_accommodation_medium_dark_skin_tone": "1f6cc-1f3fe", "sleeping_accommodation_dark_skin_tone": "1f6cc-1f3ff", "pinched_fingers_light_skin_tone": "1f90c-1f3fb", "pinched_fingers_medium_light_skin_tone": "1f90c-1f3fc", "pinched_fingers_medium_skin_tone": "1f90c-1f3fd", "pinched_fingers_medium_dark_skin_tone": "1f90c-1f3fe", "pinched_fingers_dark_skin_tone": "1f90c-1f3ff", "pinching_hand_light_skin_tone": "1f90f-1f3fb", "pinching_hand_medium_light_skin_tone": "1f90f-1f3fc", "pinching_hand_medium_skin_tone": "1f90f-1f3fd", "pinching_hand_medium_dark_skin_tone": "1f90f-1f3fe", "pinching_hand_dark_skin_tone": "1f90f-1f3ff", "the_horns_light_skin_tone": "1f918-1f3fb", "sign_of_the_horns_light_skin_tone": "1f918-1f3fb", "metal_light_skin_tone": "1f918-1f3fb", "the_horns_medium_light_skin_tone": "1f918-1f3fc", "sign_of_the_horns_medium_light_skin_tone": "1f918-1f3fc", "metal_medium_light_skin_tone": "1f918-1f3fc", "the_horns_medium_skin_tone": "1f918-1f3fd", "sign_of_the_horns_medium_skin_tone": "1f918-1f3fd", "metal_medium_skin_tone": "1f918-1f3fd", "the_horns_medium_dark_skin_tone": "1f918-1f3fe", "sign_of_the_horns_medium_dark_skin_tone": "1f918-1f3fe", "metal_medium_dark_skin_tone": "1f918-1f3fe", "the_horns_dark_skin_tone": "1f918-1f3ff", "sign_of_the_horns_dark_skin_tone": "1f918-1f3ff", "metal_dark_skin_tone": "1f918-1f3ff", "call_me_hand_light_skin_tone": "1f919-1f3fb", "call_me_hand_medium_light_skin_tone": "1f919-1f3fc", "call_me_hand_medium_skin_tone": "1f919-1f3fd", "call_me_hand_medium_dark_skin_tone": "1f919-1f3fe", "call_me_hand_dark_skin_tone": "1f919-1f3ff", "raised_back_of_hand_light_skin_tone": "1f91a-1f3fb", "raised_back_of_hand_medium_light_skin_tone": "1f91a-1f3fc", "raised_back_of_hand_medium_skin_tone": "1f91a-1f3fd", "raised_back_of_hand_medium_dark_skin_tone": "1f91a-1f3fe", "raised_back_of_hand_dark_skin_tone": "1f91a-1f3ff", "left-facing_fist_light_skin_tone": "1f91b-1f3fb", "fist_left_light_skin_tone": "1f91b-1f3fb", "left-facing_fist_medium_light_skin_tone": "1f91b-1f3fc", "fist_left_medium_light_skin_tone": "1f91b-1f3fc", "left-facing_fist_medium_skin_tone": "1f91b-1f3fd", "fist_left_medium_skin_tone": "1f91b-1f3fd", "left-facing_fist_medium_dark_skin_tone": "1f91b-1f3fe", "fist_left_medium_dark_skin_tone": "1f91b-1f3fe", "left-facing_fist_dark_skin_tone": "1f91b-1f3ff", "fist_left_dark_skin_tone": "1f91b-1f3ff", "right-facing_fist_light_skin_tone": "1f91c-1f3fb", "fist_right_light_skin_tone": "1f91c-1f3fb", "right-facing_fist_medium_light_skin_tone": "1f91c-1f3fc", "fist_right_medium_light_skin_tone": "1f91c-1f3fc", "right-facing_fist_medium_skin_tone": "1f91c-1f3fd", "fist_right_medium_skin_tone": "1f91c-1f3fd", "right-facing_fist_medium_dark_skin_tone": "1f91c-1f3fe", "fist_right_medium_dark_skin_tone": "1f91c-1f3fe", "right-facing_fist_dark_skin_tone": "1f91c-1f3ff", "fist_right_dark_skin_tone": "1f91c-1f3ff", "crossed_fingers_light_skin_tone": "1f91e-1f3fb", "hand_with_index_and_middle_fingers_crossed_light_skin_tone": "1f91e-1f3fb", "crossed_fingers_medium_light_skin_tone": "1f91e-1f3fc", "hand_with_index_and_middle_fingers_crossed_medium_light_skin_tone": "1f91e-1f3fc", "crossed_fingers_medium_skin_tone": "1f91e-1f3fd", "hand_with_index_and_middle_fingers_crossed_medium_skin_tone": "1f91e-1f3fd", "crossed_fingers_medium_dark_skin_tone": "1f91e-1f3fe", "hand_with_index_and_middle_fingers_crossed_medium_dark_skin_tone": "1f91e-1f3fe", "crossed_fingers_dark_skin_tone": "1f91e-1f3ff", "hand_with_index_and_middle_fingers_crossed_dark_skin_tone": "1f91e-1f3ff", "i_love_you_hand_sign_light_skin_tone": "1f91f-1f3fb", "i_love_you_hand_sign_medium_light_skin_tone": "1f91f-1f3fc", "i_love_you_hand_sign_medium_skin_tone": "1f91f-1f3fd", "i_love_you_hand_sign_medium_dark_skin_tone": "1f91f-1f3fe", "i_love_you_hand_sign_dark_skin_tone": "1f91f-1f3ff", "woman-facepalming_light_skin_tone": "1f926-1f3fb-200d-2640-fe0f", "woman_facepalming_light_skin_tone": "1f926-1f3fb-200d-2640-fe0f", "woman-facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2640-fe0f", "woman_facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2640-fe0f", "woman-facepalming_medium_skin_tone": "1f926-1f3fd-200d-2640-fe0f", "woman_facepalming_medium_skin_tone": "1f926-1f3fd-200d-2640-fe0f", "woman-facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2640-fe0f", "woman_facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2640-fe0f", "woman-facepalming_dark_skin_tone": "1f926-1f3ff-200d-2640-fe0f", "woman_facepalming_dark_skin_tone": "1f926-1f3ff-200d-2640-fe0f", "man-facepalming_light_skin_tone": "1f926-1f3fb-200d-2642-fe0f", "man_facepalming_light_skin_tone": "1f926-1f3fb-200d-2642-fe0f", "man-facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2642-fe0f", "man_facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2642-fe0f", "man-facepalming_medium_skin_tone": "1f926-1f3fd-200d-2642-fe0f", "man_facepalming_medium_skin_tone": "1f926-1f3fd-200d-2642-fe0f", "man-facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2642-fe0f", "man_facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2642-fe0f", "man-facepalming_dark_skin_tone": "1f926-1f3ff-200d-2642-fe0f", "man_facepalming_dark_skin_tone": "1f926-1f3ff-200d-2642-fe0f", "face_palm_light_skin_tone": "1f926-1f3fb", "face_palm_medium_light_skin_tone": "1f926-1f3fc", "face_palm_medium_skin_tone": "1f926-1f3fd", "face_palm_medium_dark_skin_tone": "1f926-1f3fe", "face_palm_dark_skin_tone": "1f926-1f3ff", "pregnant_woman_light_skin_tone": "1f930-1f3fb", "pregnant_woman_medium_light_skin_tone": "1f930-1f3fc", "pregnant_woman_medium_skin_tone": "1f930-1f3fd", "pregnant_woman_medium_dark_skin_tone": "1f930-1f3fe", "pregnant_woman_dark_skin_tone": "1f930-1f3ff", "breast-feeding_light_skin_tone": "1f931-1f3fb", "breast-feeding_medium_light_skin_tone": "1f931-1f3fc", "breast-feeding_medium_skin_tone": "1f931-1f3fd", "breast-feeding_medium_dark_skin_tone": "1f931-1f3fe", "breast-feeding_dark_skin_tone": "1f931-1f3ff", "palms_up_together_light_skin_tone": "1f932-1f3fb", "palms_up_together_medium_light_skin_tone": "1f932-1f3fc", "palms_up_together_medium_skin_tone": "1f932-1f3fd", "palms_up_together_medium_dark_skin_tone": "1f932-1f3fe", "palms_up_together_dark_skin_tone": "1f932-1f3ff", "selfie_light_skin_tone": "1f933-1f3fb", "selfie_medium_light_skin_tone": "1f933-1f3fc", "selfie_medium_skin_tone": "1f933-1f3fd", "selfie_medium_dark_skin_tone": "1f933-1f3fe", "selfie_dark_skin_tone": "1f933-1f3ff", "prince_light_skin_tone": "1f934-1f3fb", "prince_medium_light_skin_tone": "1f934-1f3fc", "prince_medium_skin_tone": "1f934-1f3fd", "prince_medium_dark_skin_tone": "1f934-1f3fe", "prince_dark_skin_tone": "1f934-1f3ff", "woman_in_tuxedo_light_skin_tone": "1f935-1f3fb-200d-2640-fe0f", "woman_in_tuxedo_medium_light_skin_tone": "1f935-1f3fc-200d-2640-fe0f", "woman_in_tuxedo_medium_skin_tone": "1f935-1f3fd-200d-2640-fe0f", "woman_in_tuxedo_medium_dark_skin_tone": "1f935-1f3fe-200d-2640-fe0f", "woman_in_tuxedo_dark_skin_tone": "1f935-1f3ff-200d-2640-fe0f", "man_in_tuxedo_light_skin_tone": "1f935-1f3fb-200d-2642-fe0f", "man_in_tuxedo_medium_light_skin_tone": "1f935-1f3fc-200d-2642-fe0f", "man_in_tuxedo_medium_skin_tone": "1f935-1f3fd-200d-2642-fe0f", "man_in_tuxedo_medium_dark_skin_tone": "1f935-1f3fe-200d-2642-fe0f", "man_in_tuxedo_dark_skin_tone": "1f935-1f3ff-200d-2642-fe0f", "person_in_tuxedo_light_skin_tone": "1f935-1f3fb", "person_in_tuxedo_medium_light_skin_tone": "1f935-1f3fc", "person_in_tuxedo_medium_skin_tone": "1f935-1f3fd", "person_in_tuxedo_medium_dark_skin_tone": "1f935-1f3fe", "person_in_tuxedo_dark_skin_tone": "1f935-1f3ff", "mrs_claus_light_skin_tone": "1f936-1f3fb", "mother_christmas_light_skin_tone": "1f936-1f3fb", "mrs_claus_medium_light_skin_tone": "1f936-1f3fc", "mother_christmas_medium_light_skin_tone": "1f936-1f3fc", "mrs_claus_medium_skin_tone": "1f936-1f3fd", "mother_christmas_medium_skin_tone": "1f936-1f3fd", "mrs_claus_medium_dark_skin_tone": "1f936-1f3fe", "mother_christmas_medium_dark_skin_tone": "1f936-1f3fe", "mrs_claus_dark_skin_tone": "1f936-1f3ff", "mother_christmas_dark_skin_tone": "1f936-1f3ff", "woman-shrugging_light_skin_tone": "1f937-1f3fb-200d-2640-fe0f", "woman_shrugging_light_skin_tone": "1f937-1f3fb-200d-2640-fe0f", "woman-shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2640-fe0f", "woman_shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2640-fe0f", "woman-shrugging_medium_skin_tone": "1f937-1f3fd-200d-2640-fe0f", "woman_shrugging_medium_skin_tone": "1f937-1f3fd-200d-2640-fe0f", "woman-shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2640-fe0f", "woman_shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2640-fe0f", "woman-shrugging_dark_skin_tone": "1f937-1f3ff-200d-2640-fe0f", "woman_shrugging_dark_skin_tone": "1f937-1f3ff-200d-2640-fe0f", "man-shrugging_light_skin_tone": "1f937-1f3fb-200d-2642-fe0f", "man_shrugging_light_skin_tone": "1f937-1f3fb-200d-2642-fe0f", "man-shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2642-fe0f", "man_shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2642-fe0f", "man-shrugging_medium_skin_tone": "1f937-1f3fd-200d-2642-fe0f", "man_shrugging_medium_skin_tone": "1f937-1f3fd-200d-2642-fe0f", "man-shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2642-fe0f", "man_shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2642-fe0f", "man-shrugging_dark_skin_tone": "1f937-1f3ff-200d-2642-fe0f", "man_shrugging_dark_skin_tone": "1f937-1f3ff-200d-2642-fe0f", "shrug_light_skin_tone": "1f937-1f3fb", "shrug_medium_light_skin_tone": "1f937-1f3fc", "shrug_medium_skin_tone": "1f937-1f3fd", "shrug_medium_dark_skin_tone": "1f937-1f3fe", "shrug_dark_skin_tone": "1f937-1f3ff", "woman-cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2640-fe0f", "woman_cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2640-fe0f", "woman-cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2640-fe0f", "woman_cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2640-fe0f", "woman-cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2640-fe0f", "woman_cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2640-fe0f", "woman-cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2640-fe0f", "woman_cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2640-fe0f", "woman-cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2640-fe0f", "woman_cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2640-fe0f", "man-cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2642-fe0f", "man_cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2642-fe0f", "man-cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2642-fe0f", "man_cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2642-fe0f", "man-cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2642-fe0f", "man_cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2642-fe0f", "man-cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2642-fe0f", "man_cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2642-fe0f", "man-cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2642-fe0f", "man_cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2642-fe0f", "person_doing_cartwheel_light_skin_tone": "1f938-1f3fb", "person_doing_cartwheel_medium_light_skin_tone": "1f938-1f3fc", "person_doing_cartwheel_medium_skin_tone": "1f938-1f3fd", "person_doing_cartwheel_medium_dark_skin_tone": "1f938-1f3fe", "person_doing_cartwheel_dark_skin_tone": "1f938-1f3ff", "woman-juggling_light_skin_tone": "1f939-1f3fb-200d-2640-fe0f", "woman_juggling_light_skin_tone": "1f939-1f3fb-200d-2640-fe0f", "woman-juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2640-fe0f", "woman_juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2640-fe0f", "woman-juggling_medium_skin_tone": "1f939-1f3fd-200d-2640-fe0f", "woman_juggling_medium_skin_tone": "1f939-1f3fd-200d-2640-fe0f", "woman-juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2640-fe0f", "woman_juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2640-fe0f", "woman-juggling_dark_skin_tone": "1f939-1f3ff-200d-2640-fe0f", "woman_juggling_dark_skin_tone": "1f939-1f3ff-200d-2640-fe0f", "man-juggling_light_skin_tone": "1f939-1f3fb-200d-2642-fe0f", "man_juggling_light_skin_tone": "1f939-1f3fb-200d-2642-fe0f", "man-juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2642-fe0f", "man_juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2642-fe0f", "man-juggling_medium_skin_tone": "1f939-1f3fd-200d-2642-fe0f", "man_juggling_medium_skin_tone": "1f939-1f3fd-200d-2642-fe0f", "man-juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2642-fe0f", "man_juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2642-fe0f", "man-juggling_dark_skin_tone": "1f939-1f3ff-200d-2642-fe0f", "man_juggling_dark_skin_tone": "1f939-1f3ff-200d-2642-fe0f", "juggling_light_skin_tone": "1f939-1f3fb", "juggling_medium_light_skin_tone": "1f939-1f3fc", "juggling_medium_skin_tone": "1f939-1f3fd", "juggling_medium_dark_skin_tone": "1f939-1f3fe", "juggling_dark_skin_tone": "1f939-1f3ff", "woman-playing-water-polo_light_skin_tone": "1f93d-1f3fb-200d-2640-fe0f", "woman_playing_water_polo_light_skin_tone": "1f93d-1f3fb-200d-2640-fe0f", "woman-playing-water-polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2640-fe0f", "woman_playing_water_polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2640-fe0f", "woman-playing-water-polo_medium_skin_tone": "1f93d-1f3fd-200d-2640-fe0f", "woman_playing_water_polo_medium_skin_tone": "1f93d-1f3fd-200d-2640-fe0f", "woman-playing-water-polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2640-fe0f", "woman_playing_water_polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2640-fe0f", "woman-playing-water-polo_dark_skin_tone": "1f93d-1f3ff-200d-2640-fe0f", "woman_playing_water_polo_dark_skin_tone": "1f93d-1f3ff-200d-2640-fe0f", "man-playing-water-polo_light_skin_tone": "1f93d-1f3fb-200d-2642-fe0f", "man_playing_water_polo_light_skin_tone": "1f93d-1f3fb-200d-2642-fe0f", "man-playing-water-polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2642-fe0f", "man_playing_water_polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2642-fe0f", "man-playing-water-polo_medium_skin_tone": "1f93d-1f3fd-200d-2642-fe0f", "man_playing_water_polo_medium_skin_tone": "1f93d-1f3fd-200d-2642-fe0f", "man-playing-water-polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2642-fe0f", "man_playing_water_polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2642-fe0f", "man-playing-water-polo_dark_skin_tone": "1f93d-1f3ff-200d-2642-fe0f", "man_playing_water_polo_dark_skin_tone": "1f93d-1f3ff-200d-2642-fe0f", "water_polo_light_skin_tone": "1f93d-1f3fb", "water_polo_medium_light_skin_tone": "1f93d-1f3fc", "water_polo_medium_skin_tone": "1f93d-1f3fd", "water_polo_medium_dark_skin_tone": "1f93d-1f3fe", "water_polo_dark_skin_tone": "1f93d-1f3ff", "woman-playing-handball_light_skin_tone": "1f93e-1f3fb-200d-2640-fe0f", "woman_playing_handball_light_skin_tone": "1f93e-1f3fb-200d-2640-fe0f", "woman-playing-handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2640-fe0f", "woman_playing_handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2640-fe0f", "woman-playing-handball_medium_skin_tone": "1f93e-1f3fd-200d-2640-fe0f", "woman_playing_handball_medium_skin_tone": "1f93e-1f3fd-200d-2640-fe0f", "woman-playing-handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2640-fe0f", "woman_playing_handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2640-fe0f", "woman-playing-handball_dark_skin_tone": "1f93e-1f3ff-200d-2640-fe0f", "woman_playing_handball_dark_skin_tone": "1f93e-1f3ff-200d-2640-fe0f", "man-playing-handball_light_skin_tone": "1f93e-1f3fb-200d-2642-fe0f", "man_playing_handball_light_skin_tone": "1f93e-1f3fb-200d-2642-fe0f", "man-playing-handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2642-fe0f", "man_playing_handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2642-fe0f", "man-playing-handball_medium_skin_tone": "1f93e-1f3fd-200d-2642-fe0f", "man_playing_handball_medium_skin_tone": "1f93e-1f3fd-200d-2642-fe0f", "man-playing-handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2642-fe0f", "man_playing_handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2642-fe0f", "man-playing-handball_dark_skin_tone": "1f93e-1f3ff-200d-2642-fe0f", "man_playing_handball_dark_skin_tone": "1f93e-1f3ff-200d-2642-fe0f", "handball_light_skin_tone": "1f93e-1f3fb", "handball_medium_light_skin_tone": "1f93e-1f3fc", "handball_medium_skin_tone": "1f93e-1f3fd", "handball_medium_dark_skin_tone": "1f93e-1f3fe", "handball_dark_skin_tone": "1f93e-1f3ff", "ninja_light_skin_tone": "1f977-1f3fb", "ninja_medium_light_skin_tone": "1f977-1f3fc", "ninja_medium_skin_tone": "1f977-1f3fd", "ninja_medium_dark_skin_tone": "1f977-1f3fe", "ninja_dark_skin_tone": "1f977-1f3ff", "leg_light_skin_tone": "1f9b5-1f3fb", "leg_medium_light_skin_tone": "1f9b5-1f3fc", "leg_medium_skin_tone": "1f9b5-1f3fd", "leg_medium_dark_skin_tone": "1f9b5-1f3fe", "leg_dark_skin_tone": "1f9b5-1f3ff", "foot_light_skin_tone": "1f9b6-1f3fb", "foot_medium_light_skin_tone": "1f9b6-1f3fc", "foot_medium_skin_tone": "1f9b6-1f3fd", "foot_medium_dark_skin_tone": "1f9b6-1f3fe", "foot_dark_skin_tone": "1f9b6-1f3ff", "female_superhero_light_skin_tone": "1f9b8-1f3fb-200d-2640-fe0f", "female_superhero_medium_light_skin_tone": "1f9b8-1f3fc-200d-2640-fe0f", "female_superhero_medium_skin_tone": "1f9b8-1f3fd-200d-2640-fe0f", "female_superhero_medium_dark_skin_tone": "1f9b8-1f3fe-200d-2640-fe0f", "female_superhero_dark_skin_tone": "1f9b8-1f3ff-200d-2640-fe0f", "male_superhero_light_skin_tone": "1f9b8-1f3fb-200d-2642-fe0f", "male_superhero_medium_light_skin_tone": "1f9b8-1f3fc-200d-2642-fe0f", "male_superhero_medium_skin_tone": "1f9b8-1f3fd-200d-2642-fe0f", "male_superhero_medium_dark_skin_tone": "1f9b8-1f3fe-200d-2642-fe0f", "male_superhero_dark_skin_tone": "1f9b8-1f3ff-200d-2642-fe0f", "superhero_light_skin_tone": "1f9b8-1f3fb", "superhero_medium_light_skin_tone": "1f9b8-1f3fc", "superhero_medium_skin_tone": "1f9b8-1f3fd", "superhero_medium_dark_skin_tone": "1f9b8-1f3fe", "superhero_dark_skin_tone": "1f9b8-1f3ff", "female_supervillain_light_skin_tone": "1f9b9-1f3fb-200d-2640-fe0f", "female_supervillain_medium_light_skin_tone": "1f9b9-1f3fc-200d-2640-fe0f", "female_supervillain_medium_skin_tone": "1f9b9-1f3fd-200d-2640-fe0f", "female_supervillain_medium_dark_skin_tone": "1f9b9-1f3fe-200d-2640-fe0f", "female_supervillain_dark_skin_tone": "1f9b9-1f3ff-200d-2640-fe0f", "male_supervillain_light_skin_tone": "1f9b9-1f3fb-200d-2642-fe0f", "male_supervillain_medium_light_skin_tone": "1f9b9-1f3fc-200d-2642-fe0f", "male_supervillain_medium_skin_tone": "1f9b9-1f3fd-200d-2642-fe0f", "male_supervillain_medium_dark_skin_tone": "1f9b9-1f3fe-200d-2642-fe0f", "male_supervillain_dark_skin_tone": "1f9b9-1f3ff-200d-2642-fe0f", "supervillain_light_skin_tone": "1f9b9-1f3fb", "supervillain_medium_light_skin_tone": "1f9b9-1f3fc", "supervillain_medium_skin_tone": "1f9b9-1f3fd", "supervillain_medium_dark_skin_tone": "1f9b9-1f3fe", "supervillain_dark_skin_tone": "1f9b9-1f3ff", "ear_with_hearing_aid_light_skin_tone": "1f9bb-1f3fb", "ear_with_hearing_aid_medium_light_skin_tone": "1f9bb-1f3fc", "ear_with_hearing_aid_medium_skin_tone": "1f9bb-1f3fd", "ear_with_hearing_aid_medium_dark_skin_tone": "1f9bb-1f3fe", "ear_with_hearing_aid_dark_skin_tone": "1f9bb-1f3ff", "woman_standing_light_skin_tone": "1f9cd-1f3fb-200d-2640-fe0f", "woman_standing_medium_light_skin_tone": "1f9cd-1f3fc-200d-2640-fe0f", "woman_standing_medium_skin_tone": "1f9cd-1f3fd-200d-2640-fe0f", "woman_standing_medium_dark_skin_tone": "1f9cd-1f3fe-200d-2640-fe0f", "woman_standing_dark_skin_tone": "1f9cd-1f3ff-200d-2640-fe0f", "man_standing_light_skin_tone": "1f9cd-1f3fb-200d-2642-fe0f", "man_standing_medium_light_skin_tone": "1f9cd-1f3fc-200d-2642-fe0f", "man_standing_medium_skin_tone": "1f9cd-1f3fd-200d-2642-fe0f", "man_standing_medium_dark_skin_tone": "1f9cd-1f3fe-200d-2642-fe0f", "man_standing_dark_skin_tone": "1f9cd-1f3ff-200d-2642-fe0f", "standing_person_light_skin_tone": "1f9cd-1f3fb", "standing_person_medium_light_skin_tone": "1f9cd-1f3fc", "standing_person_medium_skin_tone": "1f9cd-1f3fd", "standing_person_medium_dark_skin_tone": "1f9cd-1f3fe", "standing_person_dark_skin_tone": "1f9cd-1f3ff", "woman_kneeling_light_skin_tone": "1f9ce-1f3fb-200d-2640-fe0f", "woman_kneeling_medium_light_skin_tone": "1f9ce-1f3fc-200d-2640-fe0f", "woman_kneeling_medium_skin_tone": "1f9ce-1f3fd-200d-2640-fe0f", "woman_kneeling_medium_dark_skin_tone": "1f9ce-1f3fe-200d-2640-fe0f", "woman_kneeling_dark_skin_tone": "1f9ce-1f3ff-200d-2640-fe0f", "man_kneeling_light_skin_tone": "1f9ce-1f3fb-200d-2642-fe0f", "man_kneeling_medium_light_skin_tone": "1f9ce-1f3fc-200d-2642-fe0f", "man_kneeling_medium_skin_tone": "1f9ce-1f3fd-200d-2642-fe0f", "man_kneeling_medium_dark_skin_tone": "1f9ce-1f3fe-200d-2642-fe0f", "man_kneeling_dark_skin_tone": "1f9ce-1f3ff-200d-2642-fe0f", "kneeling_person_light_skin_tone": "1f9ce-1f3fb", "kneeling_person_medium_light_skin_tone": "1f9ce-1f3fc", "kneeling_person_medium_skin_tone": "1f9ce-1f3fd", "kneeling_person_medium_dark_skin_tone": "1f9ce-1f3fe", "kneeling_person_dark_skin_tone": "1f9ce-1f3ff", "deaf_woman_light_skin_tone": "1f9cf-1f3fb-200d-2640-fe0f", "deaf_woman_medium_light_skin_tone": "1f9cf-1f3fc-200d-2640-fe0f", "deaf_woman_medium_skin_tone": "1f9cf-1f3fd-200d-2640-fe0f", "deaf_woman_medium_dark_skin_tone": "1f9cf-1f3fe-200d-2640-fe0f", "deaf_woman_dark_skin_tone": "1f9cf-1f3ff-200d-2640-fe0f", "deaf_man_light_skin_tone": "1f9cf-1f3fb-200d-2642-fe0f", "deaf_man_medium_light_skin_tone": "1f9cf-1f3fc-200d-2642-fe0f", "deaf_man_medium_skin_tone": "1f9cf-1f3fd-200d-2642-fe0f", "deaf_man_medium_dark_skin_tone": "1f9cf-1f3fe-200d-2642-fe0f", "deaf_man_dark_skin_tone": "1f9cf-1f3ff-200d-2642-fe0f", "deaf_person_light_skin_tone": "1f9cf-1f3fb", "deaf_person_medium_light_skin_tone": "1f9cf-1f3fc", "deaf_person_medium_skin_tone": "1f9cf-1f3fd", "deaf_person_medium_dark_skin_tone": "1f9cf-1f3fe", "deaf_person_dark_skin_tone": "1f9cf-1f3ff", "farmer_light_skin_tone": "1f9d1-1f3fb-200d-1f33e", "farmer_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f33e", "farmer_medium_skin_tone": "1f9d1-1f3fd-200d-1f33e", "farmer_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f33e", "farmer_dark_skin_tone": "1f9d1-1f3ff-200d-1f33e", "cook_light_skin_tone": "1f9d1-1f3fb-200d-1f373", "cook_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f373", "cook_medium_skin_tone": "1f9d1-1f3fd-200d-1f373", "cook_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f373", "cook_dark_skin_tone": "1f9d1-1f3ff-200d-1f373", "person_feeding_baby_light_skin_tone": "1f9d1-1f3fb-200d-1f37c", "person_feeding_baby_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f37c", "person_feeding_baby_medium_skin_tone": "1f9d1-1f3fd-200d-1f37c", "person_feeding_baby_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f37c", "person_feeding_baby_dark_skin_tone": "1f9d1-1f3ff-200d-1f37c", "mx_claus_light_skin_tone": "1f9d1-1f3fb-200d-1f384", "mx_claus_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f384", "mx_claus_medium_skin_tone": "1f9d1-1f3fd-200d-1f384", "mx_claus_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f384", "mx_claus_dark_skin_tone": "1f9d1-1f3ff-200d-1f384", "student_light_skin_tone": "1f9d1-1f3fb-200d-1f393", "student_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f393", "student_medium_skin_tone": "1f9d1-1f3fd-200d-1f393", "student_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f393", "student_dark_skin_tone": "1f9d1-1f3ff-200d-1f393", "singer_light_skin_tone": "1f9d1-1f3fb-200d-1f3a4", "singer_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f3a4", "singer_medium_skin_tone": "1f9d1-1f3fd-200d-1f3a4", "singer_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f3a4", "singer_dark_skin_tone": "1f9d1-1f3ff-200d-1f3a4", "artist_light_skin_tone": "1f9d1-1f3fb-200d-1f3a8", "artist_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f3a8", "artist_medium_skin_tone": "1f9d1-1f3fd-200d-1f3a8", "artist_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f3a8", "artist_dark_skin_tone": "1f9d1-1f3ff-200d-1f3a8", "teacher_light_skin_tone": "1f9d1-1f3fb-200d-1f3eb", "teacher_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f3eb", "teacher_medium_skin_tone": "1f9d1-1f3fd-200d-1f3eb", "teacher_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f3eb", "teacher_dark_skin_tone": "1f9d1-1f3ff-200d-1f3eb", "factory_worker_light_skin_tone": "1f9d1-1f3fb-200d-1f3ed", "factory_worker_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f3ed", "factory_worker_medium_skin_tone": "1f9d1-1f3fd-200d-1f3ed", "factory_worker_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f3ed", "factory_worker_dark_skin_tone": "1f9d1-1f3ff-200d-1f3ed", "technologist_light_skin_tone": "1f9d1-1f3fb-200d-1f4bb", "technologist_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f4bb", "technologist_medium_skin_tone": "1f9d1-1f3fd-200d-1f4bb", "technologist_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f4bb", "technologist_dark_skin_tone": "1f9d1-1f3ff-200d-1f4bb", "office_worker_light_skin_tone": "1f9d1-1f3fb-200d-1f4bc", "office_worker_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f4bc", "office_worker_medium_skin_tone": "1f9d1-1f3fd-200d-1f4bc", "office_worker_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f4bc", "office_worker_dark_skin_tone": "1f9d1-1f3ff-200d-1f4bc", "mechanic_light_skin_tone": "1f9d1-1f3fb-200d-1f527", "mechanic_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f527", "mechanic_medium_skin_tone": "1f9d1-1f3fd-200d-1f527", "mechanic_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f527", "mechanic_dark_skin_tone": "1f9d1-1f3ff-200d-1f527", "scientist_light_skin_tone": "1f9d1-1f3fb-200d-1f52c", "scientist_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f52c", "scientist_medium_skin_tone": "1f9d1-1f3fd-200d-1f52c", "scientist_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f52c", "scientist_dark_skin_tone": "1f9d1-1f3ff-200d-1f52c", "astronaut_light_skin_tone": "1f9d1-1f3fb-200d-1f680", "astronaut_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f680", "astronaut_medium_skin_tone": "1f9d1-1f3fd-200d-1f680", "astronaut_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f680", "astronaut_dark_skin_tone": "1f9d1-1f3ff-200d-1f680", "firefighter_light_skin_tone": "1f9d1-1f3fb-200d-1f692", "firefighter_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f692", "firefighter_medium_skin_tone": "1f9d1-1f3fd-200d-1f692", "firefighter_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f692", "firefighter_dark_skin_tone": "1f9d1-1f3ff-200d-1f692", "people_holding_hands_light_skin_tone_light_skin_tone": "1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb", "people_holding_hands_light_skin_tone_medium_light_skin_tone": "1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fc", "people_holding_hands_light_skin_tone_medium_skin_tone": "1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fd", "people_holding_hands_light_skin_tone_medium_dark_skin_tone": "1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fe", "people_holding_hands_light_skin_tone_dark_skin_tone": "1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3ff", "people_holding_hands_medium_light_skin_tone_light_skin_tone": "1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb", "people_holding_hands_medium_light_skin_tone_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc", "people_holding_hands_medium_light_skin_tone_medium_skin_tone": "1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fd", "people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone": "1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fe", "people_holding_hands_medium_light_skin_tone_dark_skin_tone": "1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3ff", "people_holding_hands_medium_skin_tone_light_skin_tone": "1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb", "people_holding_hands_medium_skin_tone_medium_light_skin_tone": "1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc", "people_holding_hands_medium_skin_tone_medium_skin_tone": "1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd", "people_holding_hands_medium_skin_tone_medium_dark_skin_tone": "1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fe", "people_holding_hands_medium_skin_tone_dark_skin_tone": "1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3ff", "people_holding_hands_medium_dark_skin_tone_light_skin_tone": "1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb", "people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone": "1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc", "people_holding_hands_medium_dark_skin_tone_medium_skin_tone": "1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd", "people_holding_hands_medium_dark_skin_tone_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe", "people_holding_hands_medium_dark_skin_tone_dark_skin_tone": "1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3ff", "people_holding_hands_dark_skin_tone_light_skin_tone": "1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb", "people_holding_hands_dark_skin_tone_medium_light_skin_tone": "1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc", "people_holding_hands_dark_skin_tone_medium_skin_tone": "1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd", "people_holding_hands_dark_skin_tone_medium_dark_skin_tone": "1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe", "people_holding_hands_dark_skin_tone_dark_skin_tone": "1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff", "person_with_probing_cane_light_skin_tone": "1f9d1-1f3fb-200d-1f9af", "person_with_probing_cane_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9af", "person_with_probing_cane_medium_skin_tone": "1f9d1-1f3fd-200d-1f9af", "person_with_probing_cane_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9af", "person_with_probing_cane_dark_skin_tone": "1f9d1-1f3ff-200d-1f9af", "red_haired_person_light_skin_tone": "1f9d1-1f3fb-200d-1f9b0", "red_haired_person_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9b0", "red_haired_person_medium_skin_tone": "1f9d1-1f3fd-200d-1f9b0", "red_haired_person_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9b0", "red_haired_person_dark_skin_tone": "1f9d1-1f3ff-200d-1f9b0", "curly_haired_person_light_skin_tone": "1f9d1-1f3fb-200d-1f9b1", "curly_haired_person_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9b1", "curly_haired_person_medium_skin_tone": "1f9d1-1f3fd-200d-1f9b1", "curly_haired_person_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9b1", "curly_haired_person_dark_skin_tone": "1f9d1-1f3ff-200d-1f9b1", "bald_person_light_skin_tone": "1f9d1-1f3fb-200d-1f9b2", "bald_person_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9b2", "bald_person_medium_skin_tone": "1f9d1-1f3fd-200d-1f9b2", "bald_person_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9b2", "bald_person_dark_skin_tone": "1f9d1-1f3ff-200d-1f9b2", "white_haired_person_light_skin_tone": "1f9d1-1f3fb-200d-1f9b3", "white_haired_person_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9b3", "white_haired_person_medium_skin_tone": "1f9d1-1f3fd-200d-1f9b3", "white_haired_person_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9b3", "white_haired_person_dark_skin_tone": "1f9d1-1f3ff-200d-1f9b3", "person_in_motorized_wheelchair_light_skin_tone": "1f9d1-1f3fb-200d-1f9bc", "person_in_motorized_wheelchair_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9bc", "person_in_motorized_wheelchair_medium_skin_tone": "1f9d1-1f3fd-200d-1f9bc", "person_in_motorized_wheelchair_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9bc", "person_in_motorized_wheelchair_dark_skin_tone": "1f9d1-1f3ff-200d-1f9bc", "person_in_manual_wheelchair_light_skin_tone": "1f9d1-1f3fb-200d-1f9bd", "person_in_manual_wheelchair_medium_light_skin_tone": "1f9d1-1f3fc-200d-1f9bd", "person_in_manual_wheelchair_medium_skin_tone": "1f9d1-1f3fd-200d-1f9bd", "person_in_manual_wheelchair_medium_dark_skin_tone": "1f9d1-1f3fe-200d-1f9bd", "person_in_manual_wheelchair_dark_skin_tone": "1f9d1-1f3ff-200d-1f9bd", "health_worker_light_skin_tone": "1f9d1-1f3fb-200d-2695-fe0f", "health_worker_medium_light_skin_tone": "1f9d1-1f3fc-200d-2695-fe0f", "health_worker_medium_skin_tone": "1f9d1-1f3fd-200d-2695-fe0f", "health_worker_medium_dark_skin_tone": "1f9d1-1f3fe-200d-2695-fe0f", "health_worker_dark_skin_tone": "1f9d1-1f3ff-200d-2695-fe0f", "judge_light_skin_tone": "1f9d1-1f3fb-200d-2696-fe0f", "judge_medium_light_skin_tone": "1f9d1-1f3fc-200d-2696-fe0f", "judge_medium_skin_tone": "1f9d1-1f3fd-200d-2696-fe0f", "judge_medium_dark_skin_tone": "1f9d1-1f3fe-200d-2696-fe0f", "judge_dark_skin_tone": "1f9d1-1f3ff-200d-2696-fe0f", "pilot_light_skin_tone": "1f9d1-1f3fb-200d-2708-fe0f", "pilot_medium_light_skin_tone": "1f9d1-1f3fc-200d-2708-fe0f", "pilot_medium_skin_tone": "1f9d1-1f3fd-200d-2708-fe0f", "pilot_medium_dark_skin_tone": "1f9d1-1f3fe-200d-2708-fe0f", "pilot_dark_skin_tone": "1f9d1-1f3ff-200d-2708-fe0f", "adult_light_skin_tone": "1f9d1-1f3fb", "adult_medium_light_skin_tone": "1f9d1-1f3fc", "adult_medium_skin_tone": "1f9d1-1f3fd", "adult_medium_dark_skin_tone": "1f9d1-1f3fe", "adult_dark_skin_tone": "1f9d1-1f3ff", "child_light_skin_tone": "1f9d2-1f3fb", "child_medium_light_skin_tone": "1f9d2-1f3fc", "child_medium_skin_tone": "1f9d2-1f3fd", "child_medium_dark_skin_tone": "1f9d2-1f3fe", "child_dark_skin_tone": "1f9d2-1f3ff", "older_adult_light_skin_tone": "1f9d3-1f3fb", "older_adult_medium_light_skin_tone": "1f9d3-1f3fc", "older_adult_medium_skin_tone": "1f9d3-1f3fd", "older_adult_medium_dark_skin_tone": "1f9d3-1f3fe", "older_adult_dark_skin_tone": "1f9d3-1f3ff", "bearded_person_light_skin_tone": "1f9d4-1f3fb", "bearded_person_medium_light_skin_tone": "1f9d4-1f3fc", "bearded_person_medium_skin_tone": "1f9d4-1f3fd", "bearded_person_medium_dark_skin_tone": "1f9d4-1f3fe", "bearded_person_dark_skin_tone": "1f9d4-1f3ff", "person_with_headscarf_light_skin_tone": "1f9d5-1f3fb", "person_with_headscarf_medium_light_skin_tone": "1f9d5-1f3fc", "person_with_headscarf_medium_skin_tone": "1f9d5-1f3fd", "person_with_headscarf_medium_dark_skin_tone": "1f9d5-1f3fe", "person_with_headscarf_dark_skin_tone": "1f9d5-1f3ff", "woman_in_steamy_room_light_skin_tone": "1f9d6-1f3fb-200d-2640-fe0f", "woman_in_steamy_room_medium_light_skin_tone": "1f9d6-1f3fc-200d-2640-fe0f", "woman_in_steamy_room_medium_skin_tone": "1f9d6-1f3fd-200d-2640-fe0f", "woman_in_steamy_room_medium_dark_skin_tone": "1f9d6-1f3fe-200d-2640-fe0f", "woman_in_steamy_room_dark_skin_tone": "1f9d6-1f3ff-200d-2640-fe0f", "man_in_steamy_room_light_skin_tone": "1f9d6-1f3fb-200d-2642-fe0f", "man_in_steamy_room_medium_light_skin_tone": "1f9d6-1f3fc-200d-2642-fe0f", "man_in_steamy_room_medium_skin_tone": "1f9d6-1f3fd-200d-2642-fe0f", "man_in_steamy_room_medium_dark_skin_tone": "1f9d6-1f3fe-200d-2642-fe0f", "man_in_steamy_room_dark_skin_tone": "1f9d6-1f3ff-200d-2642-fe0f", "person_in_steamy_room_light_skin_tone": "1f9d6-1f3fb", "person_in_steamy_room_medium_light_skin_tone": "1f9d6-1f3fc", "person_in_steamy_room_medium_skin_tone": "1f9d6-1f3fd", "person_in_steamy_room_medium_dark_skin_tone": "1f9d6-1f3fe", "person_in_steamy_room_dark_skin_tone": "1f9d6-1f3ff", "woman_climbing_light_skin_tone": "1f9d7-1f3fb-200d-2640-fe0f", "woman_climbing_medium_light_skin_tone": "1f9d7-1f3fc-200d-2640-fe0f", "woman_climbing_medium_skin_tone": "1f9d7-1f3fd-200d-2640-fe0f", "woman_climbing_medium_dark_skin_tone": "1f9d7-1f3fe-200d-2640-fe0f", "woman_climbing_dark_skin_tone": "1f9d7-1f3ff-200d-2640-fe0f", "man_climbing_light_skin_tone": "1f9d7-1f3fb-200d-2642-fe0f", "man_climbing_medium_light_skin_tone": "1f9d7-1f3fc-200d-2642-fe0f", "man_climbing_medium_skin_tone": "1f9d7-1f3fd-200d-2642-fe0f", "man_climbing_medium_dark_skin_tone": "1f9d7-1f3fe-200d-2642-fe0f", "man_climbing_dark_skin_tone": "1f9d7-1f3ff-200d-2642-fe0f", "person_climbing_light_skin_tone": "1f9d7-1f3fb", "person_climbing_medium_light_skin_tone": "1f9d7-1f3fc", "person_climbing_medium_skin_tone": "1f9d7-1f3fd", "person_climbing_medium_dark_skin_tone": "1f9d7-1f3fe", "person_climbing_dark_skin_tone": "1f9d7-1f3ff", "woman_in_lotus_position_light_skin_tone": "1f9d8-1f3fb-200d-2640-fe0f", "woman_in_lotus_position_medium_light_skin_tone": "1f9d8-1f3fc-200d-2640-fe0f", "woman_in_lotus_position_medium_skin_tone": "1f9d8-1f3fd-200d-2640-fe0f", "woman_in_lotus_position_medium_dark_skin_tone": "1f9d8-1f3fe-200d-2640-fe0f", "woman_in_lotus_position_dark_skin_tone": "1f9d8-1f3ff-200d-2640-fe0f", "man_in_lotus_position_light_skin_tone": "1f9d8-1f3fb-200d-2642-fe0f", "man_in_lotus_position_medium_light_skin_tone": "1f9d8-1f3fc-200d-2642-fe0f", "man_in_lotus_position_medium_skin_tone": "1f9d8-1f3fd-200d-2642-fe0f", "man_in_lotus_position_medium_dark_skin_tone": "1f9d8-1f3fe-200d-2642-fe0f", "man_in_lotus_position_dark_skin_tone": "1f9d8-1f3ff-200d-2642-fe0f", "person_in_lotus_position_light_skin_tone": "1f9d8-1f3fb", "person_in_lotus_position_medium_light_skin_tone": "1f9d8-1f3fc", "person_in_lotus_position_medium_skin_tone": "1f9d8-1f3fd", "person_in_lotus_position_medium_dark_skin_tone": "1f9d8-1f3fe", "person_in_lotus_position_dark_skin_tone": "1f9d8-1f3ff", "female_mage_light_skin_tone": "1f9d9-1f3fb-200d-2640-fe0f", "female_mage_medium_light_skin_tone": "1f9d9-1f3fc-200d-2640-fe0f", "female_mage_medium_skin_tone": "1f9d9-1f3fd-200d-2640-fe0f", "female_mage_medium_dark_skin_tone": "1f9d9-1f3fe-200d-2640-fe0f", "female_mage_dark_skin_tone": "1f9d9-1f3ff-200d-2640-fe0f", "male_mage_light_skin_tone": "1f9d9-1f3fb-200d-2642-fe0f", "male_mage_medium_light_skin_tone": "1f9d9-1f3fc-200d-2642-fe0f", "male_mage_medium_skin_tone": "1f9d9-1f3fd-200d-2642-fe0f", "male_mage_medium_dark_skin_tone": "1f9d9-1f3fe-200d-2642-fe0f", "male_mage_dark_skin_tone": "1f9d9-1f3ff-200d-2642-fe0f", "mage_light_skin_tone": "1f9d9-1f3fb", "mage_medium_light_skin_tone": "1f9d9-1f3fc", "mage_medium_skin_tone": "1f9d9-1f3fd", "mage_medium_dark_skin_tone": "1f9d9-1f3fe", "mage_dark_skin_tone": "1f9d9-1f3ff", "female_fairy_light_skin_tone": "1f9da-1f3fb-200d-2640-fe0f", "female_fairy_medium_light_skin_tone": "1f9da-1f3fc-200d-2640-fe0f", "female_fairy_medium_skin_tone": "1f9da-1f3fd-200d-2640-fe0f", "female_fairy_medium_dark_skin_tone": "1f9da-1f3fe-200d-2640-fe0f", "female_fairy_dark_skin_tone": "1f9da-1f3ff-200d-2640-fe0f", "male_fairy_light_skin_tone": "1f9da-1f3fb-200d-2642-fe0f", "male_fairy_medium_light_skin_tone": "1f9da-1f3fc-200d-2642-fe0f", "male_fairy_medium_skin_tone": "1f9da-1f3fd-200d-2642-fe0f", "male_fairy_medium_dark_skin_tone": "1f9da-1f3fe-200d-2642-fe0f", "male_fairy_dark_skin_tone": "1f9da-1f3ff-200d-2642-fe0f", "fairy_light_skin_tone": "1f9da-1f3fb", "fairy_medium_light_skin_tone": "1f9da-1f3fc", "fairy_medium_skin_tone": "1f9da-1f3fd", "fairy_medium_dark_skin_tone": "1f9da-1f3fe", "fairy_dark_skin_tone": "1f9da-1f3ff", "female_vampire_light_skin_tone": "1f9db-1f3fb-200d-2640-fe0f", "female_vampire_medium_light_skin_tone": "1f9db-1f3fc-200d-2640-fe0f", "female_vampire_medium_skin_tone": "1f9db-1f3fd-200d-2640-fe0f", "female_vampire_medium_dark_skin_tone": "1f9db-1f3fe-200d-2640-fe0f", "female_vampire_dark_skin_tone": "1f9db-1f3ff-200d-2640-fe0f", "male_vampire_light_skin_tone": "1f9db-1f3fb-200d-2642-fe0f", "male_vampire_medium_light_skin_tone": "1f9db-1f3fc-200d-2642-fe0f", "male_vampire_medium_skin_tone": "1f9db-1f3fd-200d-2642-fe0f", "male_vampire_medium_dark_skin_tone": "1f9db-1f3fe-200d-2642-fe0f", "male_vampire_dark_skin_tone": "1f9db-1f3ff-200d-2642-fe0f", "vampire_light_skin_tone": "1f9db-1f3fb", "vampire_medium_light_skin_tone": "1f9db-1f3fc", "vampire_medium_skin_tone": "1f9db-1f3fd", "vampire_medium_dark_skin_tone": "1f9db-1f3fe", "vampire_dark_skin_tone": "1f9db-1f3ff", "mermaid_light_skin_tone": "1f9dc-1f3fb-200d-2640-fe0f", "mermaid_medium_light_skin_tone": "1f9dc-1f3fc-200d-2640-fe0f", "mermaid_medium_skin_tone": "1f9dc-1f3fd-200d-2640-fe0f", "mermaid_medium_dark_skin_tone": "1f9dc-1f3fe-200d-2640-fe0f", "mermaid_dark_skin_tone": "1f9dc-1f3ff-200d-2640-fe0f", "merman_light_skin_tone": "1f9dc-1f3fb-200d-2642-fe0f", "merman_medium_light_skin_tone": "1f9dc-1f3fc-200d-2642-fe0f", "merman_medium_skin_tone": "1f9dc-1f3fd-200d-2642-fe0f", "merman_medium_dark_skin_tone": "1f9dc-1f3fe-200d-2642-fe0f", "merman_dark_skin_tone": "1f9dc-1f3ff-200d-2642-fe0f", "merperson_light_skin_tone": "1f9dc-1f3fb", "merperson_medium_light_skin_tone": "1f9dc-1f3fc", "merperson_medium_skin_tone": "1f9dc-1f3fd", "merperson_medium_dark_skin_tone": "1f9dc-1f3fe", "merperson_dark_skin_tone": "1f9dc-1f3ff", "female_elf_light_skin_tone": "1f9dd-1f3fb-200d-2640-fe0f", "female_elf_medium_light_skin_tone": "1f9dd-1f3fc-200d-2640-fe0f", "female_elf_medium_skin_tone": "1f9dd-1f3fd-200d-2640-fe0f", "female_elf_medium_dark_skin_tone": "1f9dd-1f3fe-200d-2640-fe0f", "female_elf_dark_skin_tone": "1f9dd-1f3ff-200d-2640-fe0f", "male_elf_light_skin_tone": "1f9dd-1f3fb-200d-2642-fe0f", "male_elf_medium_light_skin_tone": "1f9dd-1f3fc-200d-2642-fe0f", "male_elf_medium_skin_tone": "1f9dd-1f3fd-200d-2642-fe0f", "male_elf_medium_dark_skin_tone": "1f9dd-1f3fe-200d-2642-fe0f", "male_elf_dark_skin_tone": "1f9dd-1f3ff-200d-2642-fe0f", "elf_light_skin_tone": "1f9dd-1f3fb", "elf_medium_light_skin_tone": "1f9dd-1f3fc", "elf_medium_skin_tone": "1f9dd-1f3fd", "elf_medium_dark_skin_tone": "1f9dd-1f3fe", "elf_dark_skin_tone": "1f9dd-1f3ff", "point_up_light_skin_tone": "261d-1f3fb", "point_up_medium_light_skin_tone": "261d-1f3fc", "point_up_medium_skin_tone": "261d-1f3fd", "point_up_medium_dark_skin_tone": "261d-1f3fe", "point_up_dark_skin_tone": "261d-1f3ff", "woman-bouncing-ball_light_skin_tone": "26f9-1f3fb-200d-2640-fe0f", "basketball_woman_light_skin_tone": "26f9-1f3fb-200d-2640-fe0f", "woman-bouncing-ball_medium_light_skin_tone": "26f9-1f3fc-200d-2640-fe0f", "basketball_woman_medium_light_skin_tone": "26f9-1f3fc-200d-2640-fe0f", "woman-bouncing-ball_medium_skin_tone": "26f9-1f3fd-200d-2640-fe0f", "basketball_woman_medium_skin_tone": "26f9-1f3fd-200d-2640-fe0f", "woman-bouncing-ball_medium_dark_skin_tone": "26f9-1f3fe-200d-2640-fe0f", "basketball_woman_medium_dark_skin_tone": "26f9-1f3fe-200d-2640-fe0f", "woman-bouncing-ball_dark_skin_tone": "26f9-1f3ff-200d-2640-fe0f", "basketball_woman_dark_skin_tone": "26f9-1f3ff-200d-2640-fe0f", "man-bouncing-ball_light_skin_tone": "26f9-1f3fb-200d-2642-fe0f", "basketball_man_light_skin_tone": "26f9-1f3fb-200d-2642-fe0f", "man-bouncing-ball_medium_light_skin_tone": "26f9-1f3fc-200d-2642-fe0f", "basketball_man_medium_light_skin_tone": "26f9-1f3fc-200d-2642-fe0f", "man-bouncing-ball_medium_skin_tone": "26f9-1f3fd-200d-2642-fe0f", "basketball_man_medium_skin_tone": "26f9-1f3fd-200d-2642-fe0f", "man-bouncing-ball_medium_dark_skin_tone": "26f9-1f3fe-200d-2642-fe0f", "basketball_man_medium_dark_skin_tone": "26f9-1f3fe-200d-2642-fe0f", "man-bouncing-ball_dark_skin_tone": "26f9-1f3ff-200d-2642-fe0f", "basketball_man_dark_skin_tone": "26f9-1f3ff-200d-2642-fe0f", "person_with_ball_light_skin_tone": "26f9-1f3fb", "person_with_ball_medium_light_skin_tone": "26f9-1f3fc", "person_with_ball_medium_skin_tone": "26f9-1f3fd", "person_with_ball_medium_dark_skin_tone": "26f9-1f3fe", "person_with_ball_dark_skin_tone": "26f9-1f3ff", "fist_light_skin_tone": "270a-1f3fb", "fist_raised_light_skin_tone": "270a-1f3fb", "fist_medium_light_skin_tone": "270a-1f3fc", "fist_raised_medium_light_skin_tone": "270a-1f3fc", "fist_medium_skin_tone": "270a-1f3fd", "fist_raised_medium_skin_tone": "270a-1f3fd", "fist_medium_dark_skin_tone": "270a-1f3fe", "fist_raised_medium_dark_skin_tone": "270a-1f3fe", "fist_dark_skin_tone": "270a-1f3ff", "fist_raised_dark_skin_tone": "270a-1f3ff", "hand_light_skin_tone": "270b-1f3fb", "raised_hand_light_skin_tone": "270b-1f3fb", "hand_medium_light_skin_tone": "270b-1f3fc", "raised_hand_medium_light_skin_tone": "270b-1f3fc", "hand_medium_skin_tone": "270b-1f3fd", "raised_hand_medium_skin_tone": "270b-1f3fd", "hand_medium_dark_skin_tone": "270b-1f3fe", "raised_hand_medium_dark_skin_tone": "270b-1f3fe", "hand_dark_skin_tone": "270b-1f3ff", "raised_hand_dark_skin_tone": "270b-1f3ff", "v_light_skin_tone": "270c-1f3fb", "v_medium_light_skin_tone": "270c-1f3fc", "v_medium_skin_tone": "270c-1f3fd", "v_medium_dark_skin_tone": "270c-1f3fe", "v_dark_skin_tone": "270c-1f3ff", "writing_hand_light_skin_tone": "270d-1f3fb", "writing_hand_medium_light_skin_tone": "270d-1f3fc", "writing_hand_medium_skin_tone": "270d-1f3fd", "writing_hand_medium_dark_skin_tone": "270d-1f3fe", "writing_hand_dark_skin_tone": "270d-1f3ff", "mattermost": "mattermost"} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_search.go b/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_search.go deleted file mode 100644 index 71e2671c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/emoji_search.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type EmojiSearch struct { - Term string `json:"term"` - PrefixOnly bool `json:"prefix_only"` -} - -func (es *EmojiSearch) ToJson() string { - b, _ := json.Marshal(es) - return string(b) -} - -func EmojiSearchFromJson(data io.Reader) *EmojiSearch { - var es *EmojiSearch - json.NewDecoder(data).Decode(&es) - return es -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/feature_flags.go b/vendor/github.com/mattermost/mattermost-server/v5/model/feature_flags.go deleted file mode 100644 index 7d3d6680..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/feature_flags.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "reflect" - "strconv" -) - -type FeatureFlags struct { - // Exists only for unit and manual testing. - // When set to a value, will be returned by the ping endpoint. - TestFeature string - // Exists only for testing bool functionality. Boolean feature flags interpret "on" or "true" as true and - // all other values as false. - TestBoolFeature bool - - // Toggle on and off scheduled jobs for cloud user limit emails see MM-29999 - CloudDelinquentEmailJobsEnabled bool - - // Toggle on and off support for Collapsed Threads - CollapsedThreads bool - - // Enable the remote cluster service for shared channels. - EnableRemoteClusterService bool - - // AppsEnabled toggle the Apps framework functionalities both in server and client side - AppsEnabled bool - - // Feature flags to control plugin versions - PluginIncidentManagement string `plugin_id:"com.mattermost.plugin-incident-management"` - PluginApps string `plugin_id:"com.mattermost.apps"` - PluginFocalboard string `plugin_id:"focalboard"` - - // Enable timed dnd support for user status - TimedDND bool -} - -func (f *FeatureFlags) SetDefaults() { - f.TestFeature = "off" - f.TestBoolFeature = false - f.CloudDelinquentEmailJobsEnabled = false - f.CollapsedThreads = true - f.EnableRemoteClusterService = false - f.AppsEnabled = false - f.PluginIncidentManagement = "1.16.1" - f.PluginApps = "" - f.PluginFocalboard = "" - f.TimedDND = false -} - -func (f *FeatureFlags) Plugins() map[string]string { - rFFVal := reflect.ValueOf(f).Elem() - rFFType := reflect.TypeOf(f).Elem() - - pluginVersions := make(map[string]string) - for i := 0; i < rFFVal.NumField(); i++ { - rFieldVal := rFFVal.Field(i) - rFieldType := rFFType.Field(i) - - pluginId, hasPluginId := rFieldType.Tag.Lookup("plugin_id") - if !hasPluginId { - continue - } - - pluginVersions[pluginId] = rFieldVal.String() - } - - return pluginVersions -} - -// ToMap returns the feature flags as a map[string]string -// Supports boolean and string feature flags. -func (f *FeatureFlags) ToMap() map[string]string { - refStructVal := reflect.ValueOf(*f) - refStructType := reflect.TypeOf(*f) - ret := make(map[string]string) - for i := 0; i < refStructVal.NumField(); i++ { - refFieldVal := refStructVal.Field(i) - if !refFieldVal.IsValid() { - continue - } - refFieldType := refStructType.Field(i) - switch refFieldType.Type.Kind() { - case reflect.Bool: - ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool()) - default: - ret[refFieldType.Name] = refFieldVal.String() - } - } - - return ret -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/file.go b/vendor/github.com/mattermost/mattermost-server/v5/model/file.go deleted file mode 100644 index d2cb8f34..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/file.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const ( - MaxImageSize = int64(6048 * 4032) // 24 megapixels, roughly 36MB as a raw image -) - -type FileUploadResponse struct { - FileInfos []*FileInfo `json:"file_infos"` - ClientIds []string `json:"client_ids"` -} - -func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse { - var o *FileUploadResponse - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *FileUploadResponse) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info.go b/vendor/github.com/mattermost/mattermost-server/v5/model/file_info.go deleted file mode 100644 index 2bad9023..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "image" - "image/gif" - "io" - "mime" - "net/http" - "path/filepath" - "strings" -) - -const ( - FILEINFO_SORT_BY_CREATED = "CreateAt" - FILEINFO_SORT_BY_SIZE = "Size" -) - -// GetFileInfosOptions contains options for getting FileInfos -type GetFileInfosOptions struct { - // UserIds optionally limits the FileInfos to those created by the given users. - UserIds []string `json:"user_ids"` - // ChannelIds optionally limits the FileInfos to those created in the given channels. - ChannelIds []string `json:"channel_ids"` - // Since optionally limits FileInfos to those created at or after the given time, specified as Unix time in milliseconds. - Since int64 `json:"since"` - // IncludeDeleted if set includes deleted FileInfos. - IncludeDeleted bool `json:"include_deleted"` - // SortBy sorts the FileInfos by this field. The default is to sort by date created. - SortBy string `json:"sort_by"` - // SortDescending changes the sort direction to descending order when true. - SortDescending bool `json:"sort_descending"` -} - -type FileInfo struct { - Id string `json:"id"` - CreatorId string `json:"user_id"` - PostId string `json:"post_id,omitempty"` - ChannelId string `db:"-" json:"channel_id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - Path string `json:"-"` // not sent back to the client - ThumbnailPath string `json:"-"` // not sent back to the client - PreviewPath string `json:"-"` // not sent back to the client - Name string `json:"name"` - Extension string `json:"extension"` - Size int64 `json:"size"` - MimeType string `json:"mime_type"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` - HasPreviewImage bool `json:"has_preview_image,omitempty"` - MiniPreview *[]byte `json:"mini_preview"` // declared as *[]byte to avoid postgres/mysql differences in deserialization - Content string `json:"-"` - RemoteId *string `json:"remote_id"` -} - -func (fi *FileInfo) ToJson() string { - b, _ := json.Marshal(fi) - return string(b) -} - -func FileInfoFromJson(data io.Reader) *FileInfo { - decoder := json.NewDecoder(data) - - var fi FileInfo - if err := decoder.Decode(&fi); err != nil { - return nil - } - return &fi -} - -func FileInfosToJson(infos []*FileInfo) string { - b, _ := json.Marshal(infos) - return string(b) -} - -func FileInfosFromJson(data io.Reader) []*FileInfo { - decoder := json.NewDecoder(data) - - var infos []*FileInfo - if err := decoder.Decode(&infos); err != nil { - return nil - } - return infos -} - -func (fi *FileInfo) PreSave() { - if fi.Id == "" { - fi.Id = NewId() - } - - if fi.CreateAt == 0 { - fi.CreateAt = GetMillis() - } - - if fi.UpdateAt < fi.CreateAt { - fi.UpdateAt = fi.CreateAt - } - - if fi.RemoteId == nil { - fi.RemoteId = NewString("") - } -} - -func (fi *FileInfo) IsValid() *AppError { - if !IsValidId(fi.Id) { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(fi.CreatorId) && fi.CreatorId != "nouser" { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.user_id.app_error", nil, "id="+fi.Id, http.StatusBadRequest) - } - - if fi.PostId != "" && !IsValidId(fi.PostId) { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.post_id.app_error", nil, "id="+fi.Id, http.StatusBadRequest) - } - - if fi.CreateAt == 0 { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.create_at.app_error", nil, "id="+fi.Id, http.StatusBadRequest) - } - - if fi.UpdateAt == 0 { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.update_at.app_error", nil, "id="+fi.Id, http.StatusBadRequest) - } - - if fi.Path == "" { - return NewAppError("FileInfo.IsValid", "model.file_info.is_valid.path.app_error", nil, "id="+fi.Id, http.StatusBadRequest) - } - - return nil -} - -func (fi *FileInfo) IsImage() bool { - return strings.HasPrefix(fi.MimeType, "image") -} - -func NewInfo(name string) *FileInfo { - info := &FileInfo{ - Name: name, - } - - extension := strings.ToLower(filepath.Ext(name)) - info.MimeType = mime.TypeByExtension(extension) - - if extension != "" && extension[0] == '.' { - // The client expects a file extension without the leading period - info.Extension = extension[1:] - } else { - info.Extension = extension - } - - return info -} - -func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *AppError) { - info := &FileInfo{ - Name: name, - Size: int64(size), - } - var err *AppError - - extension := strings.ToLower(filepath.Ext(name)) - info.MimeType = mime.TypeByExtension(extension) - - if extension != "" && extension[0] == '.' { - // The client expects a file extension without the leading period - info.Extension = extension[1:] - } else { - info.Extension = extension - } - - if info.IsImage() { - // Only set the width and height if it's actually an image that we can understand - if config, _, err := image.DecodeConfig(data); err == nil { - info.Width = config.Width - info.Height = config.Height - - if info.MimeType == "image/gif" { - // Just show the gif itself instead of a preview image for animated gifs - data.Seek(0, io.SeekStart) - gifConfig, err := gif.DecodeAll(data) - if err != nil { - // Still return the rest of the info even though it doesn't appear to be an actual gif - info.HasPreviewImage = true - return info, NewAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, err.Error(), http.StatusBadRequest) - } - info.HasPreviewImage = len(gifConfig.Image) == 1 - } else { - info.HasPreviewImage = true - } - } - } - - return info, err -} - -func GetEtagForFileInfos(infos []*FileInfo) string { - if len(infos) == 0 { - return Etag() - } - - var maxUpdateAt int64 - - for _, info := range infos { - if info.UpdateAt > maxUpdateAt { - maxUpdateAt = info.UpdateAt - } - } - - return Etag(infos[0].PostId, maxUpdateAt) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_list.go b/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_list.go deleted file mode 100644 index cd9694f5..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_list.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "sort" -) - -type FileInfoList struct { - Order []string `json:"order"` - FileInfos map[string]*FileInfo `json:"file_infos"` - NextFileInfoId string `json:"next_file_info_id"` - PrevFileInfoId string `json:"prev_file_info_id"` -} - -func NewFileInfoList() *FileInfoList { - return &FileInfoList{ - Order: make([]string, 0), - FileInfos: make(map[string]*FileInfo), - NextFileInfoId: "", - PrevFileInfoId: "", - } -} - -func (o *FileInfoList) ToSlice() []*FileInfo { - var fileInfos []*FileInfo - for _, id := range o.Order { - fileInfos = append(fileInfos, o.FileInfos[id]) - } - return fileInfos -} - -func (o *FileInfoList) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } else { - return string(b) - } -} - -func (o *FileInfoList) MakeNonNil() { - if o.Order == nil { - o.Order = make([]string, 0) - } - - if o.FileInfos == nil { - o.FileInfos = make(map[string]*FileInfo) - } -} - -func (o *FileInfoList) AddOrder(id string) { - if o.Order == nil { - o.Order = make([]string, 0, 128) - } - - o.Order = append(o.Order, id) -} - -func (o *FileInfoList) AddFileInfo(fileInfo *FileInfo) { - if o.FileInfos == nil { - o.FileInfos = make(map[string]*FileInfo) - } - - o.FileInfos[fileInfo.Id] = fileInfo -} - -func (o *FileInfoList) UniqueOrder() { - keys := make(map[string]bool) - order := []string{} - for _, fileInfoId := range o.Order { - if _, value := keys[fileInfoId]; !value { - keys[fileInfoId] = true - order = append(order, fileInfoId) - } - } - - o.Order = order -} - -func (o *FileInfoList) Extend(other *FileInfoList) { - for fileInfoId := range other.FileInfos { - o.AddFileInfo(other.FileInfos[fileInfoId]) - } - - for _, fileInfoId := range other.Order { - o.AddOrder(fileInfoId) - } - - o.UniqueOrder() -} - -func (o *FileInfoList) SortByCreateAt() { - sort.Slice(o.Order, func(i, j int) bool { - return o.FileInfos[o.Order[i]].CreateAt > o.FileInfos[o.Order[j]].CreateAt - }) -} - -func (o *FileInfoList) Etag() string { - id := "0" - var t int64 = 0 - - for _, v := range o.FileInfos { - if v.UpdateAt > t { - t = v.UpdateAt - id = v.Id - } else if v.UpdateAt == t && v.Id > id { - t = v.UpdateAt - id = v.Id - } - } - - orderId := "" - if len(o.Order) > 0 { - orderId = o.Order[0] - } - - return Etag(orderId, id, t) -} - -func FileInfoListFromJson(data io.Reader) *FileInfoList { - var o *FileInfoList - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_search_results.go b/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_search_results.go deleted file mode 100644 index 90f2922b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/file_info_search_results.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type FileInfoSearchMatches map[string][]string - -type FileInfoSearchResults struct { - *FileInfoList - Matches FileInfoSearchMatches `json:"matches"` -} - -func MakeFileInfoSearchResults(fileInfos *FileInfoList, matches FileInfoSearchMatches) *FileInfoSearchResults { - return &FileInfoSearchResults{ - fileInfos, - matches, - } -} - -func (o *FileInfoSearchResults) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } - return string(b) -} - -func FileInfoSearchResultsFromJson(data io.Reader) *FileInfoSearchResults { - var o *FileInfoSearchResults - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/gitlab.go b/vendor/github.com/mattermost/mattermost-server/v5/model/gitlab.go deleted file mode 100644 index 0b069cd6..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/gitlab.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - USER_AUTH_SERVICE_GITLAB = "gitlab" -) diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/group.go b/vendor/github.com/mattermost/mattermost-server/v5/model/group.go deleted file mode 100644 index c70b7aa1..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/group.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "regexp" -) - -const ( - GroupSourceLdap GroupSource = "ldap" - - GroupNameMaxLength = 64 - GroupSourceMaxLength = 64 - GroupDisplayNameMaxLength = 128 - GroupDescriptionMaxLength = 1024 - GroupRemoteIDMaxLength = 48 -) - -type GroupSource string - -var allGroupSources = []GroupSource{ - GroupSourceLdap, -} - -var groupSourcesRequiringRemoteID = []GroupSource{ - GroupSourceLdap, -} - -type Group struct { - Id string `json:"id"` - Name *string `json:"name,omitempty"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - Source GroupSource `json:"source"` - RemoteId string `json:"remote_id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - HasSyncables bool `db:"-" json:"has_syncables"` - MemberCount *int `db:"-" json:"member_count,omitempty"` - AllowReference bool `json:"allow_reference"` -} - -type GroupWithSchemeAdmin struct { - Group - SchemeAdmin *bool `db:"SyncableSchemeAdmin" json:"scheme_admin,omitempty"` -} - -type GroupsAssociatedToChannelWithSchemeAdmin struct { - ChannelId string `json:"channel_id"` - Group - SchemeAdmin *bool `db:"SyncableSchemeAdmin" json:"scheme_admin,omitempty"` -} -type GroupsAssociatedToChannel struct { - ChannelId string `json:"channel_id"` - Groups []*GroupWithSchemeAdmin `json:"groups"` -} - -type GroupPatch struct { - Name *string `json:"name"` - DisplayName *string `json:"display_name"` - Description *string `json:"description"` - AllowReference *bool `json:"allow_reference"` -} - -type LdapGroupSearchOpts struct { - Q string - IsLinked *bool - IsConfigured *bool -} - -type GroupSearchOpts struct { - Q string - NotAssociatedToTeam string - NotAssociatedToChannel string - IncludeMemberCount bool - FilterAllowReference bool - PageOpts *PageOpts - Since int64 - - // FilterParentTeamPermitted filters the groups to the intersect of the - // set associated to the parent team and those returned by the query. - // If the parent team is not group-constrained or if NotAssociatedToChannel - // is not set then this option is ignored. - FilterParentTeamPermitted bool -} - -type PageOpts struct { - Page int - PerPage int -} - -type GroupStats struct { - GroupID string `json:"group_id"` - TotalMemberCount int64 `json:"total_member_count"` -} - -func (group *Group) Patch(patch *GroupPatch) { - if patch.Name != nil { - group.Name = patch.Name - } - if patch.DisplayName != nil { - group.DisplayName = *patch.DisplayName - } - if patch.Description != nil { - group.Description = *patch.Description - } - if patch.AllowReference != nil { - group.AllowReference = *patch.AllowReference - } -} - -func (group *Group) IsValidForCreate() *AppError { - err := group.IsValidName() - if err != nil { - return err - } - - if l := len(group.DisplayName); l == 0 || l > GroupDisplayNameMaxLength { - return NewAppError("Group.IsValidForCreate", "model.group.display_name.app_error", map[string]interface{}{"GroupDisplayNameMaxLength": GroupDisplayNameMaxLength}, "", http.StatusBadRequest) - } - - if len(group.Description) > GroupDescriptionMaxLength { - return NewAppError("Group.IsValidForCreate", "model.group.description.app_error", map[string]interface{}{"GroupDescriptionMaxLength": GroupDescriptionMaxLength}, "", http.StatusBadRequest) - } - - isValidSource := false - for _, groupSource := range allGroupSources { - if group.Source == groupSource { - isValidSource = true - break - } - } - if !isValidSource { - return NewAppError("Group.IsValidForCreate", "model.group.source.app_error", nil, "", http.StatusBadRequest) - } - - if len(group.RemoteId) > GroupRemoteIDMaxLength || (group.RemoteId == "" && group.requiresRemoteId()) { - return NewAppError("Group.IsValidForCreate", "model.group.remote_id.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (group *Group) requiresRemoteId() bool { - for _, groupSource := range groupSourcesRequiringRemoteID { - if groupSource == group.Source { - return true - } - } - return false -} - -func (group *Group) IsValidForUpdate() *AppError { - if !IsValidId(group.Id) { - return NewAppError("Group.IsValidForUpdate", "app.group.id.app_error", nil, "", http.StatusBadRequest) - } - if group.CreateAt == 0 { - return NewAppError("Group.IsValidForUpdate", "model.group.create_at.app_error", nil, "", http.StatusBadRequest) - } - if group.UpdateAt == 0 { - return NewAppError("Group.IsValidForUpdate", "model.group.update_at.app_error", nil, "", http.StatusBadRequest) - } - if err := group.IsValidForCreate(); err != nil { - return err - } - return nil -} - -func (group *Group) ToJson() string { - b, _ := json.Marshal(group) - return string(b) -} - -var validGroupnameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`) - -func (group *Group) IsValidName() *AppError { - - if group.Name == nil { - if group.AllowReference { - return NewAppError("Group.IsValidName", "model.group.name.app_error", map[string]interface{}{"GroupNameMaxLength": GroupNameMaxLength}, "", http.StatusBadRequest) - } - } else { - if l := len(*group.Name); l == 0 || l > GroupNameMaxLength { - return NewAppError("Group.IsValidName", "model.group.name.invalid_length.app_error", map[string]interface{}{"GroupNameMaxLength": GroupNameMaxLength}, "", http.StatusBadRequest) - } - - if !validGroupnameChars.MatchString(*group.Name) { - return NewAppError("Group.IsValidName", "model.group.name.invalid_chars.app_error", nil, "", http.StatusBadRequest) - } - } - return nil -} - -func GroupFromJson(data io.Reader) *Group { - var group *Group - json.NewDecoder(data).Decode(&group) - return group -} - -func GroupsFromJson(data io.Reader) []*Group { - var groups []*Group - json.NewDecoder(data).Decode(&groups) - return groups -} - -func GroupPatchFromJson(data io.Reader) *GroupPatch { - var groupPatch *GroupPatch - json.NewDecoder(data).Decode(&groupPatch) - return groupPatch -} - -func GroupStatsFromJson(data io.Reader) *GroupStats { - var groupStats *GroupStats - json.NewDecoder(data).Decode(&groupStats) - return groupStats -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/group_member.go b/vendor/github.com/mattermost/mattermost-server/v5/model/group_member.go deleted file mode 100644 index d18d7849..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/group_member.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import "net/http" - -type GroupMember struct { - GroupId string `json:"group_id"` - UserId string `json:"user_id"` - CreateAt int64 `json:"create_at"` - DeleteAt int64 `json:"delete_at"` -} - -func (gm *GroupMember) IsValid() *AppError { - if !IsValidId(gm.GroupId) { - return NewAppError("GroupMember.IsValid", "model.group_member.group_id.app_error", nil, "", http.StatusBadRequest) - } - if !IsValidId(gm.UserId) { - return NewAppError("GroupMember.IsValid", "model.group_member.user_id.app_error", nil, "", http.StatusBadRequest) - } - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/group_syncable.go b/vendor/github.com/mattermost/mattermost-server/v5/model/group_syncable.go deleted file mode 100644 index eb3bdf09..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/group_syncable.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" -) - -type GroupSyncableType string - -const ( - GroupSyncableTypeTeam GroupSyncableType = "Team" - GroupSyncableTypeChannel GroupSyncableType = "Channel" -) - -func (gst GroupSyncableType) String() string { - return string(gst) -} - -type GroupSyncable struct { - GroupId string `json:"group_id"` - - // SyncableId represents the Id of the model that is being synced with the group, for example a ChannelId or - // TeamId. - SyncableId string `db:"-" json:"-"` - - AutoAdd bool `json:"auto_add"` - SchemeAdmin bool `json:"scheme_admin"` - CreateAt int64 `json:"create_at"` - DeleteAt int64 `json:"delete_at"` - UpdateAt int64 `json:"update_at"` - Type GroupSyncableType `db:"-" json:"-"` - - // Values joined in from the associated team and/or channel - ChannelDisplayName string `db:"-" json:"-"` - TeamDisplayName string `db:"-" json:"-"` - TeamType string `db:"-" json:"-"` - ChannelType string `db:"-" json:"-"` - TeamID string `db:"-" json:"-"` -} - -func (syncable *GroupSyncable) IsValid() *AppError { - if !IsValidId(syncable.GroupId) { - return NewAppError("GroupSyncable.SyncableIsValid", "model.group_syncable.group_id.app_error", nil, "", http.StatusBadRequest) - } - if !IsValidId(syncable.SyncableId) { - return NewAppError("GroupSyncable.SyncableIsValid", "model.group_syncable.syncable_id.app_error", nil, "", http.StatusBadRequest) - } - return nil -} - -func (syncable *GroupSyncable) UnmarshalJSON(b []byte) error { - var kvp map[string]interface{} - err := json.Unmarshal(b, &kvp) - if err != nil { - return err - } - var channelId string - var teamId string - for key, value := range kvp { - switch key { - case "team_id": - teamId = value.(string) - case "channel_id": - channelId = value.(string) - case "group_id": - syncable.GroupId = value.(string) - case "auto_add": - syncable.AutoAdd = value.(bool) - default: - } - } - if channelId != "" { - syncable.TeamID = teamId - syncable.SyncableId = channelId - syncable.Type = GroupSyncableTypeChannel - } else { - syncable.SyncableId = teamId - syncable.Type = GroupSyncableTypeTeam - } - return nil -} - -func (syncable *GroupSyncable) MarshalJSON() ([]byte, error) { - type Alias GroupSyncable - switch syncable.Type { - case GroupSyncableTypeTeam: - return json.Marshal(&struct { - TeamID string `json:"team_id"` - TeamDisplayName string `json:"team_display_name,omitempty"` - TeamType string `json:"team_type,omitempty"` - Type GroupSyncableType `json:"type,omitempty"` - *Alias - }{ - TeamDisplayName: syncable.TeamDisplayName, - TeamType: syncable.TeamType, - TeamID: syncable.SyncableId, - Type: syncable.Type, - Alias: (*Alias)(syncable), - }) - case GroupSyncableTypeChannel: - return json.Marshal(&struct { - ChannelID string `json:"channel_id"` - ChannelDisplayName string `json:"channel_display_name,omitempty"` - ChannelType string `json:"channel_type,omitempty"` - Type GroupSyncableType `json:"type,omitempty"` - - TeamID string `json:"team_id,omitempty"` - TeamDisplayName string `json:"team_display_name,omitempty"` - TeamType string `json:"team_type,omitempty"` - - *Alias - }{ - ChannelID: syncable.SyncableId, - ChannelDisplayName: syncable.ChannelDisplayName, - ChannelType: syncable.ChannelType, - Type: syncable.Type, - - TeamID: syncable.TeamID, - TeamDisplayName: syncable.TeamDisplayName, - TeamType: syncable.TeamType, - - Alias: (*Alias)(syncable), - }) - default: - return nil, &json.MarshalerError{ - Err: fmt.Errorf("unknown syncable type: %s", syncable.Type), - } - } -} - -type GroupSyncablePatch struct { - AutoAdd *bool `json:"auto_add"` - SchemeAdmin *bool `json:"scheme_admin"` -} - -func (syncable *GroupSyncable) Patch(patch *GroupSyncablePatch) { - if patch.AutoAdd != nil { - syncable.AutoAdd = *patch.AutoAdd - } - if patch.SchemeAdmin != nil { - syncable.SchemeAdmin = *patch.SchemeAdmin - } -} - -type UserTeamIDPair struct { - UserID string - TeamID string -} - -type UserChannelIDPair struct { - UserID string - ChannelID string -} - -func GroupSyncableFromJson(data io.Reader) *GroupSyncable { - groupSyncable := &GroupSyncable{} - bodyBytes, _ := ioutil.ReadAll(data) - json.Unmarshal(bodyBytes, groupSyncable) - return groupSyncable -} - -func GroupSyncablesFromJson(data io.Reader) []*GroupSyncable { - groupSyncables := []*GroupSyncable{} - bodyBytes, _ := ioutil.ReadAll(data) - json.Unmarshal(bodyBytes, &groupSyncables) - return groupSyncables -} - -func NewGroupTeam(groupID, teamID string, autoAdd bool) *GroupSyncable { - return &GroupSyncable{ - GroupId: groupID, - SyncableId: teamID, - Type: GroupSyncableTypeTeam, - AutoAdd: autoAdd, - } -} - -func NewGroupChannel(groupID, channelID string, autoAdd bool) *GroupSyncable { - return &GroupSyncable{ - GroupId: groupID, - SyncableId: channelID, - Type: GroupSyncableTypeChannel, - AutoAdd: autoAdd, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/guest_invite.go b/vendor/github.com/mattermost/mattermost-server/v5/model/guest_invite.go deleted file mode 100644 index ac803a5d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/guest_invite.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" -) - -type GuestsInvite struct { - Emails []string `json:"emails"` - Channels []string `json:"channels"` - Message string `json:"message"` -} - -// IsValid validates the user and returns an error if it isn't configured -// correctly. -func (i *GuestsInvite) IsValid() *AppError { - if len(i.Emails) == 0 { - return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.emails.app_error", nil, "", http.StatusBadRequest) - } - - for _, email := range i.Emails { - if len(email) > USER_EMAIL_MAX_LENGTH || email == "" || !IsValidEmail(email) { - return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.email.app_error", nil, "email="+email, http.StatusBadRequest) - } - } - - if len(i.Channels) == 0 { - return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channels.app_error", nil, "", http.StatusBadRequest) - } - - for _, channel := range i.Channels { - if len(channel) != 26 { - return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channel.app_error", nil, "channel="+channel, http.StatusBadRequest) - } - } - return nil -} - -// GuestsInviteFromJson will decode the input and return a GuestsInvite -func GuestsInviteFromJson(data io.Reader) *GuestsInvite { - var i *GuestsInvite - json.NewDecoder(data).Decode(&i) - return i -} - -func (i *GuestsInvite) ToJson() string { - b, _ := json.Marshal(i) - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/incoming_webhook.go b/vendor/github.com/mattermost/mattermost-server/v5/model/incoming_webhook.go deleted file mode 100644 index f8fffe20..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/incoming_webhook.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "regexp" -) - -const ( - DEFAULT_WEBHOOK_USERNAME = "webhook" -) - -type IncomingWebhook struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - TeamId string `json:"team_id"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - ChannelLocked bool `json:"channel_locked"` -} - -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 []*SlackAttachment `json:"attachments"` - Type string `json:"type"` - IconEmoji string `json:"icon_emoji"` -} - -func (o *IncomingWebhook) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func IncomingWebhookFromJson(data io.Reader) *IncomingWebhook { - var o *IncomingWebhook - json.NewDecoder(data).Decode(&o) - return o -} - -func IncomingWebhookListToJson(l []*IncomingWebhook) string { - b, _ := json.Marshal(l) - return string(b) -} - -func IncomingWebhookListFromJson(data io.Reader) []*IncomingWebhook { - var o []*IncomingWebhook - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *IncomingWebhook) IsValid() *AppError { - - if !IsValidId(o.Id) { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "", http.StatusBadRequest) - - } - - if o.CreateAt == 0 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidId(o.UserId) { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.ChannelId) { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.TeamId) { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.DisplayName) > 64 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Description) > 500 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Username) > 64 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.username.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.IconURL) > 1024 { - return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.icon_url.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *IncomingWebhook) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - o.CreateAt = GetMillis() - o.UpdateAt = o.CreateAt -} - -func (o *IncomingWebhook) PreUpdate() { - o.UpdateAt = GetMillis() -} - -// escapeControlCharsFromPayload escapes control chars (\n, \t) from a byte slice. -// Context: -// JSON strings are not supposed to contain control characters such as \n, \t, -// ... but some incoming webhooks might still send invalid JSON and we want to -// try to handle that. An example invalid JSON string from an incoming webhook -// might look like this (strings for both "text" and "fallback" attributes are -// invalid JSON strings because they contain unescaped newlines and tabs): -// `{ -// "text": "this is a test -// that contains a newline and tabs", -// "attachments": [ -// { -// "fallback": "Required plain-text summary of the attachment -// that contains a newline and tabs", -// "color": "#36a64f", -// ... -// "text": "Optional text that appears within the attachment -// that contains a newline and tabs", -// ... -// "thumb_url": "http://example.com/path/to/thumb.png" -// } -// ] -// }` -// This function will search for `"key": "value"` pairs, and escape \n, \t -// from the value. -func escapeControlCharsFromPayload(by []byte) []byte { - // we'll search for `"text": "..."` or `"fallback": "..."`, ... - keys := "text|fallback|pretext|author_name|title|value" - - // the regexp reads like this: - // (?s): this flag let . match \n (default is false) - // "(keys)": we search for the keys defined above - // \s*:\s*: followed by 0..n spaces/tabs, a colon then 0..n spaces/tabs - // ": a double-quote - // (\\"|[^"])*: any number of times the `\"` string or any char but a double-quote - // ": a double-quote - r := `(?s)"(` + keys + `)"\s*:\s*"(\\"|[^"])*"` - re := regexp.MustCompile(r) - - // the function that will escape \n and \t on the regexp matches - repl := func(b []byte) []byte { - if bytes.Contains(b, []byte("\n")) { - b = bytes.Replace(b, []byte("\n"), []byte("\\n"), -1) - } - if bytes.Contains(b, []byte("\t")) { - b = bytes.Replace(b, []byte("\t"), []byte("\\t"), -1) - } - - return b - } - - return re.ReplaceAllFunc(by, repl) -} - -func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) { - decoder := json.NewDecoder(bytes.NewReader(by)) - var o IncomingWebhookRequest - err := decoder.Decode(&o) - if err == nil { - return &o, nil - } - return nil, err -} - -func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *AppError) { - buf := new(bytes.Buffer) - buf.ReadFrom(data) - by := buf.Bytes() - - // Try to decode the JSON data. Only if it fails, try to escape control - // characters from the strings contained in the JSON data. - o, err := decodeIncomingWebhookRequest(by) - if err != nil { - o, err = decodeIncomingWebhookRequest(escapeControlCharsFromPayload(by)) - if err != nil { - return nil, NewAppError("IncomingWebhookRequestFromJson", "model.incoming_hook.parse_data.app_error", nil, err.Error(), http.StatusBadRequest) - } - } - - o.Attachments = StringifySlackFieldValue(o.Attachments) - - return o, nil -} - -func (o *IncomingWebhookRequest) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/initial_load.go b/vendor/github.com/mattermost/mattermost-server/v5/model/initial_load.go deleted file mode 100644 index c533faa5..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/initial_load.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type InitialLoad struct { - User *User `json:"user"` - TeamMembers []*TeamMember `json:"team_members"` - Teams []*Team `json:"teams"` - Preferences Preferences `json:"preferences"` - ClientCfg map[string]string `json:"client_cfg"` - LicenseCfg map[string]string `json:"license_cfg"` - NoAccounts bool `json:"no_accounts"` -} - -func (il *InitialLoad) ToJson() string { - b, _ := json.Marshal(il) - return string(b) -} - -func InitialLoadFromJson(data io.Reader) *InitialLoad { - var il *InitialLoad - json.NewDecoder(data).Decode(&il) - return il -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/integration_action.go b/vendor/github.com/mattermost/mattermost-server/v5/model/integration_action.go deleted file mode 100644 index 7124a7e3..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/integration_action.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - "crypto/rand" - "encoding/asn1" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "reflect" - "strconv" - "strings" -) - -const ( - POST_ACTION_TYPE_BUTTON = "button" - POST_ACTION_TYPE_SELECT = "select" - INTERACTIVE_DIALOG_TRIGGER_TIMEOUT_MILLISECONDS = 3000 -) - -var PostActionRetainPropKeys = []string{"from_webhook", "override_username", "override_icon_url"} - -type DoPostActionRequest struct { - SelectedOption string `json:"selected_option,omitempty"` - Cookie string `json:"cookie,omitempty"` -} - -type PostAction struct { - // A unique Action ID. If not set, generated automatically. - Id string `json:"id,omitempty"` - - // The type of the interactive element. Currently supported are - // "select" and "button". - Type string `json:"type,omitempty"` - - // The text on the button, or in the select placeholder. - Name string `json:"name,omitempty"` - - // If the action is disabled. - Disabled bool `json:"disabled,omitempty"` - - // Style defines a text and border style. - // Supported values are "default", "primary", "success", "good", "warning", "danger" - // and any hex color. - Style string `json:"style,omitempty"` - - // DataSource indicates the data source for the select action. If left - // empty, the select is populated from Options. Other supported values - // are "users" and "channels". - DataSource string `json:"data_source,omitempty"` - - // Options contains the values listed in a select dropdown on the post. - Options []*PostActionOptions `json:"options,omitempty"` - - // DefaultOption contains the option, if any, that will appear as the - // default selection in a select box. It has no effect when used with - // other types of actions. - DefaultOption string `json:"default_option,omitempty"` - - // Defines the interaction with the backend upon a user action. - // Integration contains Context, which is private plugin data; - // Integrations are stripped from Posts when they are sent to the - // client, or are encrypted in a Cookie. - Integration *PostActionIntegration `json:"integration,omitempty"` - Cookie string `json:"cookie,omitempty" db:"-"` -} - -func (p *PostAction) Equals(input *PostAction) bool { - if p.Id != input.Id { - return false - } - - if p.Type != input.Type { - return false - } - - if p.Name != input.Name { - return false - } - - if p.DataSource != input.DataSource { - return false - } - - if p.DefaultOption != input.DefaultOption { - return false - } - - if p.Cookie != input.Cookie { - return false - } - - // Compare PostActionOptions - if len(p.Options) != len(input.Options) { - return false - } - - for k := range p.Options { - if p.Options[k].Text != input.Options[k].Text { - return false - } - - if p.Options[k].Value != input.Options[k].Value { - return false - } - } - - // Compare PostActionIntegration - if p.Integration.URL != input.Integration.URL { - return false - } - - if len(p.Integration.Context) != len(input.Integration.Context) { - return false - } - - for key, value := range p.Integration.Context { - inputValue, ok := input.Integration.Context[key] - if !ok { - return false - } - - switch inputValue.(type) { - case string, bool, int, float64: - if value != inputValue { - return false - } - default: - if !reflect.DeepEqual(value, inputValue) { - return false - } - } - } - - return true -} - -// PostActionCookie is set by the server, serialized and encrypted into -// PostAction.Cookie. The clients should hold on to it, and include it with -// subsequent DoPostAction requests. This allows the server to access the -// action metadata even when it's not available in the database, for ephemeral -// posts. -type PostActionCookie struct { - Type string `json:"type,omitempty"` - PostId string `json:"post_id,omitempty"` - RootPostId string `json:"root_post_id,omitempty"` - ChannelId string `json:"channel_id,omitempty"` - DataSource string `json:"data_source,omitempty"` - Integration *PostActionIntegration `json:"integration,omitempty"` - RetainProps map[string]interface{} `json:"retain_props,omitempty"` - RemoveProps []string `json:"remove_props,omitempty"` -} - -type PostActionOptions struct { - Text string `json:"text"` - Value string `json:"value"` -} - -type PostActionIntegration struct { - URL string `json:"url,omitempty"` - Context map[string]interface{} `json:"context,omitempty"` -} - -type PostActionIntegrationRequest struct { - UserId string `json:"user_id"` - UserName string `json:"user_name"` - ChannelId string `json:"channel_id"` - ChannelName string `json:"channel_name"` - TeamId string `json:"team_id"` - TeamName string `json:"team_domain"` - PostId string `json:"post_id"` - TriggerId string `json:"trigger_id"` - Type string `json:"type"` - DataSource string `json:"data_source"` - Context map[string]interface{} `json:"context,omitempty"` -} - -type PostActionIntegrationResponse struct { - Update *Post `json:"update"` - EphemeralText string `json:"ephemeral_text"` - SkipSlackParsing bool `json:"skip_slack_parsing"` // Set to `true` to skip the Slack-compatibility handling of Text. -} - -type PostActionAPIResponse struct { - Status string `json:"status"` // needed to maintain backwards compatibility - TriggerId string `json:"trigger_id"` -} - -type Dialog struct { - CallbackId string `json:"callback_id"` - Title string `json:"title"` - IntroductionText string `json:"introduction_text"` - IconURL string `json:"icon_url"` - Elements []DialogElement `json:"elements"` - SubmitLabel string `json:"submit_label"` - NotifyOnCancel bool `json:"notify_on_cancel"` - State string `json:"state"` -} - -type DialogElement struct { - DisplayName string `json:"display_name"` - Name string `json:"name"` - Type string `json:"type"` - SubType string `json:"subtype"` - Default string `json:"default"` - Placeholder string `json:"placeholder"` - HelpText string `json:"help_text"` - Optional bool `json:"optional"` - MinLength int `json:"min_length"` - MaxLength int `json:"max_length"` - DataSource string `json:"data_source"` - Options []*PostActionOptions `json:"options"` -} - -type OpenDialogRequest struct { - TriggerId string `json:"trigger_id"` - URL string `json:"url"` - Dialog Dialog `json:"dialog"` -} - -type SubmitDialogRequest struct { - Type string `json:"type"` - URL string `json:"url,omitempty"` - CallbackId string `json:"callback_id"` - State string `json:"state"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - TeamId string `json:"team_id"` - Submission map[string]interface{} `json:"submission"` - Cancelled bool `json:"cancelled"` -} - -type SubmitDialogResponse struct { - Error string `json:"error,omitempty"` - Errors map[string]string `json:"errors,omitempty"` -} - -func GenerateTriggerId(userId string, s crypto.Signer) (string, string, *AppError) { - clientTriggerId := NewId() - triggerData := strings.Join([]string{clientTriggerId, userId, strconv.FormatInt(GetMillis(), 10)}, ":") + ":" - - h := crypto.SHA256 - sum := h.New() - sum.Write([]byte(triggerData)) - signature, err := s.Sign(rand.Reader, sum.Sum(nil), h) - if err != nil { - return "", "", NewAppError("GenerateTriggerId", "interactive_message.generate_trigger_id.signing_failed", nil, err.Error(), http.StatusInternalServerError) - } - - base64Sig := base64.StdEncoding.EncodeToString(signature) - - triggerId := base64.StdEncoding.EncodeToString([]byte(triggerData + base64Sig)) - return clientTriggerId, triggerId, nil -} - -func (r *PostActionIntegrationRequest) GenerateTriggerId(s crypto.Signer) (string, string, *AppError) { - clientTriggerId, triggerId, err := GenerateTriggerId(r.UserId, s) - if err != nil { - return "", "", err - } - - r.TriggerId = triggerId - return clientTriggerId, triggerId, nil -} - -func DecodeAndVerifyTriggerId(triggerId string, s *ecdsa.PrivateKey) (string, string, *AppError) { - triggerIdBytes, err := base64.StdEncoding.DecodeString(triggerId) - if err != nil { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.base64_decode_failed", nil, err.Error(), http.StatusBadRequest) - } - - split := strings.Split(string(triggerIdBytes), ":") - if len(split) != 4 { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.missing_data", nil, "", http.StatusBadRequest) - } - - clientTriggerId := split[0] - userId := split[1] - timestampStr := split[2] - timestamp, _ := strconv.ParseInt(timestampStr, 10, 64) - - now := GetMillis() - if now-timestamp > INTERACTIVE_DIALOG_TRIGGER_TIMEOUT_MILLISECONDS { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.expired", map[string]interface{}{"Seconds": INTERACTIVE_DIALOG_TRIGGER_TIMEOUT_MILLISECONDS / 1000}, "", http.StatusBadRequest) - } - - signature, err := base64.StdEncoding.DecodeString(split[3]) - if err != nil { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.base64_decode_failed_signature", nil, err.Error(), http.StatusBadRequest) - } - - var esig struct { - R, S *big.Int - } - - if _, err := asn1.Unmarshal(signature, &esig); err != nil { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.signature_decode_failed", nil, err.Error(), http.StatusBadRequest) - } - - triggerData := strings.Join([]string{clientTriggerId, userId, timestampStr}, ":") + ":" - - h := crypto.SHA256 - sum := h.New() - sum.Write([]byte(triggerData)) - - if !ecdsa.Verify(&s.PublicKey, sum.Sum(nil), esig.R, esig.S) { - return "", "", NewAppError("DecodeAndVerifyTriggerId", "interactive_message.decode_trigger_id.verify_signature_failed", nil, "", http.StatusBadRequest) - } - - return clientTriggerId, userId, nil -} - -func (r *OpenDialogRequest) DecodeAndVerifyTriggerId(s *ecdsa.PrivateKey) (string, string, *AppError) { - return DecodeAndVerifyTriggerId(r.TriggerId, s) -} - -func (r *PostActionIntegrationRequest) ToJson() []byte { - b, _ := json.Marshal(r) - return b -} - -func PostActionIntegrationRequestFromJson(data io.Reader) *PostActionIntegrationRequest { - var o *PostActionIntegrationRequest - err := json.NewDecoder(data).Decode(&o) - if err != nil { - return nil - } - return o -} - -func (r *PostActionIntegrationResponse) ToJson() []byte { - b, _ := json.Marshal(r) - return b -} - -func PostActionIntegrationResponseFromJson(data io.Reader) *PostActionIntegrationResponse { - var o *PostActionIntegrationResponse - err := json.NewDecoder(data).Decode(&o) - if err != nil { - return nil - } - return o -} - -func SubmitDialogRequestFromJson(data io.Reader) *SubmitDialogRequest { - var o *SubmitDialogRequest - err := json.NewDecoder(data).Decode(&o) - if err != nil { - return nil - } - return o -} - -func (r *SubmitDialogRequest) ToJson() []byte { - b, _ := json.Marshal(r) - return b -} - -func SubmitDialogResponseFromJson(data io.Reader) *SubmitDialogResponse { - var o *SubmitDialogResponse - err := json.NewDecoder(data).Decode(&o) - if err != nil { - return nil - } - return o -} - -func (r *SubmitDialogResponse) ToJson() []byte { - b, _ := json.Marshal(r) - return b -} - -func (o *Post) StripActionIntegrations() { - attachments := o.Attachments() - if o.GetProp("attachments") != nil { - o.AddProp("attachments", attachments) - } - for _, attachment := range attachments { - for _, action := range attachment.Actions { - action.Integration = nil - } - } -} - -func (o *Post) GetAction(id string) *PostAction { - for _, attachment := range o.Attachments() { - for _, action := range attachment.Actions { - if action != nil && action.Id == id { - return action - } - } - } - return nil -} - -func (o *Post) GenerateActionIds() { - if o.GetProp("attachments") != nil { - o.AddProp("attachments", o.Attachments()) - } - if attachments, ok := o.GetProp("attachments").([]*SlackAttachment); ok { - for _, attachment := range attachments { - for _, action := range attachment.Actions { - if action != nil && action.Id == "" { - action.Id = NewId() - } - } - } - } -} - -func AddPostActionCookies(o *Post, secret []byte) *Post { - p := o.Clone() - - // retainedProps carry over their value from the old post, including no value - retainProps := map[string]interface{}{} - removeProps := []string{} - for _, key := range PostActionRetainPropKeys { - value, ok := p.GetProps()[key] - if ok { - retainProps[key] = value - } else { - removeProps = append(removeProps, key) - } - } - - attachments := p.Attachments() - for _, attachment := range attachments { - for _, action := range attachment.Actions { - c := &PostActionCookie{ - Type: action.Type, - ChannelId: p.ChannelId, - DataSource: action.DataSource, - Integration: action.Integration, - RetainProps: retainProps, - RemoveProps: removeProps, - } - - c.PostId = p.Id - if p.RootId == "" { - c.RootPostId = p.Id - } else { - c.RootPostId = p.RootId - } - - b, _ := json.Marshal(c) - action.Cookie, _ = encryptPostActionCookie(string(b), secret) - } - } - - return p -} - -func encryptPostActionCookie(plain string, secret []byte) (string, error) { - if len(secret) == 0 { - return plain, nil - } - - block, err := aes.NewCipher(secret) - if err != nil { - return "", err - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - nonce := make([]byte, aesgcm.NonceSize()) - _, err = io.ReadFull(rand.Reader, nonce) - if err != nil { - return "", err - } - - sealed := aesgcm.Seal(nil, nonce, []byte(plain), nil) - - combined := append(nonce, sealed...) - encoded := make([]byte, base64.StdEncoding.EncodedLen(len(combined))) - base64.StdEncoding.Encode(encoded, combined) - - return string(encoded), nil -} - -func DecryptPostActionCookie(encoded string, secret []byte) (string, error) { - if len(secret) == 0 { - return encoded, nil - } - - block, err := aes.NewCipher(secret) - if err != nil { - return "", err - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - decoded := make([]byte, base64.StdEncoding.DecodedLen(len(encoded))) - n, err := base64.StdEncoding.Decode(decoded, []byte(encoded)) - if err != nil { - return "", err - } - decoded = decoded[:n] - - nonceSize := aesgcm.NonceSize() - if len(decoded) < nonceSize { - return "", fmt.Errorf("cookie too short") - } - - nonce, decoded := decoded[:nonceSize], decoded[nonceSize:] - plain, err := aesgcm.Open(nil, nonce, decoded, nil) - if err != nil { - return "", err - } - - return string(plain), nil -} - -func DoPostActionRequestFromJson(data io.Reader) *DoPostActionRequest { - var o *DoPostActionRequest - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/integrity.go b/vendor/github.com/mattermost/mattermost-server/v5/model/integrity.go deleted file mode 100644 index 744ad07c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/integrity.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "errors" -) - -type OrphanedRecord struct { - ParentId *string `json:"parent_id"` - ChildId *string `json:"child_id"` -} - -type RelationalIntegrityCheckData struct { - ParentName string `json:"parent_name"` - ChildName string `json:"child_name"` - ParentIdAttr string `json:"parent_id_attr"` - ChildIdAttr string `json:"child_id_attr"` - Records []OrphanedRecord `json:"records"` -} - -type IntegrityCheckResult struct { - Data interface{} `json:"data"` - Err error `json:"err"` -} - -func (r *IntegrityCheckResult) UnmarshalJSON(b []byte) error { - var data map[string]interface{} - if err := json.Unmarshal(b, &data); err != nil { - return err - } - if d, ok := data["data"]; ok && d != nil { - var rdata RelationalIntegrityCheckData - m := d.(map[string]interface{}) - rdata.ParentName = m["parent_name"].(string) - rdata.ChildName = m["child_name"].(string) - rdata.ParentIdAttr = m["parent_id_attr"].(string) - rdata.ChildIdAttr = m["child_id_attr"].(string) - for _, recData := range m["records"].([]interface{}) { - var record OrphanedRecord - m := recData.(map[string]interface{}) - if val := m["parent_id"]; val != nil { - record.ParentId = NewString(val.(string)) - } - if val := m["child_id"]; val != nil { - record.ChildId = NewString(val.(string)) - } - rdata.Records = append(rdata.Records, record) - } - r.Data = rdata - } - if err, ok := data["err"]; ok && err != nil { - r.Err = errors.New(data["err"].(string)) - } - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/job.go b/vendor/github.com/mattermost/mattermost-server/v5/model/job.go deleted file mode 100644 index 78d5a4ff..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/job.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "time" -) - -const ( - JOB_TYPE_DATA_RETENTION = "data_retention" - JOB_TYPE_MESSAGE_EXPORT = "message_export" - JOB_TYPE_ELASTICSEARCH_POST_INDEXING = "elasticsearch_post_indexing" - JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION = "elasticsearch_post_aggregation" - JOB_TYPE_BLEVE_POST_INDEXING = "bleve_post_indexing" - JOB_TYPE_LDAP_SYNC = "ldap_sync" - JOB_TYPE_MIGRATIONS = "migrations" - JOB_TYPE_PLUGINS = "plugins" - JOB_TYPE_EXPIRY_NOTIFY = "expiry_notify" - JOB_TYPE_PRODUCT_NOTICES = "product_notices" - JOB_TYPE_ACTIVE_USERS = "active_users" - JOB_TYPE_IMPORT_PROCESS = "import_process" - JOB_TYPE_IMPORT_DELETE = "import_delete" - JOB_TYPE_EXPORT_PROCESS = "export_process" - JOB_TYPE_EXPORT_DELETE = "export_delete" - JOB_TYPE_CLOUD = "cloud" - JOB_TYPE_RESEND_INVITATION_EMAIL = "resend_invitation_email" - - JOB_STATUS_PENDING = "pending" - JOB_STATUS_IN_PROGRESS = "in_progress" - JOB_STATUS_SUCCESS = "success" - JOB_STATUS_ERROR = "error" - JOB_STATUS_CANCEL_REQUESTED = "cancel_requested" - JOB_STATUS_CANCELED = "canceled" - JOB_STATUS_WARNING = "warning" -) - -var ALL_JOB_TYPES = [...]string{ - JOB_TYPE_DATA_RETENTION, - JOB_TYPE_MESSAGE_EXPORT, - JOB_TYPE_ELASTICSEARCH_POST_INDEXING, - JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION, - JOB_TYPE_BLEVE_POST_INDEXING, - JOB_TYPE_LDAP_SYNC, - JOB_TYPE_MIGRATIONS, - JOB_TYPE_PLUGINS, - JOB_TYPE_EXPIRY_NOTIFY, - JOB_TYPE_PRODUCT_NOTICES, - JOB_TYPE_ACTIVE_USERS, - JOB_TYPE_IMPORT_PROCESS, - JOB_TYPE_IMPORT_DELETE, - JOB_TYPE_EXPORT_PROCESS, - JOB_TYPE_EXPORT_DELETE, - JOB_TYPE_CLOUD, -} - -type Job struct { - Id string `json:"id"` - Type string `json:"type"` - Priority int64 `json:"priority"` - CreateAt int64 `json:"create_at"` - StartAt int64 `json:"start_at"` - LastActivityAt int64 `json:"last_activity_at"` - Status string `json:"status"` - Progress int64 `json:"progress"` - Data map[string]string `json:"data"` -} - -func (j *Job) IsValid() *AppError { - if !IsValidId(j.Id) { - return NewAppError("Job.IsValid", "model.job.is_valid.id.app_error", nil, "id="+j.Id, http.StatusBadRequest) - } - - if j.CreateAt == 0 { - return NewAppError("Job.IsValid", "model.job.is_valid.create_at.app_error", nil, "id="+j.Id, http.StatusBadRequest) - } - - switch j.Type { - case JOB_TYPE_DATA_RETENTION: - case JOB_TYPE_ELASTICSEARCH_POST_INDEXING: - case JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION: - case JOB_TYPE_BLEVE_POST_INDEXING: - case JOB_TYPE_LDAP_SYNC: - case JOB_TYPE_MESSAGE_EXPORT: - case JOB_TYPE_MIGRATIONS: - case JOB_TYPE_PLUGINS: - case JOB_TYPE_PRODUCT_NOTICES: - case JOB_TYPE_EXPIRY_NOTIFY: - case JOB_TYPE_ACTIVE_USERS: - case JOB_TYPE_IMPORT_PROCESS: - case JOB_TYPE_IMPORT_DELETE: - case JOB_TYPE_EXPORT_PROCESS: - case JOB_TYPE_EXPORT_DELETE: - case JOB_TYPE_CLOUD: - case JOB_TYPE_RESEND_INVITATION_EMAIL: - default: - return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest) - } - - switch j.Status { - case JOB_STATUS_PENDING: - case JOB_STATUS_IN_PROGRESS: - case JOB_STATUS_SUCCESS: - case JOB_STATUS_ERROR: - case JOB_STATUS_CANCEL_REQUESTED: - case JOB_STATUS_CANCELED: - default: - return NewAppError("Job.IsValid", "model.job.is_valid.status.app_error", nil, "id="+j.Id, http.StatusBadRequest) - } - - return nil -} - -func (j *Job) ToJson() string { - b, _ := json.Marshal(j) - return string(b) -} - -func JobFromJson(data io.Reader) *Job { - var job Job - if err := json.NewDecoder(data).Decode(&job); err == nil { - return &job - } - return nil -} - -func JobsToJson(jobs []*Job) string { - b, _ := json.Marshal(jobs) - return string(b) -} - -func JobsFromJson(data io.Reader) []*Job { - var jobs []*Job - if err := json.NewDecoder(data).Decode(&jobs); err == nil { - return jobs - } - return nil -} - -func (j *Job) DataToJson() string { - b, _ := json.Marshal(j.Data) - return string(b) -} - -type Worker interface { - Run() - Stop() - JobChannel() chan<- Job -} - -type Scheduler interface { - Name() string - JobType() string - Enabled(cfg *Config) bool - NextScheduleTime(cfg *Config, now time.Time, pendingJobs bool, lastSuccessfulJob *Job) *time.Time - ScheduleJob(cfg *Config, pendingJobs bool, lastSuccessfulJob *Job) (*Job, *AppError) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/ldap.go b/vendor/github.com/mattermost/mattermost-server/v5/model/ldap.go deleted file mode 100644 index 1262dfb8..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/ldap.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - USER_AUTH_SERVICE_LDAP = "ldap" - LDAP_PUBLIC_CERTIFICATE_NAME = "ldap-public.crt" - LDAP_PRIVATE_KEY_NAME = "ldap-private.key" -) diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/license.go b/vendor/github.com/mattermost/mattermost-server/v5/model/license.go deleted file mode 100644 index ab9e481a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/license.go +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "time" -) - -const ( - EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error" - INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" - LICENSE_GRACE_PERIOD = 1000 * 60 * 60 * 24 * 10 //10 days - LICENSE_RENEWAL_LINK = "https://mattermost.com/renew/" -) - -const ( - SIXTY_DAYS = 60 - FIFTY_EIGHT = 58 - LICENSE_UP_FOR_RENEWAL_EMAIL_SENT = "LicenseUpForRenewalEmailSent" -) - -var ( - trialDuration = 30*(time.Hour*24) + (time.Hour * 8) // 720 hours (30 days) + 8 hours is trial license duration - adminTrialDuration = 30*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 720 hours (30 days) + 23 hours, 59 mins and 59 seconds - - // a sanctioned trial's duration is either more than the upper bound, - // or less than the lower bound - sanctionedTrialDurationLowerBound = 31*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 744 hours (31 days) + 23 hours, 59 mins and 59 seconds - sanctionedTrialDurationUpperBound = 29*(time.Hour*24) + (time.Hour * 23) + (time.Minute * 59) + (time.Second * 59) // 696 hours (29 days) + 23 hours, 59 mins and 59 seconds -) - -type LicenseRecord struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - Bytes string `json:"-"` -} - -type License struct { - Id string `json:"id"` - IssuedAt int64 `json:"issued_at"` - StartsAt int64 `json:"starts_at"` - ExpiresAt int64 `json:"expires_at"` - Customer *Customer `json:"customer"` - Features *Features `json:"features"` - SkuName string `json:"sku_name"` - SkuShortName string `json:"sku_short_name"` - IsTrial bool `json:"is_trial"` -} - -type Customer struct { - Id string `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Company string `json:"company"` -} - -type TrialLicenseRequest struct { - ServerID string `json:"server_id"` - Email string `json:"email"` - Name string `json:"name"` - SiteURL string `json:"site_url"` - SiteName string `json:"site_name"` - Users int `json:"users"` - TermsAccepted bool `json:"terms_accepted"` - ReceiveEmailsAccepted bool `json:"receive_emails_accepted"` -} - -func (tlr *TrialLicenseRequest) ToJson() string { - b, _ := json.Marshal(tlr) - return string(b) -} - -type Features struct { - Users *int `json:"users"` - LDAP *bool `json:"ldap"` - LDAPGroups *bool `json:"ldap_groups"` - MFA *bool `json:"mfa"` - GoogleOAuth *bool `json:"google_oauth"` - Office365OAuth *bool `json:"office365_oauth"` - OpenId *bool `json:"openid"` - Compliance *bool `json:"compliance"` - Cluster *bool `json:"cluster"` - Metrics *bool `json:"metrics"` - MHPNS *bool `json:"mhpns"` - SAML *bool `json:"saml"` - Elasticsearch *bool `json:"elastic_search"` - Announcement *bool `json:"announcement"` - ThemeManagement *bool `json:"theme_management"` - EmailNotificationContents *bool `json:"email_notification_contents"` - DataRetention *bool `json:"data_retention"` - MessageExport *bool `json:"message_export"` - CustomPermissionsSchemes *bool `json:"custom_permissions_schemes"` - CustomTermsOfService *bool `json:"custom_terms_of_service"` - GuestAccounts *bool `json:"guest_accounts"` - GuestAccountsPermissions *bool `json:"guest_accounts_permissions"` - IDLoadedPushNotifications *bool `json:"id_loaded"` - LockTeammateNameDisplay *bool `json:"lock_teammate_name_display"` - EnterprisePlugins *bool `json:"enterprise_plugins"` - AdvancedLogging *bool `json:"advanced_logging"` - Cloud *bool `json:"cloud"` - SharedChannels *bool `json:"shared_channels"` - RemoteClusterService *bool `json:"remote_cluster_service"` - - // after we enabled more features we'll need to control them with this - FutureFeatures *bool `json:"future_features"` -} - -func (f *Features) ToMap() map[string]interface{} { - return map[string]interface{}{ - "ldap": *f.LDAP, - "ldap_groups": *f.LDAPGroups, - "mfa": *f.MFA, - "google": *f.GoogleOAuth, - "office365": *f.Office365OAuth, - "openid": *f.OpenId, - "compliance": *f.Compliance, - "cluster": *f.Cluster, - "metrics": *f.Metrics, - "mhpns": *f.MHPNS, - "saml": *f.SAML, - "elastic_search": *f.Elasticsearch, - "email_notification_contents": *f.EmailNotificationContents, - "data_retention": *f.DataRetention, - "message_export": *f.MessageExport, - "custom_permissions_schemes": *f.CustomPermissionsSchemes, - "guest_accounts": *f.GuestAccounts, - "guest_accounts_permissions": *f.GuestAccountsPermissions, - "id_loaded": *f.IDLoadedPushNotifications, - "lock_teammate_name_display": *f.LockTeammateNameDisplay, - "enterprise_plugins": *f.EnterprisePlugins, - "advanced_logging": *f.AdvancedLogging, - "cloud": *f.Cloud, - "shared_channels": *f.SharedChannels, - "remote_cluster_service": *f.RemoteClusterService, - "future": *f.FutureFeatures, - } -} - -func (f *Features) SetDefaults() { - if f.FutureFeatures == nil { - f.FutureFeatures = NewBool(true) - } - - if f.Users == nil { - f.Users = NewInt(0) - } - - if f.LDAP == nil { - f.LDAP = NewBool(*f.FutureFeatures) - } - - if f.LDAPGroups == nil { - f.LDAPGroups = NewBool(*f.FutureFeatures) - } - - if f.MFA == nil { - f.MFA = NewBool(*f.FutureFeatures) - } - - if f.GoogleOAuth == nil { - f.GoogleOAuth = NewBool(*f.FutureFeatures) - } - - if f.Office365OAuth == nil { - f.Office365OAuth = NewBool(*f.FutureFeatures) - } - - if f.OpenId == nil { - f.OpenId = NewBool(*f.FutureFeatures) - } - - if f.Compliance == nil { - f.Compliance = NewBool(*f.FutureFeatures) - } - - if f.Cluster == nil { - f.Cluster = NewBool(*f.FutureFeatures) - } - - if f.Metrics == nil { - f.Metrics = NewBool(*f.FutureFeatures) - } - - if f.MHPNS == nil { - f.MHPNS = NewBool(*f.FutureFeatures) - } - - if f.SAML == nil { - f.SAML = NewBool(*f.FutureFeatures) - } - - if f.Elasticsearch == nil { - f.Elasticsearch = NewBool(*f.FutureFeatures) - } - - if f.Announcement == nil { - f.Announcement = NewBool(true) - } - - if f.ThemeManagement == nil { - f.ThemeManagement = NewBool(true) - } - - if f.EmailNotificationContents == nil { - f.EmailNotificationContents = NewBool(*f.FutureFeatures) - } - - if f.DataRetention == nil { - f.DataRetention = NewBool(*f.FutureFeatures) - } - - if f.MessageExport == nil { - f.MessageExport = NewBool(*f.FutureFeatures) - } - - if f.CustomPermissionsSchemes == nil { - f.CustomPermissionsSchemes = NewBool(*f.FutureFeatures) - } - - if f.GuestAccounts == nil { - f.GuestAccounts = NewBool(*f.FutureFeatures) - } - - if f.GuestAccountsPermissions == nil { - f.GuestAccountsPermissions = NewBool(*f.FutureFeatures) - } - - if f.CustomTermsOfService == nil { - f.CustomTermsOfService = NewBool(*f.FutureFeatures) - } - - if f.IDLoadedPushNotifications == nil { - f.IDLoadedPushNotifications = NewBool(*f.FutureFeatures) - } - - if f.LockTeammateNameDisplay == nil { - f.LockTeammateNameDisplay = NewBool(*f.FutureFeatures) - } - - if f.EnterprisePlugins == nil { - f.EnterprisePlugins = NewBool(*f.FutureFeatures) - } - - if f.AdvancedLogging == nil { - f.AdvancedLogging = NewBool(*f.FutureFeatures) - } - - if f.Cloud == nil { - f.Cloud = NewBool(false) - } - - if f.SharedChannels == nil { - f.SharedChannels = NewBool(*f.FutureFeatures) - } - - if f.RemoteClusterService == nil { - f.RemoteClusterService = NewBool(*f.FutureFeatures) - } -} - -func (l *License) IsExpired() bool { - return l.ExpiresAt < GetMillis() -} - -func (l *License) IsPastGracePeriod() bool { - timeDiff := GetMillis() - l.ExpiresAt - return timeDiff > LICENSE_GRACE_PERIOD -} - -func (l *License) IsWithinExpirationPeriod() bool { - days := l.DaysToExpiration() - return days <= SIXTY_DAYS && days >= FIFTY_EIGHT -} - -func (l *License) DaysToExpiration() int { - dif := l.ExpiresAt - GetMillis() - d, _ := time.ParseDuration(fmt.Sprint(dif) + "ms") - days := d.Hours() / 24 - return int(days) -} - -func (l *License) IsStarted() bool { - return l.StartsAt < GetMillis() -} - -func (l *License) ToJson() string { - b, _ := json.Marshal(l) - return string(b) -} - -func (l *License) IsTrialLicense() bool { - return l.IsTrial || (l.ExpiresAt-l.StartsAt) == trialDuration.Milliseconds() || (l.ExpiresAt-l.StartsAt) == adminTrialDuration.Milliseconds() -} - -func (l *License) IsSanctionedTrial() bool { - duration := l.ExpiresAt - l.StartsAt - - return l.IsTrialLicense() && - (duration >= sanctionedTrialDurationLowerBound.Milliseconds() || duration <= sanctionedTrialDurationUpperBound.Milliseconds()) -} - -// NewTestLicense returns a license that expires in the future and has the given features. -func NewTestLicense(features ...string) *License { - ret := &License{ - ExpiresAt: GetMillis() + 90*24*60*60*1000, - Customer: &Customer{}, - Features: &Features{}, - } - ret.Features.SetDefaults() - - featureMap := map[string]bool{} - for _, feature := range features { - featureMap[feature] = true - } - featureJson, _ := json.Marshal(featureMap) - json.Unmarshal(featureJson, &ret.Features) - - return ret -} - -func LicenseFromJson(data io.Reader) *License { - var o *License - json.NewDecoder(data).Decode(&o) - return o -} - -func (lr *LicenseRecord) IsValid() *AppError { - if !IsValidId(lr.Id) { - return NewAppError("LicenseRecord.IsValid", "model.license_record.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if lr.CreateAt == 0 { - return NewAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - if lr.Bytes == "" || len(lr.Bytes) > 10000 { - return NewAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (lr *LicenseRecord) PreSave() { - lr.CreateAt = GetMillis() -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/link_metadata.go b/vendor/github.com/mattermost/mattermost-server/v5/model/link_metadata.go deleted file mode 100644 index 6c3e0bd8..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/link_metadata.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "hash/fnv" - "net/http" - "time" - "unicode/utf8" - - "github.com/dyatlov/go-opengraph/opengraph" -) - -const ( - LINK_METADATA_TYPE_IMAGE LinkMetadataType = "image" - LINK_METADATA_TYPE_NONE LinkMetadataType = "none" - LINK_METADATA_TYPE_OPENGRAPH LinkMetadataType = "opengraph" - MAX_IMAGES int = 5 -) - -type LinkMetadataType string - -// LinkMetadata stores arbitrary data about a link posted in a message. This includes dimensions of linked images -// and OpenGraph metadata. -type LinkMetadata struct { - // Hash is a value computed from the URL and Timestamp for use as a primary key in the database. - Hash int64 - - URL string - Timestamp int64 - Type LinkMetadataType - - // Data is the actual metadata for the link. It should contain data of one of the following types: - // - *model.PostImage if the linked content is an image - // - *opengraph.OpenGraph if the linked content is an HTML document - // - nil if the linked content has no metadata - Data interface{} -} - -// truncateText ensure string is 300 chars, truncate and add ellipsis -// if it was bigger. -func truncateText(original string) string { - if utf8.RuneCountInString(original) > 300 { - return fmt.Sprintf("%.300s[...]", original) - } - return original -} - -func firstNImages(images []*opengraph.Image, maxImages int) []*opengraph.Image { - if maxImages < 0 { // dont break stuff, if it's weird, go for sane defaults - maxImages = MAX_IMAGES - } - numImages := len(images) - if numImages > maxImages { - return images[0:maxImages] - } - return images -} - -// TruncateOpenGraph ensure OpenGraph metadata doesn't grow too big by -// shortening strings, trimming fields and reducing the number of -// images. -func TruncateOpenGraph(ogdata *opengraph.OpenGraph) *opengraph.OpenGraph { - if ogdata != nil { - empty := &opengraph.OpenGraph{} - ogdata.Title = truncateText(ogdata.Title) - ogdata.Description = truncateText(ogdata.Description) - ogdata.SiteName = truncateText(ogdata.SiteName) - ogdata.Article = empty.Article - ogdata.Book = empty.Book - ogdata.Profile = empty.Profile - ogdata.Determiner = empty.Determiner - ogdata.Locale = empty.Locale - ogdata.LocalesAlternate = empty.LocalesAlternate - ogdata.Images = firstNImages(ogdata.Images, MAX_IMAGES) - ogdata.Audios = empty.Audios - ogdata.Videos = empty.Videos - } - return ogdata -} - -func (o *LinkMetadata) PreSave() { - o.Hash = GenerateLinkMetadataHash(o.URL, o.Timestamp) -} - -func (o *LinkMetadata) IsValid() *AppError { - if o.URL == "" { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.url.app_error", nil, "", http.StatusBadRequest) - } - - if o.Timestamp == 0 || !isRoundedToNearestHour(o.Timestamp) { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.timestamp.app_error", nil, "", http.StatusBadRequest) - } - - switch o.Type { - case LINK_METADATA_TYPE_IMAGE: - if o.Data == nil { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.data.app_error", nil, "", http.StatusBadRequest) - } - - if _, ok := o.Data.(*PostImage); !ok { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.data_type.app_error", nil, "", http.StatusBadRequest) - } - case LINK_METADATA_TYPE_NONE: - if o.Data != nil { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.data_type.app_error", nil, "", http.StatusBadRequest) - } - case LINK_METADATA_TYPE_OPENGRAPH: - if o.Data == nil { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.data.app_error", nil, "", http.StatusBadRequest) - } - - if _, ok := o.Data.(*opengraph.OpenGraph); !ok { - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.data_type.app_error", nil, "", http.StatusBadRequest) - } - default: - return NewAppError("LinkMetadata.IsValid", "model.link_metadata.is_valid.type.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -// DeserializeDataToConcreteType converts o.Data from JSON into properly structured data. This is intended to be used -// after getting a LinkMetadata object that has been stored in the database. -func (o *LinkMetadata) DeserializeDataToConcreteType() error { - var b []byte - switch t := o.Data.(type) { - case []byte: - // MySQL uses a byte slice for JSON - b = t - case string: - // Postgres uses a string for JSON - b = []byte(t) - } - - if b == nil { - // Data doesn't need to be fixed - return nil - } - - var data interface{} - var err error - - switch o.Type { - case LINK_METADATA_TYPE_IMAGE: - image := &PostImage{} - - err = json.Unmarshal(b, &image) - - data = image - case LINK_METADATA_TYPE_OPENGRAPH: - og := &opengraph.OpenGraph{} - - json.Unmarshal(b, &og) - - data = og - } - - if err != nil { - return err - } - - o.Data = data - - return nil -} - -// FloorToNearestHour takes a timestamp (in milliseconds) and returns it rounded to the previous hour in UTC. -func FloorToNearestHour(ms int64) int64 { - t := time.Unix(0, ms*int64(1000*1000)).UTC() - - return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, time.UTC).UnixNano() / int64(time.Millisecond) -} - -// isRoundedToNearestHour returns true if the given timestamp (in milliseconds) has been rounded to the nearest hour in UTC. -func isRoundedToNearestHour(ms int64) bool { - return FloorToNearestHour(ms) == ms -} - -// GenerateLinkMetadataHash generates a unique hash for a given URL and timestamp for use as a database key. -func GenerateLinkMetadataHash(url string, timestamp int64) int64 { - hash := fnv.New32() - - // Note that we ignore write errors here because the Hash interface says that its Write will never return an error - binary.Write(hash, binary.LittleEndian, timestamp) - hash.Write([]byte(url)) - - return int64(hash.Sum32()) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/manifest.go b/vendor/github.com/mattermost/mattermost-server/v5/model/manifest.go deleted file mode 100644 index 3fba0890..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/manifest.go +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/blang/semver" - "github.com/pkg/errors" - "gopkg.in/yaml.v2" -) - -type PluginOption struct { - // The display name for the option. - DisplayName string `json:"display_name" yaml:"display_name"` - - // The string value for the option. - Value string `json:"value" yaml:"value"` -} - -type PluginSettingType int - -const ( - Bool PluginSettingType = iota - Dropdown - Generated - Radio - Text - LongText - Number - Username - Custom -) - -type PluginSetting struct { - // The key that the setting will be assigned to in the configuration file. - Key string `json:"key" yaml:"key"` - - // The display name for the setting. - DisplayName string `json:"display_name" yaml:"display_name"` - - // The type of the setting. - // - // "bool" will result in a boolean true or false setting. - // - // "dropdown" will result in a string setting that allows the user to select from a list of - // pre-defined options. - // - // "generated" will result in a string setting that is set to a random, cryptographically secure - // string. - // - // "radio" will result in a string setting that allows the user to select from a short selection - // of pre-defined options. - // - // "text" will result in a string setting that can be typed in manually. - // - // "longtext" will result in a multi line string that can be typed in manually. - // - // "number" will result in in integer setting that can be typed in manually. - // - // "username" will result in a text setting that will autocomplete to a username. - // - // "custom" will result in a custom defined setting and will load the custom component registered for the Web App System Console. - Type string `json:"type" yaml:"type"` - - // The help text to display to the user. Supports Markdown formatting. - HelpText string `json:"help_text" yaml:"help_text"` - - // The help text to display alongside the "Regenerate" button for settings of the "generated" type. - RegenerateHelpText string `json:"regenerate_help_text,omitempty" yaml:"regenerate_help_text,omitempty"` - - // The placeholder to display for "generated", "text", "longtext", "number" and "username" types when blank. - Placeholder string `json:"placeholder" yaml:"placeholder"` - - // The default value of the setting. - Default interface{} `json:"default" yaml:"default"` - - // For "radio" or "dropdown" settings, this is the list of pre-defined options that the user can choose - // from. - Options []*PluginOption `json:"options,omitempty" yaml:"options,omitempty"` -} - -type PluginSettingsSchema struct { - // Optional text to display above the settings. Supports Markdown formatting. - Header string `json:"header" yaml:"header"` - - // Optional text to display below the settings. Supports Markdown formatting. - Footer string `json:"footer" yaml:"footer"` - - // A list of setting definitions. - Settings []*PluginSetting `json:"settings" yaml:"settings"` -} - -// The plugin manifest defines the metadata required to load and present your plugin. The manifest -// file should be named plugin.json or plugin.yaml and placed in the top of your -// plugin bundle. -// -// Example plugin.json: -// -// -// { -// "id": "com.mycompany.myplugin", -// "name": "My Plugin", -// "description": "This is my plugin", -// "homepage_url": "https://example.com", -// "support_url": "https://example.com/support", -// "release_notes_url": "https://example.com/releases/v0.0.1", -// "icon_path": "assets/logo.svg", -// "version": "0.1.0", -// "min_server_version": "5.6.0", -// "server": { -// "executables": { -// "linux-amd64": "server/dist/plugin-linux-amd64", -// "darwin-amd64": "server/dist/plugin-darwin-amd64", -// "windows-amd64": "server/dist/plugin-windows-amd64.exe" -// } -// }, -// "webapp": { -// "bundle_path": "webapp/dist/main.js" -// }, -// "settings_schema": { -// "header": "Some header text", -// "footer": "Some footer text", -// "settings": [{ -// "key": "someKey", -// "display_name": "Enable Extra Feature", -// "type": "bool", -// "help_text": "When true, an extra feature will be enabled!", -// "default": "false" -// }] -// }, -// "props": { -// "someKey": "someData" -// } -// } -type Manifest struct { - // The id is a globally unique identifier that represents your plugin. Ids must be at least - // 3 characters, at most 190 characters and must match ^[a-zA-Z0-9-_\.]+$. - // Reverse-DNS notation using a name you control is a good option, e.g. "com.mycompany.myplugin". - Id string `json:"id" yaml:"id"` - - // The name to be displayed for the plugin. - Name string `json:"name" yaml:"name"` - - // A description of what your plugin is and does. - Description string `json:"description,omitempty" yaml:"description,omitempty"` - - // HomepageURL is an optional link to learn more about the plugin. - HomepageURL string `json:"homepage_url,omitempty" yaml:"homepage_url,omitempty"` - - // SupportURL is an optional URL where plugin issues can be reported. - SupportURL string `json:"support_url,omitempty" yaml:"support_url,omitempty"` - - // ReleaseNotesURL is an optional URL where a changelog for the release can be found. - ReleaseNotesURL string `json:"release_notes_url,omitempty" yaml:"release_notes_url,omitempty"` - - // A relative file path in the bundle that points to the plugins svg icon for use with the Plugin Marketplace. - // This should be relative to the root of your bundle and the location of the manifest file. Bitmap image formats are not supported. - IconPath string `json:"icon_path,omitempty" yaml:"icon_path,omitempty"` - - // A version number for your plugin. Semantic versioning is recommended: http://semver.org - Version string `json:"version" yaml:"version"` - - // The minimum Mattermost server version required for your plugin. - // - // Minimum server version: 5.6 - MinServerVersion string `json:"min_server_version,omitempty" yaml:"min_server_version,omitempty"` - - // Server defines the server-side portion of your plugin. - Server *ManifestServer `json:"server,omitempty" yaml:"server,omitempty"` - - // Backend is a deprecated flag for defining the server-side portion of your plugin. Going forward, use Server instead. - Backend *ManifestServer `json:"backend,omitempty" yaml:"backend,omitempty"` - - // If your plugin extends the web app, you'll need to define webapp. - Webapp *ManifestWebapp `json:"webapp,omitempty" yaml:"webapp,omitempty"` - - // To allow administrators to configure your plugin via the Mattermost system console, you can - // provide your settings schema. - SettingsSchema *PluginSettingsSchema `json:"settings_schema,omitempty" yaml:"settings_schema,omitempty"` - - // Plugins can store any kind of data in Props to allow other plugins to use it. - Props map[string]interface{} `json:"props,omitempty" yaml:"props,omitempty"` - - // RequiredConfig defines any required server configuration fields for the plugin to function properly. - // - // Use the plugin helpers CheckRequiredServerConfiguration method to enforce this. - RequiredConfig *Config `json:"required_configuration,omitempty" yaml:"required_configuration,omitempty"` -} - -type ManifestServer struct { - // AllExecutables are the paths to your executable binaries, specifying multiple entry - // points for different platforms when bundled together in a single plugin. - AllExecutables map[string]string `json:"executables,omitempty" yaml:"executables,omitempty"` - - // Executables is a legacy field populated with a subset of supported platform executables. - // When unmarshalling, Executables is authoritative for the platform executable paths it - // contains, overriding any values in AllExecutables. When marshalling, AllExecutables - // is authoritative. - // - // Code duplication is avoided when (un)marshalling by leveraging type aliases in the - // various (Un)Marshal(JSON|YAML) methods, since aliases don't inherit the aliased type's - // methods. - // - // In v6.0, we should remove this field and rename AllExecutables back to Executables. - Executables *ManifestExecutables `json:"-" yaml:"-"` - - // Executable is the path to your executable binary. This should be relative to the root - // of your bundle and the location of the manifest file. - // - // On Windows, this file must have a ".exe" extension. - // - // If your plugin is compiled for multiple platforms, consider bundling them together - // and using the Executables field instead. - Executable string `json:"executable" yaml:"executable"` -} - -func (ms *ManifestServer) MarshalJSON() ([]byte, error) { - type auxManifestServer ManifestServer - - // Populate AllExecutables from Executables, if it exists. - if ms.Executables != nil { - if ms.AllExecutables == nil { - ms.AllExecutables = make(map[string]string) - } - - ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64 - ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64 - ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64 - } - - return json.Marshal((*auxManifestServer)(ms)) -} - -func (ms *ManifestServer) UnmarshalJSON(data []byte) error { - type auxManifestServer ManifestServer - - aux := (*auxManifestServer)(ms) - if err := json.Unmarshal(data, aux); err != nil { - return err - } - - if len(aux.AllExecutables) > 0 { - ms.Executables = &ManifestExecutables{ - LinuxAmd64: aux.AllExecutables["linux-amd64"], - DarwinAmd64: aux.AllExecutables["darwin-amd64"], - WindowsAmd64: aux.AllExecutables["windows-amd64"], - } - } - - return nil -} - -func (ms *ManifestServer) MarshalYAML() ([]byte, error) { - type auxManifestServer ManifestServer - - // Populate AllExecutables from Executables, if it exists. - if ms.Executables != nil { - if ms.AllExecutables == nil { - ms.AllExecutables = make(map[string]string) - } - - ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64 - ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64 - ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64 - } - - return yaml.Marshal((*auxManifestServer)(ms)) -} - -func (ms *ManifestServer) UnmarshalYAML(unmarshal func(interface{}) error) error { - type auxManifestServer ManifestServer - - aux := (*auxManifestServer)(ms) - if err := unmarshal(&aux); err != nil { - return err - } - - if len(aux.AllExecutables) > 0 { - ms.Executables = &ManifestExecutables{ - LinuxAmd64: aux.AllExecutables["linux-amd64"], - DarwinAmd64: aux.AllExecutables["darwin-amd64"], - WindowsAmd64: aux.AllExecutables["windows-amd64"], - } - } - - return nil -} - -// ManifestExecutables is a legacy structure capturing a subet of the known platform executables. -type ManifestExecutables struct { - // LinuxAmd64 is the path to your executable binary for the corresponding platform - LinuxAmd64 string `json:"linux-amd64,omitempty" yaml:"linux-amd64,omitempty"` - // DarwinAmd64 is the path to your executable binary for the corresponding platform - DarwinAmd64 string `json:"darwin-amd64,omitempty" yaml:"darwin-amd64,omitempty"` - // WindowsAmd64 is the path to your executable binary for the corresponding platform - // This file must have a ".exe" extension - WindowsAmd64 string `json:"windows-amd64,omitempty" yaml:"windows-amd64,omitempty"` -} - -type ManifestWebapp struct { - // The path to your webapp bundle. This should be relative to the root of your bundle and the - // location of the manifest file. - BundlePath string `json:"bundle_path" yaml:"bundle_path"` - - // BundleHash is the 64-bit FNV-1a hash of the webapp bundle, computed when the plugin is loaded - BundleHash []byte `json:"-"` -} - -func (m *Manifest) ToJson() string { - b, _ := json.Marshal(m) - return string(b) -} - -func ManifestListToJson(m []*Manifest) string { - b, _ := json.Marshal(m) - return string(b) -} - -func ManifestFromJson(data io.Reader) *Manifest { - var m *Manifest - json.NewDecoder(data).Decode(&m) - return m -} - -func ManifestListFromJson(data io.Reader) []*Manifest { - var manifests []*Manifest - json.NewDecoder(data).Decode(&manifests) - return manifests -} - -func (m *Manifest) HasClient() bool { - return m.Webapp != nil -} - -func (m *Manifest) ClientManifest() *Manifest { - cm := new(Manifest) - *cm = *m - cm.Name = "" - cm.Description = "" - cm.Server = nil - if cm.Webapp != nil { - cm.Webapp = new(ManifestWebapp) - *cm.Webapp = *m.Webapp - cm.Webapp.BundlePath = "/static/" + m.Id + "/" + fmt.Sprintf("%s_%x_bundle.js", m.Id, m.Webapp.BundleHash) - } - return cm -} - -// GetExecutableForRuntime returns the path to the executable for the given runtime architecture. -// -// If the manifest defines multiple executables, but none match, or if only a single executable -// is defined, the Executable field will be returned. This method does not guarantee that the -// resulting binary can actually execute on the given platform. -func (m *Manifest) GetExecutableForRuntime(goOs, goArch string) string { - server := m.Server - - // Support the deprecated backend parameter. - if server == nil { - server = m.Backend - } - - if server == nil { - return "" - } - - var executable string - if len(server.AllExecutables) > 0 { - osArch := fmt.Sprintf("%s-%s", goOs, goArch) - executable = server.AllExecutables[osArch] - } - - if executable == "" { - executable = server.Executable - } - - return executable -} - -func (m *Manifest) HasServer() bool { - return m.Server != nil || m.Backend != nil -} - -func (m *Manifest) HasWebapp() bool { - return m.Webapp != nil -} - -func (m *Manifest) MeetMinServerVersion(serverVersion string) (bool, error) { - minServerVersion, err := semver.Parse(m.MinServerVersion) - if err != nil { - return false, errors.New("failed to parse MinServerVersion") - } - sv := semver.MustParse(serverVersion) - if sv.LT(minServerVersion) { - return false, nil - } - return true, nil -} - -func (m *Manifest) IsValid() error { - if !IsValidPluginId(m.Id) { - return errors.New("invalid plugin ID") - } - - if strings.TrimSpace(m.Name) == "" { - return errors.New("a plugin name is needed") - } - - if m.HomepageURL != "" && !IsValidHttpUrl(m.HomepageURL) { - return errors.New("invalid HomepageURL") - } - - if m.SupportURL != "" && !IsValidHttpUrl(m.SupportURL) { - return errors.New("invalid SupportURL") - } - - if m.ReleaseNotesURL != "" && !IsValidHttpUrl(m.ReleaseNotesURL) { - return errors.New("invalid ReleaseNotesURL") - } - - if m.Version != "" { - _, err := semver.Parse(m.Version) - if err != nil { - return errors.Wrap(err, "failed to parse Version") - } - } - - if m.MinServerVersion != "" { - _, err := semver.Parse(m.MinServerVersion) - if err != nil { - return errors.Wrap(err, "failed to parse MinServerVersion") - } - } - - if m.SettingsSchema != nil { - err := m.SettingsSchema.isValid() - if err != nil { - return errors.Wrap(err, "invalid settings schema") - } - } - - return nil -} - -func (s *PluginSettingsSchema) isValid() error { - for _, setting := range s.Settings { - err := setting.isValid() - if err != nil { - return err - } - } - - return nil -} - -func (s *PluginSetting) isValid() error { - pluginSettingType, err := convertTypeToPluginSettingType(s.Type) - if err != nil { - return err - } - - if s.RegenerateHelpText != "" && pluginSettingType != Generated { - return errors.New("should not set RegenerateHelpText for setting type that is not generated") - } - - if s.Placeholder != "" && !(pluginSettingType == Generated || - pluginSettingType == Text || - pluginSettingType == LongText || - pluginSettingType == Number || - pluginSettingType == Username) { - return errors.New("should not set Placeholder for setting type not in text, generated or username") - } - - if s.Options != nil { - if pluginSettingType != Radio && pluginSettingType != Dropdown { - return errors.New("should not set Options for setting type not in radio or dropdown") - } - - for _, option := range s.Options { - if option.DisplayName == "" || option.Value == "" { - return errors.New("should not have empty Displayname or Value for any option") - } - } - } - - return nil -} - -func convertTypeToPluginSettingType(t string) (PluginSettingType, error) { - var settingType PluginSettingType - switch t { - case "bool": - return Bool, nil - case "dropdown": - return Dropdown, nil - case "generated": - return Generated, nil - case "radio": - return Radio, nil - case "text": - return Text, nil - case "number": - return Number, nil - case "longtext": - return LongText, nil - case "username": - return Username, nil - case "custom": - return Custom, nil - default: - return settingType, errors.New("invalid setting type: " + t) - } -} - -// FindManifest will find and parse the manifest in a given directory. -// -// In all cases other than a does-not-exist error, path is set to the path of the manifest file that was -// found. -// -// Manifests are JSON or YAML files named plugin.json, plugin.yaml, or plugin.yml. -func FindManifest(dir string) (manifest *Manifest, path string, err error) { - for _, name := range []string{"plugin.yml", "plugin.yaml"} { - path = filepath.Join(dir, name) - f, ferr := os.Open(path) - if ferr != nil { - if !os.IsNotExist(ferr) { - return nil, "", ferr - } - continue - } - b, ioerr := ioutil.ReadAll(f) - f.Close() - if ioerr != nil { - return nil, path, ioerr - } - var parsed Manifest - err = yaml.Unmarshal(b, &parsed) - if err != nil { - return nil, path, err - } - manifest = &parsed - manifest.Id = strings.ToLower(manifest.Id) - return manifest, path, nil - } - - path = filepath.Join(dir, "plugin.json") - f, ferr := os.Open(path) - if ferr != nil { - if os.IsNotExist(ferr) { - path = "" - } - return nil, path, ferr - } - defer f.Close() - var parsed Manifest - err = json.NewDecoder(f).Decode(&parsed) - if err != nil { - return nil, path, err - } - manifest = &parsed - manifest.Id = strings.ToLower(manifest.Id) - return manifest, path, nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/marketplace_plugin.go b/vendor/github.com/mattermost/mattermost-server/v5/model/marketplace_plugin.go deleted file mode 100644 index cad93dfb..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/marketplace_plugin.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "io" - "net/url" - "strconv" - - "github.com/pkg/errors" -) - -// BaseMarketplacePlugin is a Mattermost plugin received from the Marketplace server. -type BaseMarketplacePlugin struct { - HomepageURL string `json:"homepage_url"` - IconData string `json:"icon_data"` - DownloadURL string `json:"download_url"` - ReleaseNotesURL string `json:"release_notes_url"` - Labels []MarketplaceLabel `json:"labels,omitempty"` - Hosting string `json:"hosting"` // Indicated if the plugin is limited to a certain hosting type - AuthorType string `json:"author_type"` // The maintainer of the plugin - ReleaseStage string `json:"release_stage"` // The stage in the software release cycle that the plugin is in - Enterprise bool `json:"enterprise"` // Indicated if the plugin is an enterprise plugin - Signature string `json:"signature"` // Signature represents a signature of a plugin saved in base64 encoding. - Manifest *Manifest `json:"manifest"` -} - -// MarketplaceLabel represents a label shown in the Marketplace UI. -type MarketplaceLabel struct { - Name string `json:"name"` - Description string `json:"description"` - URL string `json:"url"` - Color string `json:"color"` -} - -// MarketplacePlugin is a state aware Marketplace plugin. -type MarketplacePlugin struct { - *BaseMarketplacePlugin - InstalledVersion string `json:"installed_version"` -} - -// BaseMarketplacePluginsFromReader decodes a json-encoded list of plugins from the given io.Reader. -func BaseMarketplacePluginsFromReader(reader io.Reader) ([]*BaseMarketplacePlugin, error) { - plugins := []*BaseMarketplacePlugin{} - decoder := json.NewDecoder(reader) - - if err := decoder.Decode(&plugins); err != nil && err != io.EOF { - return nil, err - } - - return plugins, nil -} - -// MarketplacePluginsFromReader decodes a json-encoded list of plugins from the given io.Reader. -func MarketplacePluginsFromReader(reader io.Reader) ([]*MarketplacePlugin, error) { - plugins := []*MarketplacePlugin{} - decoder := json.NewDecoder(reader) - - if err := decoder.Decode(&plugins); err != nil && err != io.EOF { - return nil, err - } - - return plugins, nil -} - -// DecodeSignature Decodes signature and returns ReadSeeker. -func (plugin *BaseMarketplacePlugin) DecodeSignature() (io.ReadSeeker, error) { - signatureBytes, err := base64.StdEncoding.DecodeString(plugin.Signature) - if err != nil { - return nil, errors.Wrap(err, "Unable to decode base64 signature.") - } - return bytes.NewReader(signatureBytes), nil -} - -// MarketplacePluginFilter describes the parameters to request a list of plugins. -type MarketplacePluginFilter struct { - Page int - PerPage int - Filter string - ServerVersion string - BuildEnterpriseReady bool - EnterprisePlugins bool - Cloud bool - LocalOnly bool - Platform string - PluginId string - ReturnAllVersions bool -} - -// ApplyToURL modifies the given url to include query string parameters for the request. -func (filter *MarketplacePluginFilter) ApplyToURL(u *url.URL) { - q := u.Query() - q.Add("page", strconv.Itoa(filter.Page)) - if filter.PerPage > 0 { - q.Add("per_page", strconv.Itoa(filter.PerPage)) - } - q.Add("filter", filter.Filter) - q.Add("server_version", filter.ServerVersion) - q.Add("build_enterprise_ready", strconv.FormatBool(filter.BuildEnterpriseReady)) - q.Add("enterprise_plugins", strconv.FormatBool(filter.EnterprisePlugins)) - q.Add("cloud", strconv.FormatBool(filter.Cloud)) - q.Add("local_only", strconv.FormatBool(filter.LocalOnly)) - q.Add("platform", filter.Platform) - q.Add("plugin_id", filter.PluginId) - q.Add("return_all_versions", strconv.FormatBool(filter.ReturnAllVersions)) - u.RawQuery = q.Encode() -} - -// InstallMarketplacePluginRequest struct describes parameters of the requested plugin. -type InstallMarketplacePluginRequest struct { - Id string `json:"id"` - Version string `json:"version"` -} - -// PluginRequestFromReader decodes a json-encoded plugin request from the given io.Reader. -func PluginRequestFromReader(reader io.Reader) (*InstallMarketplacePluginRequest, error) { - var r *InstallMarketplacePluginRequest - err := json.NewDecoder(reader).Decode(&r) - if err != nil { - return nil, err - } - return r, nil -} - -// ToJson method will return json from plugin request. -func (r *InstallMarketplacePluginRequest) ToJson() (string, error) { - b, err := json.Marshal(r) - if err != nil { - return "", err - } - return string(b), nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/mention_map.go b/vendor/github.com/mattermost/mattermost-server/v5/model/mention_map.go deleted file mode 100644 index 2f3444dd..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/mention_map.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "fmt" - "net/url" -) - -type UserMentionMap map[string]string -type ChannelMentionMap map[string]string - -const ( - userMentionsKey = "user_mentions" - userMentionsIdsKey = "user_mentions_ids" - channelMentionsKey = "channel_mentions" - channelMentionsIdsKey = "channel_mentions_ids" -) - -func UserMentionMapFromURLValues(values url.Values) (UserMentionMap, error) { - return mentionsFromURLValues(values, userMentionsKey, userMentionsIdsKey) -} - -func (m UserMentionMap) ToURLValues() url.Values { - return mentionsToURLValues(m, userMentionsKey, userMentionsIdsKey) -} - -func ChannelMentionMapFromURLValues(values url.Values) (ChannelMentionMap, error) { - return mentionsFromURLValues(values, channelMentionsKey, channelMentionsIdsKey) -} - -func (m ChannelMentionMap) ToURLValues() url.Values { - return mentionsToURLValues(m, channelMentionsKey, channelMentionsIdsKey) -} - -func mentionsFromURLValues(values url.Values, mentionKey, idKey string) (map[string]string, error) { - mentions, mentionsOk := values[mentionKey] - ids, idsOk := values[idKey] - - if !mentionsOk && !idsOk { - return map[string]string{}, nil - } - - if !mentionsOk { - return nil, fmt.Errorf("%s key not found", mentionKey) - } - - if !idsOk { - return nil, fmt.Errorf("%s key not found", idKey) - } - - if len(mentions) != len(ids) { - return nil, fmt.Errorf("keys %s and %s have different length", mentionKey, idKey) - } - - mentionsMap := make(map[string]string) - for i, mention := range mentions { - id := ids[i] - - if oldId, ok := mentionsMap[mention]; ok && oldId != id { - return nil, fmt.Errorf("key %s has two different values: %s and %s", mention, oldId, id) - } - - mentionsMap[mention] = id - } - - return mentionsMap, nil -} - -func mentionsToURLValues(mentions map[string]string, mentionKey, idKey string) url.Values { - values := url.Values{} - - for mention, id := range mentions { - values.Add(mentionKey, mention) - values.Add(idKey, id) - } - - return values -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/message_export.go b/vendor/github.com/mattermost/mattermost-server/v5/model/message_export.go deleted file mode 100644 index f94d861f..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/message_export.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -type MessageExport struct { - TeamId *string - TeamName *string - TeamDisplayName *string - - ChannelId *string - ChannelName *string - ChannelDisplayName *string - ChannelType *string - - UserId *string - UserEmail *string - Username *string - IsBot bool - - PostId *string - PostCreateAt *int64 - PostUpdateAt *int64 - PostDeleteAt *int64 - PostMessage *string - PostType *string - PostRootId *string - PostProps *string - PostOriginalId *string - PostFileIds StringArray -} - -type MessageExportCursor struct { - LastPostUpdateAt int64 - LastPostId string -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/mfa_secret.go b/vendor/github.com/mattermost/mattermost-server/v5/model/mfa_secret.go deleted file mode 100644 index b2293566..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/mfa_secret.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type MfaSecret struct { - Secret string `json:"secret"` - QRCode string `json:"qr_code"` -} - -func (mfa *MfaSecret) ToJson() string { - b, _ := json.Marshal(mfa) - return string(b) -} - -func MfaSecretFromJson(data io.Reader) *MfaSecret { - var mfa *MfaSecret - json.NewDecoder(data).Decode(&mfa) - return mfa -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/migration.go b/vendor/github.com/mattermost/mattermost-server/v5/model/migration.go deleted file mode 100644 index 019ca7b5..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/migration.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete" - MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2 = "migration_advanced_permissions_phase_2" - - MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT = "emoji_permissions_split" - MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT = "webhook_permissions_split" - MIGRATION_KEY_LIST_JOIN_PUBLIC_PRIVATE_TEAMS = "list_join_public_private_teams" - MIGRATION_KEY_REMOVE_PERMANENT_DELETE_USER = "remove_permanent_delete_user" - MIGRATION_KEY_ADD_BOT_PERMISSIONS = "add_bot_permissions" - MIGRATION_KEY_APPLY_CHANNEL_MANAGE_DELETE_TO_CHANNEL_USER = "apply_channel_manage_delete_to_channel_user" - MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER = "remove_channel_manage_delete_from_team_user" - MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION = "view_members_new_permission" - MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions" - MIGRATION_KEY_CHANNEL_MODERATIONS_PERMISSIONS = "channel_moderations_permissions" - MIGRATION_KEY_ADD_USE_GROUP_MENTIONS_PERMISSION = "add_use_group_mentions_permission" - MIGRATION_KEY_ADD_SYSTEM_CONSOLE_PERMISSIONS = "add_system_console_permissions" - MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2 = "migration_sidebar_categories_phase_2" - MIGRATION_KEY_ADD_CONVERT_CHANNEL_PERMISSIONS = "add_convert_channel_permissions" - MIGRATION_KEY_ADD_SYSTEM_ROLES_PERMISSIONS = "add_system_roles_permissions" - MIGRATION_KEY_ADD_BILLING_PERMISSIONS = "add_billing_permissions" - MIGRATION_KEY_ADD_MANAGE_SHARED_CHANNEL_PERMISSIONS = "manage_shared_channel_permissions" - MIGRATION_KEY_ADD_MANAGE_SECURE_CONNECTIONS_PERMISSIONS = "manage_secure_connections_permissions" - MIGRATION_KEY_ADD_DOWNLOAD_COMPLIANCE_EXPORT_RESULTS = "download_compliance_export_results" - MIGRATION_KEY_ADD_COMPLIANCE_SUBSECTION_PERMISSIONS = "compliance_subsection_permissions" - MIGRATION_KEY_ADD_EXPERIMENTAL_SUBSECTION_PERMISSIONS = "experimental_subsection_permissions" - MIGRATION_KEY_ADD_AUTHENTICATION_SUBSECTION_PERMISSIONS = "authentication_subsection_permissions" - MIGRATION_KEY_ADD_SITE_SUBSECTION_PERMISSIONS = "site_subsection_permissions" - MIGRATION_KEY_ADD_ENVIRONMENT_SUBSECTION_PERMISSIONS = "environment_subsection_permissions" - MIGRATION_KEY_ADD_REPORTING_SUBSECTION_PERMISSIONS = "reporting_subsection_permissions" - MIGRATION_KEY_ADD_TEST_EMAIL_ANCILLARY_PERMISSION = "test_email_ancillary_permission" - MIGRATION_KEY_ADD_ABOUT_SUBSECTION_PERMISSIONS = "about_subsection_permissions" - MIGRATION_KEY_ADD_INTEGRATIONS_SUBSECTION_PERMISSIONS = "integrations_subsection_permissions" -) diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/oauth.go b/vendor/github.com/mattermost/mattermost-server/v5/model/oauth.go deleted file mode 100644 index 07198116..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/oauth.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "unicode/utf8" -) - -const ( - OAUTH_ACTION_SIGNUP = "signup" - OAUTH_ACTION_LOGIN = "login" - OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso" - OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email" - OAUTH_ACTION_MOBILE = "mobile" -) - -type OAuthApp struct { - Id string `json:"id"` - CreatorId string `json:"creator_id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - ClientSecret string `json:"client_secret"` - Name string `json:"name"` - Description string `json:"description"` - IconURL string `json:"icon_url"` - CallbackUrls StringArray `json:"callback_urls"` - Homepage string `json:"homepage"` - IsTrusted bool `json:"is_trusted"` -} - -// IsValid validates the app and returns an error if it isn't configured -// correctly. -func (a *OAuthApp) IsValid() *AppError { - - if !IsValidId(a.Id) { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "", http.StatusBadRequest) - } - - if a.CreateAt == 0 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if a.UpdateAt == 0 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if !IsValidId(a.CreatorId) { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if a.ClientSecret == "" || len(a.ClientSecret) > 128 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if a.Name == "" || len(a.Name) > 64 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if len(a.CallbackUrls) == 0 || len(fmt.Sprintf("%s", a.CallbackUrls)) > 1024 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - for _, callback := range a.CallbackUrls { - if !IsValidHttpUrl(callback) { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "", http.StatusBadRequest) - } - } - - if a.Homepage == "" || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(a.Description) > 512 { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - - if a.IconURL != "" { - if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) { - return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) - } - } - - return nil -} - -// PreSave will set the Id and ClientSecret if missing. It will also fill -// in the CreateAt, UpdateAt times. It should be run before saving the app to the db. -func (a *OAuthApp) PreSave() { - if a.Id == "" { - a.Id = NewId() - } - - if a.ClientSecret == "" { - a.ClientSecret = NewId() - } - - a.CreateAt = GetMillis() - a.UpdateAt = a.CreateAt -} - -// PreUpdate should be run before updating the app in the db. -func (a *OAuthApp) PreUpdate() { - a.UpdateAt = GetMillis() -} - -func (a *OAuthApp) ToJson() string { - b, _ := json.Marshal(a) - return string(b) -} - -// Generate a valid strong etag so the browser can cache the results -func (a *OAuthApp) Etag() string { - return Etag(a.Id, a.UpdateAt) -} - -// Remove any private data from the app object -func (a *OAuthApp) Sanitize() { - a.ClientSecret = "" -} - -func (a *OAuthApp) IsValidRedirectURL(url string) bool { - for _, u := range a.CallbackUrls { - if u == url { - return true - } - } - - return false -} - -func OAuthAppFromJson(data io.Reader) *OAuthApp { - var app *OAuthApp - json.NewDecoder(data).Decode(&app) - return app -} - -func OAuthAppListToJson(l []*OAuthApp) string { - b, _ := json.Marshal(l) - return string(b) -} - -func OAuthAppListFromJson(data io.Reader) []*OAuthApp { - var o []*OAuthApp - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/outgoing_webhook.go b/vendor/github.com/mattermost/mattermost-server/v5/model/outgoing_webhook.go deleted file mode 100644 index 0d7a88fb..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/outgoing_webhook.go +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - "strings" -) - -type OutgoingWebhook struct { - Id string `json:"id"` - Token string `json:"token"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - CreatorId string `json:"creator_id"` - ChannelId string `json:"channel_id"` - TeamId string `json:"team_id"` - TriggerWords StringArray `json:"trigger_words"` - TriggerWhen int `json:"trigger_when"` - CallbackURLs StringArray `json:"callback_urls"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - ContentType string `json:"content_type"` - Username string `json:"username"` - IconURL string `json:"icon_url"` -} - -type OutgoingWebhookPayload struct { - Token string `json:"token"` - TeamId string `json:"team_id"` - TeamDomain string `json:"team_domain"` - ChannelId string `json:"channel_id"` - ChannelName string `json:"channel_name"` - Timestamp int64 `json:"timestamp"` - UserId string `json:"user_id"` - UserName string `json:"user_name"` - PostId string `json:"post_id"` - Text string `json:"text"` - TriggerWord string `json:"trigger_word"` - FileIds string `json:"file_ids"` -} - -type OutgoingWebhookResponse struct { - Text *string `json:"text"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - Props StringInterface `json:"props"` - Attachments []*SlackAttachment `json:"attachments"` - Type string `json:"type"` - ResponseType string `json:"response_type"` -} - -const OUTGOING_HOOK_RESPONSE_TYPE_COMMENT = "comment" - -func (o *OutgoingWebhookPayload) ToJSON() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *OutgoingWebhookPayload) ToFormValues() string { - v := url.Values{} - v.Set("token", o.Token) - v.Set("team_id", o.TeamId) - v.Set("team_domain", o.TeamDomain) - v.Set("channel_id", o.ChannelId) - v.Set("channel_name", o.ChannelName) - v.Set("timestamp", strconv.FormatInt(o.Timestamp/1000, 10)) - v.Set("user_id", o.UserId) - v.Set("user_name", o.UserName) - v.Set("post_id", o.PostId) - v.Set("text", o.Text) - v.Set("trigger_word", o.TriggerWord) - v.Set("file_ids", o.FileIds) - - return v.Encode() -} - -func (o *OutgoingWebhook) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func OutgoingWebhookFromJson(data io.Reader) *OutgoingWebhook { - var o *OutgoingWebhook - json.NewDecoder(data).Decode(&o) - return o -} - -func OutgoingWebhookListToJson(l []*OutgoingWebhook) string { - b, _ := json.Marshal(l) - return string(b) -} - -func OutgoingWebhookListFromJson(data io.Reader) []*OutgoingWebhook { - var o []*OutgoingWebhook - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *OutgoingWebhookResponse) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func OutgoingWebhookResponseFromJson(data io.Reader) (*OutgoingWebhookResponse, error) { - var o *OutgoingWebhookResponse - err := json.NewDecoder(data).Decode(&o) - if err == io.EOF { - return nil, nil - } - return o, err -} - -func (o *OutgoingWebhook) IsValid() *AppError { - - if !IsValidId(o.Id) { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Token) != 26 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.token.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidId(o.CreatorId) { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if o.ChannelId != "" && !IsValidId(o.ChannelId) { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.TeamId) { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.team_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(fmt.Sprintf("%s", o.TriggerWords)) > 1024 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.words.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.TriggerWords) != 0 { - for _, triggerWord := range o.TriggerWords { - if triggerWord == "" { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.trigger_words.app_error", nil, "", http.StatusBadRequest) - } - } - } - - if len(o.CallbackURLs) == 0 || len(fmt.Sprintf("%s", o.CallbackURLs)) > 1024 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.callback.app_error", nil, "", http.StatusBadRequest) - } - - for _, callback := range o.CallbackURLs { - if !IsValidHttpUrl(callback) { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.url.app_error", nil, "", http.StatusBadRequest) - } - } - - if len(o.DisplayName) > 64 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Description) > 500 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.ContentType) > 128 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "", http.StatusBadRequest) - } - - if o.TriggerWhen > 1 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.Username) > 64 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.username.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.IconURL) > 1024 { - return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.icon_url.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *OutgoingWebhook) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - if o.Token == "" { - o.Token = NewId() - } - - o.CreateAt = GetMillis() - o.UpdateAt = o.CreateAt -} - -func (o *OutgoingWebhook) PreUpdate() { - o.UpdateAt = GetMillis() -} - -func (o *OutgoingWebhook) TriggerWordExactMatch(word string) bool { - if word == "" { - return false - } - - for _, trigger := range o.TriggerWords { - if trigger == word { - return true - } - } - - return false -} - -func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool { - if word == "" { - return false - } - - for _, trigger := range o.TriggerWords { - if strings.HasPrefix(word, trigger) { - return true - } - } - - return false -} - -func (o *OutgoingWebhook) GetTriggerWord(word string, isExactMatch bool) (triggerWord string) { - if word == "" { - return - } - - if isExactMatch { - for _, trigger := range o.TriggerWords { - if trigger == word { - triggerWord = trigger - break - } - } - } else { - for _, trigger := range o.TriggerWords { - if strings.HasPrefix(word, trigger) { - triggerWord = trigger - break - } - } - } - - return triggerWord -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/permission.go b/vendor/github.com/mattermost/mattermost-server/v5/model/permission.go deleted file mode 100644 index bc4de236..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/permission.go +++ /dev/null @@ -1,2192 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - PermissionScopeSystem = "system_scope" - PermissionScopeTeam = "team_scope" - PermissionScopeChannel = "channel_scope" -) - -type Permission struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Scope string `json:"scope"` -} - -var PERMISSION_INVITE_USER *Permission -var PERMISSION_ADD_USER_TO_TEAM *Permission -var PERMISSION_USE_SLASH_COMMANDS *Permission -var PERMISSION_MANAGE_SLASH_COMMANDS *Permission -var PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS *Permission -var PERMISSION_CREATE_PUBLIC_CHANNEL *Permission -var PERMISSION_CREATE_PRIVATE_CHANNEL *Permission -var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission -var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission -var PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE *Permission -var PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC *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_PUBLIC_TEAMS *Permission -var PERMISSION_JOIN_PUBLIC_TEAMS *Permission -var PERMISSION_LIST_PRIVATE_TEAMS *Permission -var PERMISSION_JOIN_PRIVATE_TEAMS *Permission -var PERMISSION_LIST_TEAM_CHANNELS *Permission -var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission -var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission -var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission -var PERMISSION_EDIT_OTHER_USERS *Permission -var PERMISSION_READ_CHANNEL *Permission -var PERMISSION_READ_PUBLIC_CHANNEL_GROUPS *Permission -var PERMISSION_READ_PRIVATE_CHANNEL_GROUPS *Permission -var PERMISSION_READ_PUBLIC_CHANNEL *Permission -var PERMISSION_ADD_REACTION *Permission -var PERMISSION_REMOVE_REACTION *Permission -var PERMISSION_REMOVE_OTHERS_REACTIONS *Permission -var PERMISSION_PERMANENT_DELETE_USER *Permission -var PERMISSION_UPLOAD_FILE *Permission -var PERMISSION_GET_PUBLIC_LINK *Permission -var PERMISSION_MANAGE_WEBHOOKS *Permission -var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission -var PERMISSION_MANAGE_INCOMING_WEBHOOKS *Permission -var PERMISSION_MANAGE_OUTGOING_WEBHOOKS *Permission -var PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS *Permission -var PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS *Permission -var PERMISSION_MANAGE_OAUTH *Permission -var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission -var PERMISSION_MANAGE_EMOJIS *Permission -var PERMISSION_MANAGE_OTHERS_EMOJIS *Permission -var PERMISSION_CREATE_EMOJIS *Permission -var PERMISSION_DELETE_EMOJIS *Permission -var PERMISSION_DELETE_OTHERS_EMOJIS *Permission -var PERMISSION_CREATE_POST *Permission -var PERMISSION_CREATE_POST_PUBLIC *Permission -var PERMISSION_CREATE_POST_EPHEMERAL *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 -var PERMISSION_LIST_USERS_WITHOUT_TEAM *Permission -var PERMISSION_READ_JOBS *Permission -var PERMISSION_MANAGE_JOBS *Permission -var PERMISSION_CREATE_USER_ACCESS_TOKEN *Permission -var PERMISSION_READ_USER_ACCESS_TOKEN *Permission -var PERMISSION_REVOKE_USER_ACCESS_TOKEN *Permission -var PERMISSION_CREATE_BOT *Permission -var PERMISSION_ASSIGN_BOT *Permission -var PERMISSION_READ_BOTS *Permission -var PERMISSION_READ_OTHERS_BOTS *Permission -var PERMISSION_MANAGE_BOTS *Permission -var PERMISSION_MANAGE_OTHERS_BOTS *Permission -var PERMISSION_VIEW_MEMBERS *Permission -var PERMISSION_INVITE_GUEST *Permission -var PERMISSION_PROMOTE_GUEST *Permission -var PERMISSION_DEMOTE_TO_GUEST *Permission -var PERMISSION_USE_CHANNEL_MENTIONS *Permission -var PERMISSION_USE_GROUP_MENTIONS *Permission -var PERMISSION_READ_OTHER_USERS_TEAMS *Permission -var PERMISSION_EDIT_BRAND *Permission -var PERMISSION_MANAGE_SHARED_CHANNELS *Permission -var PERMISSION_MANAGE_SECURE_CONNECTIONS *Permission -var PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT *Permission -var PERMISSION_CREATE_DATA_RETENTION_JOB *Permission -var PERMISSION_READ_DATA_RETENTION_JOB *Permission -var PERMISSION_CREATE_COMPLIANCE_EXPORT_JOB *Permission -var PERMISSION_READ_COMPLIANCE_EXPORT_JOB *Permission -var PERMISSION_READ_AUDITS *Permission -var PERMISSION_TEST_ELASTICSEARCH *Permission -var PERMISSION_TEST_SITE_URL *Permission -var PERMISSION_TEST_S3 *Permission -var PERMISSION_RELOAD_CONFIG *Permission -var PERMISSION_INVALIDATE_CACHES *Permission -var PERMISSION_RECYCLE_DATABASE_CONNECTIONS *Permission -var PERMISSION_PURGE_ELASTICSEARCH_INDEXES *Permission -var PERMISSION_TEST_EMAIL *Permission -var PERMISSION_CREATE_ELASTICSEARCH_POST_INDEXING_JOB *Permission -var PERMISSION_CREATE_ELASTICSEARCH_POST_AGGREGATION_JOB *Permission -var PERMISSION_READ_ELASTICSEARCH_POST_INDEXING_JOB *Permission -var PERMISSION_READ_ELASTICSEARCH_POST_AGGREGATION_JOB *Permission -var PERMISSION_PURGE_BLEVE_INDEXES *Permission -var PERMISSION_CREATE_POST_BLEVE_INDEXES_JOB *Permission -var PERMISSION_CREATE_LDAP_SYNC_JOB *Permission -var PERMISSION_READ_LDAP_SYNC_JOB *Permission -var PERMISSION_TEST_LDAP *Permission -var PERMISSION_INVALIDATE_EMAIL_INVITE *Permission -var PERMISSION_GET_SAML_METADATA_FROM_IDP *Permission -var PERMISSION_ADD_SAML_PUBLIC_CERT *Permission -var PERMISSION_ADD_SAML_PRIVATE_CERT *Permission -var PERMISSION_ADD_SAML_IDP_CERT *Permission -var PERMISSION_REMOVE_SAML_PUBLIC_CERT *Permission -var PERMISSION_REMOVE_SAML_PRIVATE_CERT *Permission -var PERMISSION_REMOVE_SAML_IDP_CERT *Permission -var PERMISSION_GET_SAML_CERT_STATUS *Permission -var PERMISSION_ADD_LDAP_PUBLIC_CERT *Permission -var PERMISSION_ADD_LDAP_PRIVATE_CERT *Permission -var PERMISSION_REMOVE_LDAP_PUBLIC_CERT *Permission -var PERMISSION_REMOVE_LDAP_PRIVATE_CERT *Permission -var PERMISSION_GET_LOGS *Permission -var PERMISSION_GET_ANALYTICS *Permission -var PERMISSION_READ_LICENSE_INFORMATION *Permission -var PERMISSION_MANAGE_LICENSE_INFORMATION *Permission - -var PERMISSION_SYSCONSOLE_READ_ABOUT *Permission -var PERMISSION_SYSCONSOLE_WRITE_ABOUT *Permission - -var PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE *Permission -var PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE *Permission - -var PERMISSION_SYSCONSOLE_READ_BILLING *Permission -var PERMISSION_SYSCONSOLE_WRITE_BILLING *Permission - -var PERMISSION_SYSCONSOLE_READ_REPORTING *Permission -var PERMISSION_SYSCONSOLE_WRITE_REPORTING *Permission - -var PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS *Permission -var PERMISSION_SYSCONSOLE_WRITE_REPORTING_SITE_STATISTICS *Permission - -var PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS *Permission -var PERMISSION_SYSCONSOLE_WRITE_REPORTING_TEAM_STATISTICS *Permission - -var PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS *Permission -var PERMISSION_SYSCONSOLE_WRITE_REPORTING_SERVER_LOGS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS *Permission - -var PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_SYSTEM_ROLES *Permission -var PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_SYSTEM_ROLES *Permission - -// DEPRECATED -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT *Permission - -// DEPRECATED -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_IMAGE_PROXY *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PUSH_NOTIFICATION_SERVER *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_HIGH_AVAILABILITY *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_RATE_LIMITING *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_LOGGING *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING *Permission - -var PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER *Permission -var PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_LOCALIZATION *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_USERS_AND_TEAMS *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_NOTIFICATIONS *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_ANNOUNCEMENT_BANNER *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_EMOJI *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_EMOJI *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_POSTS *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_POSTS *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_FILE_SHARING_AND_DOWNLOADS *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_PUBLIC_LINKS *Permission - -var PERMISSION_SYSCONSOLE_READ_SITE_NOTICES *Permission -var PERMISSION_SYSCONSOLE_WRITE_SITE_NOTICES *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_PASSWORD *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_MFA *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_LDAP *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SAML *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_OPENID *Permission - -var PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS *Permission -var PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_GUEST_ACCESS *Permission - -var PERMISSION_SYSCONSOLE_READ_PLUGINS *Permission -var PERMISSION_SYSCONSOLE_WRITE_PLUGINS *Permission - -var PERMISSION_SYSCONSOLE_READ_INTEGRATIONS *Permission -var PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS *Permission - -var PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT *Permission -var PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_INTEGRATION_MANAGEMENT *Permission - -var PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS *Permission -var PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_BOT_ACCOUNTS *Permission - -var PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF *Permission -var PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_GIF *Permission - -var PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS *Permission -var PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS *Permission - -var PERMISSION_SYSCONSOLE_READ_COMPLIANCE *Permission -var PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE *Permission - -var PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY *Permission -var PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_DATA_RETENTION_POLICY *Permission - -var PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT *Permission -var PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_EXPORT *Permission - -var PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_MONITORING *Permission -var PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_MONITORING *Permission - -var PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE *Permission -var PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE *Permission - -var PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL *Permission -var PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL *Permission - -var PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURES *Permission -var PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURES *Permission - -var PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURE_FLAGS *Permission -var PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURE_FLAGS *Permission - -var PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_BLEVE *Permission -var PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_BLEVE *Permission - -// General permission that encompasses all system admin functions -// in the future this could be broken up to allow access to some -// admin functions but not others -var PERMISSION_MANAGE_SYSTEM *Permission - -var AllPermissions []*Permission -var DeprecatedPermissions []*Permission - -var ChannelModeratedPermissions []string -var ChannelModeratedPermissionsMap map[string]string - -var SysconsoleReadPermissions []*Permission -var SysconsoleWritePermissions []*Permission - -func initializePermissions() { - PERMISSION_INVITE_USER = &Permission{ - "invite_user", - "authentication.permissions.team_invite_user.name", - "authentication.permissions.team_invite_user.description", - PermissionScopeTeam, - } - PERMISSION_ADD_USER_TO_TEAM = &Permission{ - "add_user_to_team", - "authentication.permissions.add_user_to_team.name", - "authentication.permissions.add_user_to_team.description", - PermissionScopeTeam, - } - PERMISSION_USE_SLASH_COMMANDS = &Permission{ - "use_slash_commands", - "authentication.permissions.team_use_slash_commands.name", - "authentication.permissions.team_use_slash_commands.description", - PermissionScopeChannel, - } - PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{ - "manage_slash_commands", - "authentication.permissions.manage_slash_commands.name", - "authentication.permissions.manage_slash_commands.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{ - "manage_others_slash_commands", - "authentication.permissions.manage_others_slash_commands.name", - "authentication.permissions.manage_others_slash_commands.description", - PermissionScopeTeam, - } - PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{ - "create_public_channel", - "authentication.permissions.create_public_channel.name", - "authentication.permissions.create_public_channel.description", - PermissionScopeTeam, - } - PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{ - "create_private_channel", - "authentication.permissions.create_private_channel.name", - "authentication.permissions.create_private_channel.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{ - "manage_public_channel_members", - "authentication.permissions.manage_public_channel_members.name", - "authentication.permissions.manage_public_channel_members.description", - PermissionScopeChannel, - } - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{ - "manage_private_channel_members", - "authentication.permissions.manage_private_channel_members.name", - "authentication.permissions.manage_private_channel_members.description", - PermissionScopeChannel, - } - PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE = &Permission{ - "convert_public_channel_to_private", - "authentication.permissions.convert_public_channel_to_private.name", - "authentication.permissions.convert_public_channel_to_private.description", - PermissionScopeChannel, - } - PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC = &Permission{ - "convert_private_channel_to_public", - "authentication.permissions.convert_private_channel_to_public.name", - "authentication.permissions.convert_private_channel_to_public.description", - PermissionScopeChannel, - } - PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{ - "assign_system_admin_role", - "authentication.permissions.assign_system_admin_role.name", - "authentication.permissions.assign_system_admin_role.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_ROLES = &Permission{ - "manage_roles", - "authentication.permissions.manage_roles.name", - "authentication.permissions.manage_roles.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_TEAM_ROLES = &Permission{ - "manage_team_roles", - "authentication.permissions.manage_team_roles.name", - "authentication.permissions.manage_team_roles.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ - "manage_channel_roles", - "authentication.permissions.manage_channel_roles.name", - "authentication.permissions.manage_channel_roles.description", - PermissionScopeChannel, - } - PERMISSION_MANAGE_SYSTEM = &Permission{ - "manage_system", - "authentication.permissions.manage_system.name", - "authentication.permissions.manage_system.description", - PermissionScopeSystem, - } - PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{ - "create_direct_channel", - "authentication.permissions.create_direct_channel.name", - "authentication.permissions.create_direct_channel.description", - PermissionScopeSystem, - } - PERMISSION_CREATE_GROUP_CHANNEL = &Permission{ - "create_group_channel", - "authentication.permissions.create_group_channel.name", - "authentication.permissions.create_group_channel.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{ - "manage_public_channel_properties", - "authentication.permissions.manage_public_channel_properties.name", - "authentication.permissions.manage_public_channel_properties.description", - PermissionScopeChannel, - } - PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{ - "manage_private_channel_properties", - "authentication.permissions.manage_private_channel_properties.name", - "authentication.permissions.manage_private_channel_properties.description", - PermissionScopeChannel, - } - PERMISSION_LIST_PUBLIC_TEAMS = &Permission{ - "list_public_teams", - "authentication.permissions.list_public_teams.name", - "authentication.permissions.list_public_teams.description", - PermissionScopeSystem, - } - PERMISSION_JOIN_PUBLIC_TEAMS = &Permission{ - "join_public_teams", - "authentication.permissions.join_public_teams.name", - "authentication.permissions.join_public_teams.description", - PermissionScopeSystem, - } - PERMISSION_LIST_PRIVATE_TEAMS = &Permission{ - "list_private_teams", - "authentication.permissions.list_private_teams.name", - "authentication.permissions.list_private_teams.description", - PermissionScopeSystem, - } - PERMISSION_JOIN_PRIVATE_TEAMS = &Permission{ - "join_private_teams", - "authentication.permissions.join_private_teams.name", - "authentication.permissions.join_private_teams.description", - PermissionScopeSystem, - } - PERMISSION_LIST_TEAM_CHANNELS = &Permission{ - "list_team_channels", - "authentication.permissions.list_team_channels.name", - "authentication.permissions.list_team_channels.description", - PermissionScopeTeam, - } - PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{ - "join_public_channels", - "authentication.permissions.join_public_channels.name", - "authentication.permissions.join_public_channels.description", - PermissionScopeTeam, - } - PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{ - "delete_public_channel", - "authentication.permissions.delete_public_channel.name", - "authentication.permissions.delete_public_channel.description", - PermissionScopeChannel, - } - PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{ - "delete_private_channel", - "authentication.permissions.delete_private_channel.name", - "authentication.permissions.delete_private_channel.description", - PermissionScopeChannel, - } - PERMISSION_EDIT_OTHER_USERS = &Permission{ - "edit_other_users", - "authentication.permissions.edit_other_users.name", - "authentication.permissions.edit_other_users.description", - PermissionScopeSystem, - } - PERMISSION_READ_CHANNEL = &Permission{ - "read_channel", - "authentication.permissions.read_channel.name", - "authentication.permissions.read_channel.description", - PermissionScopeChannel, - } - PERMISSION_READ_PUBLIC_CHANNEL_GROUPS = &Permission{ - "read_public_channel_groups", - "authentication.permissions.read_public_channel_groups.name", - "authentication.permissions.read_public_channel_groups.description", - PermissionScopeChannel, - } - PERMISSION_READ_PRIVATE_CHANNEL_GROUPS = &Permission{ - "read_private_channel_groups", - "authentication.permissions.read_private_channel_groups.name", - "authentication.permissions.read_private_channel_groups.description", - PermissionScopeChannel, - } - PERMISSION_READ_PUBLIC_CHANNEL = &Permission{ - "read_public_channel", - "authentication.permissions.read_public_channel.name", - "authentication.permissions.read_public_channel.description", - PermissionScopeTeam, - } - PERMISSION_ADD_REACTION = &Permission{ - "add_reaction", - "authentication.permissions.add_reaction.name", - "authentication.permissions.add_reaction.description", - PermissionScopeChannel, - } - PERMISSION_REMOVE_REACTION = &Permission{ - "remove_reaction", - "authentication.permissions.remove_reaction.name", - "authentication.permissions.remove_reaction.description", - PermissionScopeChannel, - } - PERMISSION_REMOVE_OTHERS_REACTIONS = &Permission{ - "remove_others_reactions", - "authentication.permissions.remove_others_reactions.name", - "authentication.permissions.remove_others_reactions.description", - PermissionScopeChannel, - } - // DEPRECATED - PERMISSION_PERMANENT_DELETE_USER = &Permission{ - "permanent_delete_user", - "authentication.permissions.permanent_delete_user.name", - "authentication.permissions.permanent_delete_user.description", - PermissionScopeSystem, - } - PERMISSION_UPLOAD_FILE = &Permission{ - "upload_file", - "authentication.permissions.upload_file.name", - "authentication.permissions.upload_file.description", - PermissionScopeChannel, - } - PERMISSION_GET_PUBLIC_LINK = &Permission{ - "get_public_link", - "authentication.permissions.get_public_link.name", - "authentication.permissions.get_public_link.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_MANAGE_WEBHOOKS = &Permission{ - "manage_webhooks", - "authentication.permissions.manage_webhooks.name", - "authentication.permissions.manage_webhooks.description", - PermissionScopeTeam, - } - // DEPRECATED - PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{ - "manage_others_webhooks", - "authentication.permissions.manage_others_webhooks.name", - "authentication.permissions.manage_others_webhooks.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_INCOMING_WEBHOOKS = &Permission{ - "manage_incoming_webhooks", - "authentication.permissions.manage_incoming_webhooks.name", - "authentication.permissions.manage_incoming_webhooks.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_OUTGOING_WEBHOOKS = &Permission{ - "manage_outgoing_webhooks", - "authentication.permissions.manage_outgoing_webhooks.name", - "authentication.permissions.manage_outgoing_webhooks.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS = &Permission{ - "manage_others_incoming_webhooks", - "authentication.permissions.manage_others_incoming_webhooks.name", - "authentication.permissions.manage_others_incoming_webhooks.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS = &Permission{ - "manage_others_outgoing_webhooks", - "authentication.permissions.manage_others_outgoing_webhooks.name", - "authentication.permissions.manage_others_outgoing_webhooks.description", - PermissionScopeTeam, - } - PERMISSION_MANAGE_OAUTH = &Permission{ - "manage_oauth", - "authentication.permissions.manage_oauth.name", - "authentication.permissions.manage_oauth.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{ - "manage_system_wide_oauth", - "authentication.permissions.manage_system_wide_oauth.name", - "authentication.permissions.manage_system_wide_oauth.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_MANAGE_EMOJIS = &Permission{ - "manage_emojis", - "authentication.permissions.manage_emojis.name", - "authentication.permissions.manage_emojis.description", - PermissionScopeTeam, - } - // DEPRECATED - PERMISSION_MANAGE_OTHERS_EMOJIS = &Permission{ - "manage_others_emojis", - "authentication.permissions.manage_others_emojis.name", - "authentication.permissions.manage_others_emojis.description", - PermissionScopeTeam, - } - PERMISSION_CREATE_EMOJIS = &Permission{ - "create_emojis", - "authentication.permissions.create_emojis.name", - "authentication.permissions.create_emojis.description", - PermissionScopeTeam, - } - PERMISSION_DELETE_EMOJIS = &Permission{ - "delete_emojis", - "authentication.permissions.delete_emojis.name", - "authentication.permissions.delete_emojis.description", - PermissionScopeTeam, - } - PERMISSION_DELETE_OTHERS_EMOJIS = &Permission{ - "delete_others_emojis", - "authentication.permissions.delete_others_emojis.name", - "authentication.permissions.delete_others_emojis.description", - PermissionScopeTeam, - } - PERMISSION_CREATE_POST = &Permission{ - "create_post", - "authentication.permissions.create_post.name", - "authentication.permissions.create_post.description", - PermissionScopeChannel, - } - PERMISSION_CREATE_POST_PUBLIC = &Permission{ - "create_post_public", - "authentication.permissions.create_post_public.name", - "authentication.permissions.create_post_public.description", - PermissionScopeChannel, - } - PERMISSION_CREATE_POST_EPHEMERAL = &Permission{ - "create_post_ephemeral", - "authentication.permissions.create_post_ephemeral.name", - "authentication.permissions.create_post_ephemeral.description", - PermissionScopeChannel, - } - PERMISSION_EDIT_POST = &Permission{ - "edit_post", - "authentication.permissions.edit_post.name", - "authentication.permissions.edit_post.description", - PermissionScopeChannel, - } - PERMISSION_EDIT_OTHERS_POSTS = &Permission{ - "edit_others_posts", - "authentication.permissions.edit_others_posts.name", - "authentication.permissions.edit_others_posts.description", - PermissionScopeChannel, - } - PERMISSION_DELETE_POST = &Permission{ - "delete_post", - "authentication.permissions.delete_post.name", - "authentication.permissions.delete_post.description", - PermissionScopeChannel, - } - PERMISSION_DELETE_OTHERS_POSTS = &Permission{ - "delete_others_posts", - "authentication.permissions.delete_others_posts.name", - "authentication.permissions.delete_others_posts.description", - PermissionScopeChannel, - } - PERMISSION_MANAGE_SHARED_CHANNELS = &Permission{ - "manage_shared_channels", - "authentication.permissions.manage_shared_channels.name", - "authentication.permissions.manage_shared_channels.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_SECURE_CONNECTIONS = &Permission{ - "manage_secure_connections", - "authentication.permissions.manage_secure_connections.name", - "authentication.permissions.manage_secure_connections.description", - PermissionScopeSystem, - } - - PERMISSION_CREATE_DATA_RETENTION_JOB = &Permission{ - "create_data_retention_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_READ_DATA_RETENTION_JOB = &Permission{ - "read_data_retention_job", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_CREATE_COMPLIANCE_EXPORT_JOB = &Permission{ - "create_compliance_export_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_READ_COMPLIANCE_EXPORT_JOB = &Permission{ - "read_compliance_export_job", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_READ_AUDITS = &Permission{ - "read_audits", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_PURGE_BLEVE_INDEXES = &Permission{ - "purge_bleve_indexes", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_CREATE_POST_BLEVE_INDEXES_JOB = &Permission{ - "create_post_bleve_indexes_job", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_CREATE_LDAP_SYNC_JOB = &Permission{ - "create_ldap_sync_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_READ_LDAP_SYNC_JOB = &Permission{ - "read_ldap_sync_job", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_TEST_LDAP = &Permission{ - "test_ldap", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_INVALIDATE_EMAIL_INVITE = &Permission{ - "invalidate_email_invite", - "", - "", - PermissionScopeSystem, - } - PERMISSION_GET_SAML_METADATA_FROM_IDP = &Permission{ - "get_saml_metadata_from_idp", - "", - "", - PermissionScopeSystem, - } - PERMISSION_ADD_SAML_PUBLIC_CERT = &Permission{ - "add_saml_public_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_ADD_SAML_PRIVATE_CERT = &Permission{ - "add_saml_private_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_ADD_SAML_IDP_CERT = &Permission{ - "add_saml_idp_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_SAML_PUBLIC_CERT = &Permission{ - "remove_saml_public_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_SAML_PRIVATE_CERT = &Permission{ - "remove_saml_private_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_SAML_IDP_CERT = &Permission{ - "remove_saml_idp_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_GET_SAML_CERT_STATUS = &Permission{ - "get_saml_cert_status", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_ADD_LDAP_PUBLIC_CERT = &Permission{ - "add_ldap_public_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_ADD_LDAP_PRIVATE_CERT = &Permission{ - "add_ldap_private_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_LDAP_PUBLIC_CERT = &Permission{ - "remove_ldap_public_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_LDAP_PRIVATE_CERT = &Permission{ - "remove_ldap_private_cert", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_GET_LOGS = &Permission{ - "get_logs", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_READ_LICENSE_INFORMATION = &Permission{ - "read_license_information", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_GET_ANALYTICS = &Permission{ - "get_analytics", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_MANAGE_LICENSE_INFORMATION = &Permission{ - "manage_license_information", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT = &Permission{ - "download_compliance_export_result", - "authentication.permissions.download_compliance_export_result.name", - "authentication.permissions.download_compliance_export_result.description", - PermissionScopeSystem, - } - - PERMISSION_TEST_SITE_URL = &Permission{ - "test_site_url", - "", - "", - PermissionScopeSystem, - } - PERMISSION_TEST_ELASTICSEARCH = &Permission{ - "test_elasticsearch", - "", - "", - PermissionScopeSystem, - } - PERMISSION_TEST_S3 = &Permission{ - "test_s3", - "", - "", - PermissionScopeSystem, - } - PERMISSION_RELOAD_CONFIG = &Permission{ - "reload_config", - "", - "", - PermissionScopeSystem, - } - PERMISSION_INVALIDATE_CACHES = &Permission{ - "invalidate_caches", - "", - "", - PermissionScopeSystem, - } - PERMISSION_RECYCLE_DATABASE_CONNECTIONS = &Permission{ - "recycle_database_connections", - "", - "", - PermissionScopeSystem, - } - PERMISSION_PURGE_ELASTICSEARCH_INDEXES = &Permission{ - "purge_elasticsearch_indexes", - "", - "", - PermissionScopeSystem, - } - PERMISSION_TEST_EMAIL = &Permission{ - "test_email", - "", - "", - PermissionScopeSystem, - } - PERMISSION_CREATE_ELASTICSEARCH_POST_INDEXING_JOB = &Permission{ - "create_elasticsearch_post_indexing_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_CREATE_ELASTICSEARCH_POST_AGGREGATION_JOB = &Permission{ - "create_elasticsearch_post_aggregation_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_READ_ELASTICSEARCH_POST_INDEXING_JOB = &Permission{ - "read_elasticsearch_post_indexing_job", - "", - "", - PermissionScopeSystem, - } - PERMISSION_READ_ELASTICSEARCH_POST_AGGREGATION_JOB = &Permission{ - "read_elasticsearch_post_aggregation_job", - "", - "", - PermissionScopeSystem, - } - - PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{ - "remove_user_from_team", - "authentication.permissions.remove_user_from_team.name", - "authentication.permissions.remove_user_from_team.description", - PermissionScopeTeam, - } - PERMISSION_CREATE_TEAM = &Permission{ - "create_team", - "authentication.permissions.create_team.name", - "authentication.permissions.create_team.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_TEAM = &Permission{ - "manage_team", - "authentication.permissions.manage_team.name", - "authentication.permissions.manage_team.description", - PermissionScopeTeam, - } - PERMISSION_IMPORT_TEAM = &Permission{ - "import_team", - "authentication.permissions.import_team.name", - "authentication.permissions.import_team.description", - PermissionScopeTeam, - } - PERMISSION_VIEW_TEAM = &Permission{ - "view_team", - "authentication.permissions.view_team.name", - "authentication.permissions.view_team.description", - PermissionScopeTeam, - } - PERMISSION_LIST_USERS_WITHOUT_TEAM = &Permission{ - "list_users_without_team", - "authentication.permissions.list_users_without_team.name", - "authentication.permissions.list_users_without_team.description", - PermissionScopeSystem, - } - PERMISSION_CREATE_USER_ACCESS_TOKEN = &Permission{ - "create_user_access_token", - "authentication.permissions.create_user_access_token.name", - "authentication.permissions.create_user_access_token.description", - PermissionScopeSystem, - } - PERMISSION_READ_USER_ACCESS_TOKEN = &Permission{ - "read_user_access_token", - "authentication.permissions.read_user_access_token.name", - "authentication.permissions.read_user_access_token.description", - PermissionScopeSystem, - } - PERMISSION_REVOKE_USER_ACCESS_TOKEN = &Permission{ - "revoke_user_access_token", - "authentication.permissions.revoke_user_access_token.name", - "authentication.permissions.revoke_user_access_token.description", - PermissionScopeSystem, - } - PERMISSION_CREATE_BOT = &Permission{ - "create_bot", - "authentication.permissions.create_bot.name", - "authentication.permissions.create_bot.description", - PermissionScopeSystem, - } - PERMISSION_ASSIGN_BOT = &Permission{ - "assign_bot", - "authentication.permissions.assign_bot.name", - "authentication.permissions.assign_bot.description", - PermissionScopeSystem, - } - PERMISSION_READ_BOTS = &Permission{ - "read_bots", - "authentication.permissions.read_bots.name", - "authentication.permissions.read_bots.description", - PermissionScopeSystem, - } - PERMISSION_READ_OTHERS_BOTS = &Permission{ - "read_others_bots", - "authentication.permissions.read_others_bots.name", - "authentication.permissions.read_others_bots.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_BOTS = &Permission{ - "manage_bots", - "authentication.permissions.manage_bots.name", - "authentication.permissions.manage_bots.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_OTHERS_BOTS = &Permission{ - "manage_others_bots", - "authentication.permissions.manage_others_bots.name", - "authentication.permissions.manage_others_bots.description", - PermissionScopeSystem, - } - PERMISSION_READ_JOBS = &Permission{ - "read_jobs", - "authentication.permisssions.read_jobs.name", - "authentication.permisssions.read_jobs.description", - PermissionScopeSystem, - } - PERMISSION_MANAGE_JOBS = &Permission{ - "manage_jobs", - "authentication.permisssions.manage_jobs.name", - "authentication.permisssions.manage_jobs.description", - PermissionScopeSystem, - } - PERMISSION_VIEW_MEMBERS = &Permission{ - "view_members", - "authentication.permisssions.view_members.name", - "authentication.permisssions.view_members.description", - PermissionScopeTeam, - } - PERMISSION_INVITE_GUEST = &Permission{ - "invite_guest", - "authentication.permissions.invite_guest.name", - "authentication.permissions.invite_guest.description", - PermissionScopeTeam, - } - PERMISSION_PROMOTE_GUEST = &Permission{ - "promote_guest", - "authentication.permissions.promote_guest.name", - "authentication.permissions.promote_guest.description", - PermissionScopeSystem, - } - PERMISSION_DEMOTE_TO_GUEST = &Permission{ - "demote_to_guest", - "authentication.permissions.demote_to_guest.name", - "authentication.permissions.demote_to_guest.description", - PermissionScopeSystem, - } - PERMISSION_USE_CHANNEL_MENTIONS = &Permission{ - "use_channel_mentions", - "authentication.permissions.use_channel_mentions.name", - "authentication.permissions.use_channel_mentions.description", - PermissionScopeChannel, - } - PERMISSION_USE_GROUP_MENTIONS = &Permission{ - "use_group_mentions", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeChannel, - } - PERMISSION_READ_OTHER_USERS_TEAMS = &Permission{ - "read_other_users_teams", - "authentication.permissions.read_other_users_teams.name", - "authentication.permissions.read_other_users_teams.description", - PermissionScopeSystem, - } - PERMISSION_EDIT_BRAND = &Permission{ - "edit_brand", - "authentication.permissions.edit_brand.name", - "authentication.permissions.edit_brand.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_ABOUT = &Permission{ - "sysconsole_read_about", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_ABOUT = &Permission{ - "sysconsole_write_about", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE = &Permission{ - "sysconsole_read_about_edition_and_license", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE = &Permission{ - "sysconsole_write_about_edition_and_license", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_BILLING = &Permission{ - "sysconsole_read_billing", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_BILLING = &Permission{ - "sysconsole_write_billing", - "", - "", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_REPORTING = &Permission{ - "sysconsole_read_reporting", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_REPORTING = &Permission{ - "sysconsole_write_reporting", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS = &Permission{ - "sysconsole_read_reporting_site_statistics", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_REPORTING_SITE_STATISTICS = &Permission{ - "sysconsole_write_reporting_site_statistics", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS = &Permission{ - "sysconsole_read_reporting_team_statistics", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_REPORTING_TEAM_STATISTICS = &Permission{ - "sysconsole_write_reporting_team_statistics", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS = &Permission{ - "sysconsole_read_reporting_server_logs", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_REPORTING_SERVER_LOGS = &Permission{ - "sysconsole_write_reporting_server_logs", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS = &Permission{ - "sysconsole_read_user_management_users", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS = &Permission{ - "sysconsole_write_user_management_users", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS = &Permission{ - "sysconsole_read_user_management_groups", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS = &Permission{ - "sysconsole_write_user_management_groups", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS = &Permission{ - "sysconsole_read_user_management_teams", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS = &Permission{ - "sysconsole_write_user_management_teams", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS = &Permission{ - "sysconsole_read_user_management_channels", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS = &Permission{ - "sysconsole_write_user_management_channels", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS = &Permission{ - "sysconsole_read_user_management_permissions", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS = &Permission{ - "sysconsole_write_user_management_permissions", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_SYSTEM_ROLES = &Permission{ - "sysconsole_read_user_management_system_roles", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_SYSTEM_ROLES = &Permission{ - "sysconsole_write_user_management_system_roles", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT = &Permission{ - "sysconsole_read_environment", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT = &Permission{ - "sysconsole_write_environment", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER = &Permission{ - "sysconsole_read_environment_web_server", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER = &Permission{ - "sysconsole_write_environment_web_server", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE = &Permission{ - "sysconsole_read_environment_database", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE = &Permission{ - "sysconsole_write_environment_database", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH = &Permission{ - "sysconsole_read_environment_elasticsearch", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH = &Permission{ - "sysconsole_write_environment_elasticsearch", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE = &Permission{ - "sysconsole_read_environment_file_storage", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE = &Permission{ - "sysconsole_write_environment_file_storage", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY = &Permission{ - "sysconsole_read_environment_image_proxy", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_IMAGE_PROXY = &Permission{ - "sysconsole_write_environment_image_proxy", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP = &Permission{ - "sysconsole_read_environment_smtp", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP = &Permission{ - "sysconsole_write_environment_smtp", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER = &Permission{ - "sysconsole_read_environment_push_notification_server", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PUSH_NOTIFICATION_SERVER = &Permission{ - "sysconsole_write_environment_push_notification_server", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY = &Permission{ - "sysconsole_read_environment_high_availability", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_HIGH_AVAILABILITY = &Permission{ - "sysconsole_write_environment_high_availability", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING = &Permission{ - "sysconsole_read_environment_rate_limiting", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_RATE_LIMITING = &Permission{ - "sysconsole_write_environment_rate_limiting", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING = &Permission{ - "sysconsole_read_environment_logging", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_LOGGING = &Permission{ - "sysconsole_write_environment_logging", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS = &Permission{ - "sysconsole_read_environment_session_lengths", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS = &Permission{ - "sysconsole_write_environment_session_lengths", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING = &Permission{ - "sysconsole_read_environment_performance_monitoring", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING = &Permission{ - "sysconsole_write_environment_performance_monitoring", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER = &Permission{ - "sysconsole_read_environment_developer", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER = &Permission{ - "sysconsole_write_environment_developer", - "", - "", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_SITE = &Permission{ - "sysconsole_read_site", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_SITE = &Permission{ - "sysconsole_write_site", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - - PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION = &Permission{ - "sysconsole_read_site_customization", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION = &Permission{ - "sysconsole_write_site_customization", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION = &Permission{ - "sysconsole_read_site_localization", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_LOCALIZATION = &Permission{ - "sysconsole_write_site_localization", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS = &Permission{ - "sysconsole_read_site_users_and_teams", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_USERS_AND_TEAMS = &Permission{ - "sysconsole_write_site_users_and_teams", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS = &Permission{ - "sysconsole_read_site_notifications", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTIFICATIONS = &Permission{ - "sysconsole_write_site_notifications", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER = &Permission{ - "sysconsole_read_site_announcement_banner", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_ANNOUNCEMENT_BANNER = &Permission{ - "sysconsole_write_site_announcement_banner", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_EMOJI = &Permission{ - "sysconsole_read_site_emoji", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_EMOJI = &Permission{ - "sysconsole_write_site_emoji", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_POSTS = &Permission{ - "sysconsole_read_site_posts", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_POSTS = &Permission{ - "sysconsole_write_site_posts", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS = &Permission{ - "sysconsole_read_site_file_sharing_and_downloads", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_FILE_SHARING_AND_DOWNLOADS = &Permission{ - "sysconsole_write_site_file_sharing_and_downloads", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS = &Permission{ - "sysconsole_read_site_public_links", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_PUBLIC_LINKS = &Permission{ - "sysconsole_write_site_public_links", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_SITE_NOTICES = &Permission{ - "sysconsole_read_site_notices", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTICES = &Permission{ - "sysconsole_write_site_notices", - "", - "", - PermissionScopeSystem, - } - - // Deprecated - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION = &Permission{ - "sysconsole_read_authentication", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // Deprecated - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION = &Permission{ - "sysconsole_write_authentication", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP = &Permission{ - "sysconsole_read_authentication_signup", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP = &Permission{ - "sysconsole_write_authentication_signup", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL = &Permission{ - "sysconsole_read_authentication_email", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL = &Permission{ - "sysconsole_write_authentication_email", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD = &Permission{ - "sysconsole_read_authentication_password", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_PASSWORD = &Permission{ - "sysconsole_write_authentication_password", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA = &Permission{ - "sysconsole_read_authentication_mfa", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_MFA = &Permission{ - "sysconsole_write_authentication_mfa", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP = &Permission{ - "sysconsole_read_authentication_ldap", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_LDAP = &Permission{ - "sysconsole_write_authentication_ldap", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML = &Permission{ - "sysconsole_read_authentication_saml", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SAML = &Permission{ - "sysconsole_write_authentication_saml", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID = &Permission{ - "sysconsole_read_authentication_openid", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_OPENID = &Permission{ - "sysconsole_write_authentication_openid", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS = &Permission{ - "sysconsole_read_authentication_guest_access", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_GUEST_ACCESS = &Permission{ - "sysconsole_write_authentication_guest_access", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_PLUGINS = &Permission{ - "sysconsole_read_plugins", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_PLUGINS = &Permission{ - "sysconsole_write_plugins", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS = &Permission{ - "sysconsole_read_integrations", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS = &Permission{ - "sysconsole_write_integrations", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT = &Permission{ - "sysconsole_read_integrations_integration_management", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_INTEGRATION_MANAGEMENT = &Permission{ - "sysconsole_write_integrations_integration_management", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS = &Permission{ - "sysconsole_read_integrations_bot_accounts", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_BOT_ACCOUNTS = &Permission{ - "sysconsole_write_integrations_bot_accounts", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF = &Permission{ - "sysconsole_read_integrations_gif", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_GIF = &Permission{ - "sysconsole_write_integrations_gif", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS = &Permission{ - "sysconsole_read_integrations_cors", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS = &Permission{ - "sysconsole_write_integrations_cors", - "", - "", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_COMPLIANCE = &Permission{ - "sysconsole_read_compliance", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE = &Permission{ - "sysconsole_write_compliance", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY = &Permission{ - "sysconsole_read_compliance_data_retention_policy", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_DATA_RETENTION_POLICY = &Permission{ - "sysconsole_write_compliance_data_retention_policy", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT = &Permission{ - "sysconsole_read_compliance_compliance_export", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_EXPORT = &Permission{ - "sysconsole_write_compliance_compliance_export", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_MONITORING = &Permission{ - "sysconsole_read_compliance_compliance_monitoring", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_MONITORING = &Permission{ - "sysconsole_write_compliance_compliance_monitoring", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE = &Permission{ - "sysconsole_read_compliance_custom_terms_of_service", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE = &Permission{ - "sysconsole_write_compliance_custom_terms_of_service", - "", - "", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL = &Permission{ - "sysconsole_read_experimental", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - // DEPRECATED - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL = &Permission{ - "sysconsole_write_experimental", - "authentication.permissions.use_group_mentions.name", - "authentication.permissions.use_group_mentions.description", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURES = &Permission{ - "sysconsole_read_experimental_features", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURES = &Permission{ - "sysconsole_write_experimental_features", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURE_FLAGS = &Permission{ - "sysconsole_read_experimental_feature_flags", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURE_FLAGS = &Permission{ - "sysconsole_write_experimental_feature_flags", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_BLEVE = &Permission{ - "sysconsole_read_experimental_bleve", - "", - "", - PermissionScopeSystem, - } - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_BLEVE = &Permission{ - "sysconsole_write_experimental_bleve", - "", - "", - PermissionScopeSystem, - } - - SysconsoleReadPermissions = []*Permission{ - PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE, - PERMISSION_SYSCONSOLE_READ_BILLING, - PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS, - PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS, - PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_SYSTEM_ROLES, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER, - PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION, - PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION, - PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS, - PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS, - PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER, - PERMISSION_SYSCONSOLE_READ_SITE_EMOJI, - PERMISSION_SYSCONSOLE_READ_SITE_POSTS, - PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS, - PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS, - PERMISSION_SYSCONSOLE_READ_SITE_NOTICES, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS, - PERMISSION_SYSCONSOLE_READ_PLUGINS, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_MONITORING, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURES, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURE_FLAGS, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_BLEVE, - } - - SysconsoleWritePermissions = []*Permission{ - PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE, - PERMISSION_SYSCONSOLE_WRITE_BILLING, - PERMISSION_SYSCONSOLE_WRITE_REPORTING_SITE_STATISTICS, - PERMISSION_SYSCONSOLE_WRITE_REPORTING_TEAM_STATISTICS, - PERMISSION_SYSCONSOLE_WRITE_REPORTING_SERVER_LOGS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_SYSTEM_ROLES, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_IMAGE_PROXY, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PUSH_NOTIFICATION_SERVER, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_HIGH_AVAILABILITY, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_RATE_LIMITING, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_LOGGING, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER, - PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION, - PERMISSION_SYSCONSOLE_WRITE_SITE_LOCALIZATION, - PERMISSION_SYSCONSOLE_WRITE_SITE_USERS_AND_TEAMS, - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTIFICATIONS, - PERMISSION_SYSCONSOLE_WRITE_SITE_ANNOUNCEMENT_BANNER, - PERMISSION_SYSCONSOLE_WRITE_SITE_EMOJI, - PERMISSION_SYSCONSOLE_WRITE_SITE_POSTS, - PERMISSION_SYSCONSOLE_WRITE_SITE_FILE_SHARING_AND_DOWNLOADS, - PERMISSION_SYSCONSOLE_WRITE_SITE_PUBLIC_LINKS, - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTICES, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SIGNUP, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_PASSWORD, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_MFA, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_LDAP, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SAML, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_OPENID, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_GUEST_ACCESS, - PERMISSION_SYSCONSOLE_WRITE_PLUGINS, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_INTEGRATION_MANAGEMENT, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_BOT_ACCOUNTS, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_GIF, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_DATA_RETENTION_POLICY, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_EXPORT, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_MONITORING, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE, - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURES, - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_FEATURE_FLAGS, - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_BLEVE, - } - - SystemScopedPermissionsMinusSysconsole := []*Permission{ - PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE, - PERMISSION_MANAGE_ROLES, - PERMISSION_MANAGE_SYSTEM, - PERMISSION_CREATE_DIRECT_CHANNEL, - PERMISSION_CREATE_GROUP_CHANNEL, - PERMISSION_LIST_PUBLIC_TEAMS, - PERMISSION_JOIN_PUBLIC_TEAMS, - PERMISSION_LIST_PRIVATE_TEAMS, - PERMISSION_JOIN_PRIVATE_TEAMS, - PERMISSION_EDIT_OTHER_USERS, - PERMISSION_READ_OTHER_USERS_TEAMS, - PERMISSION_GET_PUBLIC_LINK, - PERMISSION_MANAGE_OAUTH, - PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH, - PERMISSION_CREATE_TEAM, - PERMISSION_LIST_USERS_WITHOUT_TEAM, - PERMISSION_CREATE_USER_ACCESS_TOKEN, - PERMISSION_READ_USER_ACCESS_TOKEN, - PERMISSION_REVOKE_USER_ACCESS_TOKEN, - PERMISSION_CREATE_BOT, - PERMISSION_ASSIGN_BOT, - PERMISSION_READ_BOTS, - PERMISSION_READ_OTHERS_BOTS, - PERMISSION_MANAGE_BOTS, - PERMISSION_MANAGE_OTHERS_BOTS, - PERMISSION_READ_JOBS, - PERMISSION_MANAGE_JOBS, - PERMISSION_PROMOTE_GUEST, - PERMISSION_DEMOTE_TO_GUEST, - PERMISSION_EDIT_BRAND, - PERMISSION_MANAGE_SHARED_CHANNELS, - PERMISSION_MANAGE_SECURE_CONNECTIONS, - PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT, - PERMISSION_CREATE_DATA_RETENTION_JOB, - PERMISSION_READ_DATA_RETENTION_JOB, - PERMISSION_CREATE_COMPLIANCE_EXPORT_JOB, - PERMISSION_READ_COMPLIANCE_EXPORT_JOB, - PERMISSION_READ_AUDITS, - PERMISSION_TEST_SITE_URL, - PERMISSION_TEST_ELASTICSEARCH, - PERMISSION_TEST_S3, - PERMISSION_RELOAD_CONFIG, - PERMISSION_INVALIDATE_CACHES, - PERMISSION_RECYCLE_DATABASE_CONNECTIONS, - PERMISSION_PURGE_ELASTICSEARCH_INDEXES, - PERMISSION_TEST_EMAIL, - PERMISSION_CREATE_ELASTICSEARCH_POST_INDEXING_JOB, - PERMISSION_CREATE_ELASTICSEARCH_POST_AGGREGATION_JOB, - PERMISSION_READ_ELASTICSEARCH_POST_INDEXING_JOB, - PERMISSION_READ_ELASTICSEARCH_POST_AGGREGATION_JOB, - PERMISSION_PURGE_BLEVE_INDEXES, - PERMISSION_CREATE_POST_BLEVE_INDEXES_JOB, - PERMISSION_CREATE_LDAP_SYNC_JOB, - PERMISSION_READ_LDAP_SYNC_JOB, - PERMISSION_TEST_LDAP, - PERMISSION_INVALIDATE_EMAIL_INVITE, - PERMISSION_GET_SAML_METADATA_FROM_IDP, - PERMISSION_ADD_SAML_PUBLIC_CERT, - PERMISSION_ADD_SAML_PRIVATE_CERT, - PERMISSION_ADD_SAML_IDP_CERT, - PERMISSION_REMOVE_SAML_PUBLIC_CERT, - PERMISSION_REMOVE_SAML_PRIVATE_CERT, - PERMISSION_REMOVE_SAML_IDP_CERT, - PERMISSION_GET_SAML_CERT_STATUS, - PERMISSION_ADD_LDAP_PUBLIC_CERT, - PERMISSION_ADD_LDAP_PRIVATE_CERT, - PERMISSION_REMOVE_LDAP_PUBLIC_CERT, - PERMISSION_REMOVE_LDAP_PRIVATE_CERT, - PERMISSION_GET_ANALYTICS, - PERMISSION_GET_LOGS, - PERMISSION_READ_LICENSE_INFORMATION, - PERMISSION_MANAGE_LICENSE_INFORMATION, - } - - TeamScopedPermissions := []*Permission{ - PERMISSION_INVITE_USER, - PERMISSION_ADD_USER_TO_TEAM, - PERMISSION_MANAGE_SLASH_COMMANDS, - PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS, - PERMISSION_CREATE_PUBLIC_CHANNEL, - PERMISSION_CREATE_PRIVATE_CHANNEL, - PERMISSION_MANAGE_TEAM_ROLES, - PERMISSION_LIST_TEAM_CHANNELS, - PERMISSION_JOIN_PUBLIC_CHANNELS, - PERMISSION_READ_PUBLIC_CHANNEL, - PERMISSION_MANAGE_INCOMING_WEBHOOKS, - PERMISSION_MANAGE_OUTGOING_WEBHOOKS, - PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS, - PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS, - PERMISSION_CREATE_EMOJIS, - PERMISSION_DELETE_EMOJIS, - PERMISSION_DELETE_OTHERS_EMOJIS, - PERMISSION_REMOVE_USER_FROM_TEAM, - PERMISSION_MANAGE_TEAM, - PERMISSION_IMPORT_TEAM, - PERMISSION_VIEW_TEAM, - PERMISSION_VIEW_MEMBERS, - PERMISSION_INVITE_GUEST, - } - - ChannelScopedPermissions := []*Permission{ - PERMISSION_USE_SLASH_COMMANDS, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS, - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS, - PERMISSION_MANAGE_CHANNEL_ROLES, - PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES, - PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES, - PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, - PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC, - PERMISSION_DELETE_PUBLIC_CHANNEL, - PERMISSION_DELETE_PRIVATE_CHANNEL, - PERMISSION_READ_CHANNEL, - PERMISSION_READ_PUBLIC_CHANNEL_GROUPS, - PERMISSION_READ_PRIVATE_CHANNEL_GROUPS, - PERMISSION_ADD_REACTION, - PERMISSION_REMOVE_REACTION, - PERMISSION_REMOVE_OTHERS_REACTIONS, - PERMISSION_UPLOAD_FILE, - PERMISSION_CREATE_POST, - PERMISSION_CREATE_POST_PUBLIC, - PERMISSION_CREATE_POST_EPHEMERAL, - PERMISSION_EDIT_POST, - PERMISSION_EDIT_OTHERS_POSTS, - PERMISSION_DELETE_POST, - PERMISSION_DELETE_OTHERS_POSTS, - PERMISSION_USE_CHANNEL_MENTIONS, - PERMISSION_USE_GROUP_MENTIONS, - } - - DeprecatedPermissions = []*Permission{ - PERMISSION_PERMANENT_DELETE_USER, - PERMISSION_MANAGE_WEBHOOKS, - PERMISSION_MANAGE_OTHERS_WEBHOOKS, - PERMISSION_MANAGE_EMOJIS, - PERMISSION_MANAGE_OTHERS_EMOJIS, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION, - PERMISSION_SYSCONSOLE_READ_SITE, - PERMISSION_SYSCONSOLE_WRITE_SITE, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT, - PERMISSION_SYSCONSOLE_READ_REPORTING, - PERMISSION_SYSCONSOLE_WRITE_REPORTING, - PERMISSION_SYSCONSOLE_READ_ABOUT, - PERMISSION_SYSCONSOLE_WRITE_ABOUT, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL, - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE, - } - - AllPermissions = []*Permission{} - AllPermissions = append(AllPermissions, SystemScopedPermissionsMinusSysconsole...) - AllPermissions = append(AllPermissions, TeamScopedPermissions...) - AllPermissions = append(AllPermissions, ChannelScopedPermissions...) - AllPermissions = append(AllPermissions, SysconsoleReadPermissions...) - AllPermissions = append(AllPermissions, SysconsoleWritePermissions...) - - ChannelModeratedPermissions = []string{ - PERMISSION_CREATE_POST.Id, - "create_reactions", - "manage_members", - PERMISSION_USE_CHANNEL_MENTIONS.Id, - } - - ChannelModeratedPermissionsMap = map[string]string{ - PERMISSION_CREATE_POST.Id: ChannelModeratedPermissions[0], - PERMISSION_ADD_REACTION.Id: ChannelModeratedPermissions[1], - PERMISSION_REMOVE_REACTION.Id: ChannelModeratedPermissions[1], - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id: ChannelModeratedPermissions[2], - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id: ChannelModeratedPermissions[2], - PERMISSION_USE_CHANNEL_MENTIONS.Id: ChannelModeratedPermissions[3], - } -} - -func init() { - initializePermissions() -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_cluster_event.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_cluster_event.go deleted file mode 100644 index ba5c8052..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_cluster_event.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - PluginClusterEventSendTypeReliable = CLUSTER_SEND_RELIABLE - PluginClusterEventSendTypeBestEffort = CLUSTER_SEND_BEST_EFFORT -) - -// PluginClusterEvent is used to allow intra-cluster plugin communication. -type PluginClusterEvent struct { - // Id is the unique identifier for the event. - Id string - // Data is the event payload. - Data []byte -} - -// PluginClusterEventSendOptions defines some properties that apply when sending -// plugin events across a cluster. -type PluginClusterEventSendOptions struct { - // SendType defines the type of communication channel used to send the event. - SendType string - // TargetId identifies the cluster node to which the event should be sent. - // It should match the cluster id of the receiving instance. - // If empty, the event gets broadcasted to all other nodes. - TargetId string -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_event_data.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_event_data.go deleted file mode 100644 index c704c993..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_event_data.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -// PluginEventData used to notify peers about plugin changes. -type PluginEventData struct { - Id string `json:"id"` -} - -func (p *PluginEventData) ToJson() string { - b, _ := json.Marshal(p) - return string(b) -} - -func PluginEventDataFromJson(data io.Reader) PluginEventData { - var m PluginEventData - json.NewDecoder(data).Decode(&m) - return m -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_key_value.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_key_value.go deleted file mode 100644 index 73ef2d23..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_key_value.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "net/http" - "unicode/utf8" -) - -const ( - KEY_VALUE_PLUGIN_ID_MAX_RUNES = 190 - KEY_VALUE_KEY_MAX_RUNES = 50 -) - -type PluginKeyValue struct { - PluginId string `json:"plugin_id"` - Key string `json:"key" db:"PKey"` - Value []byte `json:"value" db:"PValue"` - ExpireAt int64 `json:"expire_at"` -} - -func (kv *PluginKeyValue) IsValid() *AppError { - if kv.PluginId == "" || utf8.RuneCountInString(kv.PluginId) > KEY_VALUE_PLUGIN_ID_MAX_RUNES { - return NewAppError("PluginKeyValue.IsValid", "model.plugin_key_value.is_valid.plugin_id.app_error", map[string]interface{}{"Max": KEY_VALUE_KEY_MAX_RUNES, "Min": 0}, "key="+kv.Key, http.StatusBadRequest) - } - - if kv.Key == "" || utf8.RuneCountInString(kv.Key) > KEY_VALUE_KEY_MAX_RUNES { - return NewAppError("PluginKeyValue.IsValid", "model.plugin_key_value.is_valid.key.app_error", map[string]interface{}{"Max": KEY_VALUE_KEY_MAX_RUNES, "Min": 0}, "key="+kv.Key, http.StatusBadRequest) - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_kvset_options.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_kvset_options.go deleted file mode 100644 index 1d374c80..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_kvset_options.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "net/http" -) - -// PluginKVSetOptions contains information on how to store a value in the plugin KV store. -type PluginKVSetOptions struct { - Atomic bool // Only store the value if the current value matches the oldValue - OldValue []byte // The value to compare with the current value. Only used when Atomic is true - ExpireInSeconds int64 // Set an expire counter -} - -// IsValid returns nil if the chosen options are valid. -func (opt *PluginKVSetOptions) IsValid() *AppError { - if !opt.Atomic && opt.OldValue != nil { - return NewAppError( - "PluginKVSetOptions.IsValid", - "model.plugin_kvset_options.is_valid.old_value.app_error", - nil, - "", - http.StatusBadRequest, - ) - } - - return nil -} - -// NewPluginKeyValueFromOptions return a PluginKeyValue given a pluginID, a KV pair and options. -func NewPluginKeyValueFromOptions(pluginId, key string, value []byte, opt PluginKVSetOptions) (*PluginKeyValue, *AppError) { - expireAt := int64(0) - if opt.ExpireInSeconds != 0 { - expireAt = GetMillis() + (opt.ExpireInSeconds * 1000) - } - - kv := &PluginKeyValue{ - PluginId: pluginId, - Key: key, - Value: value, - ExpireAt: expireAt, - } - - return kv, nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_status.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_status.go deleted file mode 100644 index b4ba2e73..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_status.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const ( - PluginStateNotRunning = 0 - PluginStateStarting = 1 // unused by server - PluginStateRunning = 2 - PluginStateFailedToStart = 3 - PluginStateFailedToStayRunning = 4 - PluginStateStopping = 5 // unused by server -) - -// PluginStatus provides a cluster-aware view of installed plugins. -type PluginStatus struct { - PluginId string `json:"plugin_id"` - ClusterId string `json:"cluster_id"` - PluginPath string `json:"plugin_path"` - State int `json:"state"` - Name string `json:"name"` - Description string `json:"description"` - Version string `json:"version"` -} - -type PluginStatuses []*PluginStatus - -func (m *PluginStatuses) ToJson() string { - b, _ := json.Marshal(m) - return string(b) -} - -func PluginStatusesFromJson(data io.Reader) PluginStatuses { - var m PluginStatuses - json.NewDecoder(data).Decode(&m) - return m -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_valid.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_valid.go deleted file mode 100644 index b6144513..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugin_valid.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "regexp" - "unicode/utf8" -) - -const ( - MinIdLength = 3 - MaxIdLength = 190 - ValidIdRegex = `^[a-zA-Z0-9-_\.]+$` -) - -// ValidId constrains the set of valid plugin identifiers: -// ^[a-zA-Z0-9-_\.]+ -var validId *regexp.Regexp - -func init() { - validId = regexp.MustCompile(ValidIdRegex) -} - -// IsValidPluginId verifies that the plugin id has a minimum length of 3, maximum length of 190, and -// contains only alphanumeric characters, dashes, underscores and periods. -// -// These constraints are necessary since the plugin id is used as part of a filesystem path. -func IsValidPluginId(id string) bool { - if utf8.RuneCountInString(id) < MinIdLength { - return false - } - - if utf8.RuneCountInString(id) > MaxIdLength { - return false - } - - return validId.MatchString(id) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/plugins_response.go b/vendor/github.com/mattermost/mattermost-server/v5/model/plugins_response.go deleted file mode 100644 index 421ee2f5..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/plugins_response.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type PluginInfo struct { - Manifest -} - -type PluginsResponse struct { - Active []*PluginInfo `json:"active"` - Inactive []*PluginInfo `json:"inactive"` -} - -func (m *PluginsResponse) ToJson() string { - b, _ := json.Marshal(m) - return string(b) -} - -func PluginsResponseFromJson(data io.Reader) *PluginsResponse { - var m *PluginsResponse - json.NewDecoder(data).Decode(&m) - return m -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/post.go b/vendor/github.com/mattermost/mattermost-server/v5/model/post.go deleted file mode 100644 index b7dff5bc..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/post.go +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "errors" - "io" - "net/http" - "regexp" - "sort" - "strings" - "sync" - "unicode/utf8" - - "github.com/mattermost/mattermost-server/v5/shared/markdown" -) - -const ( - POST_SYSTEM_MESSAGE_PREFIX = "system_" - POST_DEFAULT = "" - POST_SLACK_ATTACHMENT = "slack_attachment" - POST_SYSTEM_GENERIC = "system_generic" - POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead - POST_JOIN_CHANNEL = "system_join_channel" - POST_GUEST_JOIN_CHANNEL = "system_guest_join_channel" - POST_LEAVE_CHANNEL = "system_leave_channel" - POST_JOIN_TEAM = "system_join_team" - POST_LEAVE_TEAM = "system_leave_team" - POST_AUTO_RESPONDER = "system_auto_responder" - 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_ADD_GUEST_TO_CHANNEL = "system_add_guest_to_chan" - POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel" - POST_MOVE_CHANNEL = "system_move_channel" - POST_ADD_TO_TEAM = "system_add_to_team" - POST_REMOVE_FROM_TEAM = "system_remove_from_team" - POST_HEADER_CHANGE = "system_header_change" - POST_DISPLAYNAME_CHANGE = "system_displayname_change" - POST_CONVERT_CHANNEL = "system_convert_channel" - POST_PURPOSE_CHANGE = "system_purpose_change" - POST_CHANNEL_DELETED = "system_channel_deleted" - POST_CHANNEL_RESTORED = "system_channel_restored" - POST_EPHEMERAL = "system_ephemeral" - POST_CHANGE_CHANNEL_PRIVACY = "system_change_chan_privacy" - POST_ADD_BOT_TEAMS_CHANNELS = "add_bot_teams_channels" - POST_FILEIDS_MAX_RUNES = 300 - POST_FILENAMES_MAX_RUNES = 4000 - POST_HASHTAGS_MAX_RUNES = 1000 - POST_MESSAGE_MAX_RUNES_V1 = 4000 - POST_MESSAGE_MAX_BYTES_V2 = 65535 // Maximum size of a TEXT column in MySQL - POST_MESSAGE_MAX_RUNES_V2 = POST_MESSAGE_MAX_BYTES_V2 / 4 // Assume a worst-case representation - POST_PROPS_MAX_RUNES = 8000 - POST_PROPS_MAX_USER_RUNES = POST_PROPS_MAX_RUNES - 400 // Leave some room for system / pre-save modifications - POST_CUSTOM_TYPE_PREFIX = "custom_" - POST_ME = "me" - PROPS_ADD_CHANNEL_MEMBER = "add_channel_member" - - POST_PROPS_ADDED_USER_ID = "addedUserId" - POST_PROPS_DELETE_BY = "deleteBy" - POST_PROPS_OVERRIDE_ICON_URL = "override_icon_url" - POST_PROPS_OVERRIDE_ICON_EMOJI = "override_icon_emoji" - - POST_PROPS_MENTION_HIGHLIGHT_DISABLED = "mentionHighlightDisabled" - POST_PROPS_GROUP_HIGHLIGHT_DISABLED = "disable_group_highlight" - POST_SYSTEM_WARN_METRIC_STATUS = "warn_metric_status" -) - -var AT_MENTION_PATTEN = regexp.MustCompile(`\B@`) - -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"` - IsPinned bool `json:"is_pinned"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - RootId string `json:"root_id"` - ParentId string `json:"parent_id"` - OriginalId string `json:"original_id"` - - Message string `json:"message"` - // MessageSource will contain the message as submitted by the user if Message has been modified - // by Mattermost for presentation (e.g if an image proxy is being used). It should be used to - // populate edit boxes if present. - MessageSource string `json:"message_source,omitempty" db:"-"` - - Type string `json:"type"` - propsMu sync.RWMutex `db:"-"` // Unexported mutex used to guard Post.Props. - Props StringInterface `json:"props"` // Deprecated: use GetProps() - Hashtags string `json:"hashtags"` - Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more - FileIds StringArray `json:"file_ids,omitempty"` - PendingPostId string `json:"pending_post_id" db:"-"` - HasReactions bool `json:"has_reactions,omitempty"` - RemoteId *string `json:"remote_id,omitempty"` - - // Transient data populated before sending a post to the client - ReplyCount int64 `json:"reply_count" db:"-"` - LastReplyAt int64 `json:"last_reply_at" db:"-"` - Participants []*User `json:"participants" db:"-"` - IsFollowing *bool `json:"is_following,omitempty" db:"-"` // for root posts in collapsed thread mode indicates if the current user is following this thread - Metadata *PostMetadata `json:"metadata,omitempty" db:"-"` -} - -type PostEphemeral struct { - UserID string `json:"user_id"` - Post *Post `json:"post"` -} - -type PostPatch struct { - IsPinned *bool `json:"is_pinned"` - Message *string `json:"message"` - Props *StringInterface `json:"props"` - FileIds *StringArray `json:"file_ids"` - HasReactions *bool `json:"has_reactions"` -} - -type SearchParameter struct { - Terms *string `json:"terms"` - IsOrSearch *bool `json:"is_or_search"` - TimeZoneOffset *int `json:"time_zone_offset"` - Page *int `json:"page"` - PerPage *int `json:"per_page"` - IncludeDeletedChannels *bool `json:"include_deleted_channels"` -} - -type AnalyticsPostCountsOptions struct { - TeamId string - BotsOnly bool - YesterdayOnly bool -} - -func (o *PostPatch) WithRewrittenImageURLs(f func(string) string) *PostPatch { - copy := *o - if copy.Message != nil { - *copy.Message = RewriteImageURLs(*o.Message, f) - } - return © -} - -type PostForExport struct { - Post - TeamName string - ChannelName string - Username string - ReplyCount int -} - -type DirectPostForExport struct { - Post - User string - ChannelMembers *[]string -} - -type ReplyForExport struct { - Post - Username string -} - -type PostForIndexing struct { - Post - TeamId string `json:"team_id"` - ParentCreateAt *int64 `json:"parent_create_at"` -} - -type FileForIndexing struct { - FileInfo - ChannelId string `json:"channel_id"` - Content string `json:"content"` -} - -// ShallowCopy is an utility function to shallow copy a Post to the given -// destination without touching the internal RWMutex. -func (o *Post) ShallowCopy(dst *Post) error { - if dst == nil { - return errors.New("dst cannot be nil") - } - o.propsMu.RLock() - defer o.propsMu.RUnlock() - dst.propsMu.Lock() - defer dst.propsMu.Unlock() - dst.Id = o.Id - dst.CreateAt = o.CreateAt - dst.UpdateAt = o.UpdateAt - dst.EditAt = o.EditAt - dst.DeleteAt = o.DeleteAt - dst.IsPinned = o.IsPinned - dst.UserId = o.UserId - dst.ChannelId = o.ChannelId - dst.RootId = o.RootId - dst.ParentId = o.ParentId - dst.OriginalId = o.OriginalId - dst.Message = o.Message - dst.MessageSource = o.MessageSource - dst.Type = o.Type - dst.Props = o.Props - dst.Hashtags = o.Hashtags - dst.Filenames = o.Filenames - dst.FileIds = o.FileIds - dst.PendingPostId = o.PendingPostId - dst.HasReactions = o.HasReactions - dst.ReplyCount = o.ReplyCount - dst.Participants = o.Participants - dst.LastReplyAt = o.LastReplyAt - dst.Metadata = o.Metadata - if o.IsFollowing != nil { - dst.IsFollowing = NewBool(*o.IsFollowing) - } - dst.RemoteId = o.RemoteId - return nil -} - -// Clone shallowly copies the post and returns the copy. -func (o *Post) Clone() *Post { - copy := &Post{} - o.ShallowCopy(copy) - return copy -} - -func (o *Post) ToJson() string { - copy := o.Clone() - copy.StripActionIntegrations() - b, _ := json.Marshal(copy) - return string(b) -} - -func (o *Post) ToUnsanitizedJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -type GetPostsSinceOptions struct { - UserId string - ChannelId string - Time int64 - SkipFetchThreads bool - CollapsedThreads bool - CollapsedThreadsExtended bool - SortAscending bool -} - -type GetPostsSinceForSyncCursor struct { - LastPostUpdateAt int64 - LastPostId string -} - -type GetPostsSinceForSyncOptions struct { - ChannelId string - ExcludeRemoteId string - IncludeDeleted bool -} - -type GetPostsOptions struct { - UserId string - ChannelId string - PostId string - Page int - PerPage int - SkipFetchThreads bool - CollapsedThreads bool - CollapsedThreadsExtended bool -} - -func PostFromJson(data io.Reader) *Post { - var o *Post - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *Post) Etag() string { - return Etag(o.Id, o.UpdateAt) -} - -func (o *Post) IsValid(maxPostSize int) *AppError { - if !IsValidId(o.Id) { - return NewAppError("Post.IsValid", "model.post.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("Post.IsValid", "model.post.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("Post.IsValid", "model.post.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidId(o.UserId) { - return NewAppError("Post.IsValid", "model.post.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.ChannelId) { - return NewAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest) - } - - if !(IsValidId(o.RootId) || o.RootId == "") { - return NewAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "", http.StatusBadRequest) - } - - if !(IsValidId(o.ParentId) || o.ParentId == "") { - return NewAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(o.ParentId) == 26 && o.RootId == "" { - return NewAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "", http.StatusBadRequest) - } - - if !(len(o.OriginalId) == 26 || o.OriginalId == "") { - return NewAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "", http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.Message) > maxPostSize { - return NewAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.Hashtags) > POST_HASHTAGS_MAX_RUNES { - return NewAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - switch o.Type { - case - POST_DEFAULT, - POST_SYSTEM_GENERIC, - POST_JOIN_LEAVE, - POST_AUTO_RESPONDER, - POST_ADD_REMOVE, - POST_JOIN_CHANNEL, - POST_GUEST_JOIN_CHANNEL, - POST_LEAVE_CHANNEL, - POST_JOIN_TEAM, - POST_LEAVE_TEAM, - POST_ADD_TO_CHANNEL, - POST_ADD_GUEST_TO_CHANNEL, - POST_REMOVE_FROM_CHANNEL, - POST_MOVE_CHANNEL, - POST_ADD_TO_TEAM, - POST_REMOVE_FROM_TEAM, - POST_SLACK_ATTACHMENT, - POST_HEADER_CHANGE, - POST_PURPOSE_CHANGE, - POST_DISPLAYNAME_CHANGE, - POST_CONVERT_CHANNEL, - POST_CHANNEL_DELETED, - POST_CHANNEL_RESTORED, - POST_CHANGE_CHANNEL_PRIVACY, - POST_ME, - POST_ADD_BOT_TEAMS_CHANNELS, - POST_SYSTEM_WARN_METRIC_STATUS: - default: - if !strings.HasPrefix(o.Type, POST_CUSTOM_TYPE_PREFIX) { - return NewAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type, http.StatusBadRequest) - } - } - - if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > POST_FILENAMES_MAX_RUNES { - return NewAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > POST_FILEIDS_MAX_RUNES { - return NewAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if utf8.RuneCountInString(StringInterfaceToJson(o.GetProps())) > POST_PROPS_MAX_RUNES { - return NewAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - return nil -} - -func (o *Post) SanitizeProps() { - if o == nil { - return - } - membersToSanitize := []string{ - PROPS_ADD_CHANNEL_MEMBER, - } - - for _, member := range membersToSanitize { - if _, ok := o.GetProps()[member]; ok { - o.DelProp(member) - } - } - for _, p := range o.Participants { - p.Sanitize(map[string]bool{}) - } -} - -func (o *Post) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - o.OriginalId = "" - - if o.CreateAt == 0 { - o.CreateAt = GetMillis() - } - - o.UpdateAt = o.CreateAt - o.PreCommit() -} - -func (o *Post) PreCommit() { - if o.GetProps() == nil { - o.SetProps(make(map[string]interface{})) - } - - if o.Filenames == nil { - o.Filenames = []string{} - } - - if o.FileIds == nil { - o.FileIds = []string{} - } - - o.GenerateActionIds() - - // There's a rare bug where the client sends up duplicate FileIds so protect against that - o.FileIds = RemoveDuplicateStrings(o.FileIds) -} - -func (o *Post) MakeNonNil() { - if o.GetProps() == nil { - o.SetProps(make(map[string]interface{})) - } -} - -func (o *Post) DelProp(key string) { - o.propsMu.Lock() - defer o.propsMu.Unlock() - propsCopy := make(map[string]interface{}, len(o.Props)-1) - for k, v := range o.Props { - propsCopy[k] = v - } - delete(propsCopy, key) - o.Props = propsCopy -} - -func (o *Post) AddProp(key string, value interface{}) { - o.propsMu.Lock() - defer o.propsMu.Unlock() - propsCopy := make(map[string]interface{}, len(o.Props)+1) - for k, v := range o.Props { - propsCopy[k] = v - } - propsCopy[key] = value - o.Props = propsCopy -} - -func (o *Post) GetProps() StringInterface { - o.propsMu.RLock() - defer o.propsMu.RUnlock() - return o.Props -} - -func (o *Post) SetProps(props StringInterface) { - o.propsMu.Lock() - defer o.propsMu.Unlock() - o.Props = props -} - -func (o *Post) GetProp(key string) interface{} { - o.propsMu.RLock() - defer o.propsMu.RUnlock() - return o.Props[key] -} - -func (o *Post) IsSystemMessage() bool { - return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX -} - -// IsRemote returns true if the post originated on a remote cluster. -func (o *Post) IsRemote() bool { - return o.RemoteId != nil && *o.RemoteId != "" -} - -// GetRemoteID safely returns the remoteID or empty string if not remote. -func (o *Post) GetRemoteID() string { - if o.RemoteId != nil { - return *o.RemoteId - } - return "" -} - -func (o *Post) IsJoinLeaveMessage() bool { - return o.Type == POST_JOIN_LEAVE || - o.Type == POST_ADD_REMOVE || - o.Type == POST_JOIN_CHANNEL || - o.Type == POST_LEAVE_CHANNEL || - o.Type == POST_JOIN_TEAM || - o.Type == POST_LEAVE_TEAM || - o.Type == POST_ADD_TO_CHANNEL || - o.Type == POST_REMOVE_FROM_CHANNEL || - o.Type == POST_ADD_TO_TEAM || - o.Type == POST_REMOVE_FROM_TEAM -} - -func (o *Post) Patch(patch *PostPatch) { - if patch.IsPinned != nil { - o.IsPinned = *patch.IsPinned - } - - if patch.Message != nil { - o.Message = *patch.Message - } - - if patch.Props != nil { - newProps := *patch.Props - o.SetProps(newProps) - } - - if patch.FileIds != nil { - o.FileIds = *patch.FileIds - } - - if patch.HasReactions != nil { - o.HasReactions = *patch.HasReactions - } -} - -func (o *PostPatch) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } - - return string(b) -} - -func PostPatchFromJson(data io.Reader) *PostPatch { - decoder := json.NewDecoder(data) - var post PostPatch - err := decoder.Decode(&post) - if err != nil { - return nil - } - - return &post -} - -func (o *SearchParameter) SearchParameterToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" - } - - return string(b) -} - -func SearchParameterFromJson(data io.Reader) (*SearchParameter, error) { - decoder := json.NewDecoder(data) - var searchParam SearchParameter - if err := decoder.Decode(&searchParam); err != nil { - return nil, err - } - - return &searchParam, nil -} - -func (o *Post) ChannelMentions() []string { - return ChannelMentions(o.Message) -} - -// DisableMentionHighlights disables a posts mention highlighting and returns the first channel mention that was present in the message. -func (o *Post) DisableMentionHighlights() string { - mention, hasMentions := findAtChannelMention(o.Message) - if hasMentions { - o.AddProp(POST_PROPS_MENTION_HIGHLIGHT_DISABLED, true) - } - return mention -} - -// DisableMentionHighlights disables mention highlighting for a post patch if required. -func (o *PostPatch) DisableMentionHighlights() { - if o.Message == nil { - return - } - if _, hasMentions := findAtChannelMention(*o.Message); hasMentions { - if o.Props == nil { - o.Props = &StringInterface{} - } - (*o.Props)[POST_PROPS_MENTION_HIGHLIGHT_DISABLED] = true - } -} - -func findAtChannelMention(message string) (mention string, found bool) { - re := regexp.MustCompile(`(?i)\B@(channel|all|here)\b`) - matched := re.FindStringSubmatch(message) - if found = (len(matched) > 0); found { - mention = strings.ToLower(matched[0]) - } - return -} - -func (o *Post) Attachments() []*SlackAttachment { - if attachments, ok := o.GetProp("attachments").([]*SlackAttachment); ok { - return attachments - } - var ret []*SlackAttachment - if attachments, ok := o.GetProp("attachments").([]interface{}); ok { - for _, attachment := range attachments { - if enc, err := json.Marshal(attachment); err == nil { - var decoded SlackAttachment - if json.Unmarshal(enc, &decoded) == nil { - // Ignoring nil actions - i := 0 - for _, action := range decoded.Actions { - if action != nil { - decoded.Actions[i] = action - i++ - } - } - decoded.Actions = decoded.Actions[:i] - - // Ignoring nil fields - i = 0 - for _, field := range decoded.Fields { - if field != nil { - decoded.Fields[i] = field - i++ - } - } - decoded.Fields = decoded.Fields[:i] - ret = append(ret, &decoded) - } - } - } - } - return ret -} - -func (o *Post) AttachmentsEqual(input *Post) bool { - attachments := o.Attachments() - inputAttachments := input.Attachments() - - if len(attachments) != len(inputAttachments) { - return false - } - - for i := range attachments { - if !attachments[i].Equals(inputAttachments[i]) { - return false - } - } - - return true -} - -var markdownDestinationEscaper = strings.NewReplacer( - `\`, `\\`, - `<`, `\<`, - `>`, `\>`, - `(`, `\(`, - `)`, `\)`, -) - -// WithRewrittenImageURLs returns a new shallow copy of the post where the message has been -// rewritten via RewriteImageURLs. -func (o *Post) WithRewrittenImageURLs(f func(string) string) *Post { - copy := o.Clone() - copy.Message = RewriteImageURLs(o.Message, f) - if copy.MessageSource == "" && copy.Message != o.Message { - copy.MessageSource = o.Message - } - return copy -} - -func (o *PostEphemeral) ToUnsanitizedJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -// RewriteImageURLs takes a message and returns a copy that has all of the image URLs replaced -// according to the function f. For each image URL, f will be invoked, and the resulting markdown -// will contain the URL returned by that invocation instead. -// -// Image URLs are destination URLs used in inline images or reference definitions that are used -// anywhere in the input markdown as an image. -func RewriteImageURLs(message string, f func(string) string) string { - if !strings.Contains(message, "![") { - return message - } - - var ranges []markdown.Range - - markdown.Inspect(message, func(blockOrInline interface{}) bool { - switch v := blockOrInline.(type) { - case *markdown.ReferenceImage: - ranges = append(ranges, v.ReferenceDefinition.RawDestination) - case *markdown.InlineImage: - ranges = append(ranges, v.RawDestination) - default: - return true - } - return true - }) - - if ranges == nil { - return message - } - - sort.Slice(ranges, func(i, j int) bool { - return ranges[i].Position < ranges[j].Position - }) - - copyRanges := make([]markdown.Range, 0, len(ranges)) - urls := make([]string, 0, len(ranges)) - resultLength := len(message) - - start := 0 - for i, r := range ranges { - switch { - case i == 0: - case r.Position != ranges[i-1].Position: - start = ranges[i-1].End - default: - continue - } - original := message[r.Position:r.End] - replacement := markdownDestinationEscaper.Replace(f(markdown.Unescape(original))) - resultLength += len(replacement) - len(original) - copyRanges = append(copyRanges, markdown.Range{Position: start, End: r.Position}) - urls = append(urls, replacement) - } - - result := make([]byte, resultLength) - - offset := 0 - for i, r := range copyRanges { - offset += copy(result[offset:], message[r.Position:r.End]) - offset += copy(result[offset:], urls[i]) - } - copy(result[offset:], message[ranges[len(ranges)-1].End:]) - - return string(result) -} - -func (o *Post) IsFromOAuthBot() bool { - props := o.GetProps() - return props["from_webhook"] == "true" && props["override_username"] != "" -} - -func (o *Post) ToNilIfInvalid() *Post { - if o.Id == "" { - return nil - } - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/post_embed.go b/vendor/github.com/mattermost/mattermost-server/v5/model/post_embed.go deleted file mode 100644 index 5c6efec1..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/post_embed.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -const ( - POST_EMBED_IMAGE PostEmbedType = "image" - POST_EMBED_MESSAGE_ATTACHMENT PostEmbedType = "message_attachment" - POST_EMBED_OPENGRAPH PostEmbedType = "opengraph" - POST_EMBED_LINK PostEmbedType = "link" -) - -type PostEmbedType string - -type PostEmbed struct { - Type PostEmbedType `json:"type"` - - // The URL of the embedded content. Used for image and OpenGraph embeds. - URL string `json:"url,omitempty"` - - // Any additional data for the embedded content. Only used for OpenGraph embeds. - Data interface{} `json:"data,omitempty"` -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/post_list.go b/vendor/github.com/mattermost/mattermost-server/v5/model/post_list.go deleted file mode 100644 index ba84f749..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/post_list.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "sort" -) - -type PostList struct { - Order []string `json:"order"` - Posts map[string]*Post `json:"posts"` - NextPostId string `json:"next_post_id"` - PrevPostId string `json:"prev_post_id"` -} - -func NewPostList() *PostList { - return &PostList{ - Order: make([]string, 0), - Posts: make(map[string]*Post), - NextPostId: "", - PrevPostId: "", - } -} - -func (o *PostList) ToSlice() []*Post { - var posts []*Post - - if l := len(o.Posts); l > 0 { - posts = make([]*Post, 0, l) - } - - for _, id := range o.Order { - posts = append(posts, o.Posts[id]) - } - return posts -} - -func (o *PostList) WithRewrittenImageURLs(f func(string) string) *PostList { - copy := *o - copy.Posts = make(map[string]*Post) - for id, post := range o.Posts { - copy.Posts[id] = post.WithRewrittenImageURLs(f) - } - return © -} - -func (o *PostList) StripActionIntegrations() { - posts := o.Posts - o.Posts = make(map[string]*Post) - for id, post := range posts { - pcopy := post.Clone() - pcopy.StripActionIntegrations() - o.Posts[id] = pcopy - } -} - -func (o *PostList) ToJson() string { - copy := *o - copy.StripActionIntegrations() - b, err := json.Marshal(©) - if err != nil { - return "" - } - return string(b) -} - -func (o *PostList) MakeNonNil() { - if o.Order == nil { - o.Order = make([]string, 0) - } - - if o.Posts == nil { - o.Posts = make(map[string]*Post) - } - - for _, v := range o.Posts { - v.MakeNonNil() - } -} - -func (o *PostList) AddOrder(id string) { - - if o.Order == nil { - o.Order = make([]string, 0, 128) - } - - o.Order = append(o.Order, id) -} - -func (o *PostList) AddPost(post *Post) { - - if o.Posts == nil { - o.Posts = make(map[string]*Post) - } - - o.Posts[post.Id] = post -} - -func (o *PostList) UniqueOrder() { - keys := make(map[string]bool) - order := []string{} - for _, postId := range o.Order { - if _, value := keys[postId]; !value { - keys[postId] = true - order = append(order, postId) - } - } - - o.Order = order -} - -func (o *PostList) Extend(other *PostList) { - for postId := range other.Posts { - o.AddPost(other.Posts[postId]) - } - - for _, postId := range other.Order { - o.AddOrder(postId) - } - - o.UniqueOrder() -} - -func (o *PostList) SortByCreateAt() { - sort.Slice(o.Order, func(i, j int) bool { - return o.Posts[o.Order[i]].CreateAt > o.Posts[o.Order[j]].CreateAt - }) -} - -func (o *PostList) Etag() string { - - id := "0" - var t int64 = 0 - - for _, v := range o.Posts { - if v.UpdateAt > t { - t = v.UpdateAt - id = v.Id - } else if v.UpdateAt == t && v.Id > id { - t = v.UpdateAt - id = v.Id - } - } - - orderId := "" - if len(o.Order) > 0 { - orderId = o.Order[0] - } - - return Etag(orderId, id, t) -} - -func (o *PostList) IsChannelId(channelId string) bool { - for _, v := range o.Posts { - if v.ChannelId != channelId { - return false - } - } - - return true -} - -func PostListFromJson(data io.Reader) *PostList { - var o *PostList - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/post_metadata.go b/vendor/github.com/mattermost/mattermost-server/v5/model/post_metadata.go deleted file mode 100644 index 7b0687ca..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/post_metadata.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" -) - -type PostMetadata struct { - // Embeds holds information required to render content embedded in the post. This includes the OpenGraph metadata - // for links in the post. - Embeds []*PostEmbed `json:"embeds,omitempty"` - - // Emojis holds all custom emojis used in the post or used in reaction to the post. - Emojis []*Emoji `json:"emojis,omitempty"` - - // Files holds information about the file attachments on the post. - Files []*FileInfo `json:"files,omitempty"` - - // Images holds the dimensions of all external images in the post as a map of the image URL to its diemsnions. - // This includes image embeds (when the message contains a plaintext link to an image), Markdown images, images - // contained in the OpenGraph metadata, and images contained in message attachments. It does not contain - // the dimensions of any file attachments as those are stored in FileInfos. - Images map[string]*PostImage `json:"images,omitempty"` - - // Reactions holds reactions made to the post. - Reactions []*Reaction `json:"reactions,omitempty"` -} - -type PostImage struct { - Width int `json:"width"` - Height int `json:"height"` - - // Format is the name of the image format as used by image/go such as "png", "gif", or "jpeg". - Format string `json:"format"` - - // FrameCount stores the number of frames in this image, if it is an animated gif. It will be 0 for other formats. - FrameCount int `json:"frame_count"` -} - -func (o *PostImage) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/post_search_results.go b/vendor/github.com/mattermost/mattermost-server/v5/model/post_search_results.go deleted file mode 100644 index fc9ba083..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/post_search_results.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type PostSearchMatches map[string][]string - -type PostSearchResults struct { - *PostList - Matches PostSearchMatches `json:"matches"` -} - -func MakePostSearchResults(posts *PostList, matches PostSearchMatches) *PostSearchResults { - return &PostSearchResults{ - posts, - matches, - } -} - -func (o *PostSearchResults) ToJson() string { - copy := *o - copy.PostList.StripActionIntegrations() - b, err := json.Marshal(©) - if err != nil { - return "" - } - return string(b) -} - -func PostSearchResultsFromJson(data io.Reader) *PostSearchResults { - var o *PostSearchResults - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/preference.go b/vendor/github.com/mattermost/mattermost-server/v5/model/preference.go deleted file mode 100644 index ee0d21aa..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/preference.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "regexp" - "strings" - "unicode/utf8" -) - -const ( - PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show" - PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW = "group_channel_show" - PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" - PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" - PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post" - PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel" - PREFERENCE_CATEGORY_SIDEBAR_SETTINGS = "sidebar_settings" - - PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" - PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED = "collapsed_reply_threads" - PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode" - PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" - PREFERENCE_NAME_MESSAGE_DISPLAY = "message_display" - PREFERENCE_NAME_NAME_FORMAT = "name_format" - PREFERENCE_NAME_USE_MILITARY_TIME = "use_military_time" - PREFERENCE_RECOMMENDED_NEXT_STEPS = "recommended_next_steps" - - PREFERENCE_CATEGORY_THEME = "theme" - // the name for theme props is the team id - - PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app" - // the name for oauth_app is the client_id and value is the current scope - - PREFERENCE_CATEGORY_LAST = "last" - PREFERENCE_NAME_LAST_CHANNEL = "channel" - PREFERENCE_NAME_LAST_TEAM = "team" - - PREFERENCE_CATEGORY_CUSTOM_STATUS = "custom_status" - PREFERENCE_NAME_RECENT_CUSTOM_STATUSES = "recent_custom_statuses" - PREFERENCE_NAME_CUSTOM_STATUS_TUTORIAL_STATE = "custom_status_tutorial_state" - - PREFERENCE_CUSTOM_STATUS_MODAL_VIEWED = "custom_status_modal_viewed" - - PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications" - PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval" - - PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS = "30" // the "immediate" setting is actually 30s - PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS = "900" // fifteen minutes is 900 seconds - PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY = "immediately" - PREFERENCE_EMAIL_INTERVAL_FIFTEEN = "fifteen" - PREFERENCE_EMAIL_INTERVAL_FIFTEEN_AS_SECONDS = "900" - PREFERENCE_EMAIL_INTERVAL_HOUR = "hour" - PREFERENCE_EMAIL_INTERVAL_HOUR_AS_SECONDS = "3600" -) - -type Preference struct { - UserId string `json:"user_id"` - Category string `json:"category"` - Name string `json:"name"` - Value string `json:"value"` -} - -func (o *Preference) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func PreferenceFromJson(data io.Reader) *Preference { - var o *Preference - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *Preference) IsValid() *AppError { - if !IsValidId(o.UserId) { - return NewAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest) - } - - if o.Category == "" || len(o.Category) > 32 { - return NewAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category, http.StatusBadRequest) - } - - if len(o.Name) > 32 { - return NewAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name, http.StatusBadRequest) - } - - if utf8.RuneCountInString(o.Value) > 2000 { - return NewAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value, http.StatusBadRequest) - } - - if o.Category == PREFERENCE_CATEGORY_THEME { - var unused map[string]string - if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil { - return NewAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value, http.StatusBadRequest) - } - } - - return nil -} - -func (o *Preference) PreUpdate() { - if o.Category == PREFERENCE_CATEGORY_THEME { - // decode the value of theme (a map of strings to string) and eliminate any invalid values - var props map[string]string - if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil { - // just continue, the invalid preference value should get caught by IsValid before saving - return - } - - colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`) - - // blank out any invalid theme values - for name, value := range props { - if name == "image" || name == "type" || name == "codeTheme" { - continue - } - - if !colorPattern.MatchString(value) { - props[name] = "#ffffff" - } - } - - if b, err := json.Marshal(props); err == nil { - o.Value = string(b) - } - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/preferences.go b/vendor/github.com/mattermost/mattermost-server/v5/model/preferences.go deleted file mode 100644 index c2d24865..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/preferences.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type Preferences []Preference - -func (o *Preferences) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func PreferencesFromJson(data io.Reader) (Preferences, error) { - decoder := json.NewDecoder(data) - var o Preferences - err := decoder.Decode(&o) - if err != nil { - return nil, err - } - return o, nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/product_notices.go b/vendor/github.com/mattermost/mattermost-server/v5/model/product_notices.go deleted file mode 100644 index 455ae475..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/product_notices.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - - "github.com/pkg/errors" -) - -type ProductNotices []ProductNotice - -func (r *ProductNotices) Marshal() ([]byte, error) { - return json.Marshal(r) -} - -func UnmarshalProductNotices(data []byte) (ProductNotices, error) { - var r ProductNotices - err := json.Unmarshal(data, &r) - return r, err -} - -// List of product notices. Order is important and is used to resolve priorities. -// Each notice will only be show if conditions are met. -type ProductNotice struct { - Conditions Conditions `json:"conditions"` - ID string `json:"id"` // Unique identifier for this notice. Can be a running number. Used for storing 'viewed'; state on the server. - LocalizedMessages map[string]NoticeMessageInternal `json:"localizedMessages"` // Notice message data, organized by locale.; Example:; "localizedMessages": {; "en": { "title": "English", description: "English description"},; "frFR": { "title": "Frances", description: "French description"}; } - Repeatable *bool `json:"repeatable,omitempty"` // Configurable flag if the notice should reappear after it’s seen and dismissed -} - -func (n *ProductNotice) SysAdminOnly() bool { - return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudience_Sysadmin -} - -func (n *ProductNotice) TeamAdminOnly() bool { - return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudience_TeamAdmin -} - -type Conditions struct { - Audience *NoticeAudience `json:"audience,omitempty"` - ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all' - DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"] - DisplayDate *string `json:"displayDate,omitempty"` // When to display the notice.; Examples:; "2020-03-01T00:00:00Z" - show on specified date; ">= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates - InstanceType *NoticeInstanceType `json:"instanceType,omitempty"` - MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"] - NumberOfPosts *int64 `json:"numberOfPosts,omitempty"` // Only show the notice when server has more than specified number of posts - NumberOfUsers *int64 `json:"numberOfUsers,omitempty"` // Only show the notice when server has more than specified number of users - ServerConfig map[string]interface{} `json:"serverConfig,omitempty"` // Map of mattermost server config paths and their values. Notice will be displayed only if; the values match the target server config; Example: serverConfig: { "PluginSettings.Enable": true, "GuestAccountsSettings.Enable":; false } - ServerVersion []string `json:"serverVersion,omitempty"` // What server versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"] - Sku *NoticeSKU `json:"sku,omitempty"` - UserConfig map[string]interface{} `json:"userConfig,omitempty"` // Map of user's settings and their values. Notice will be displayed only if the values; match the viewing users' config; Example: userConfig: { "new_sidebar.disabled": true } - DeprecatingDependency *ExternalDependency `json:"deprecating_dependency,omitempty"` // External dependency which is going to be deprecated -} - -type NoticeMessageInternal struct { - Action *NoticeAction `json:"action,omitempty"` // Optional action to perform on action button click. (defaults to closing the notice) - ActionParam *string `json:"actionParam,omitempty"` // Optional action parameter.; Example: {"action": "url", actionParam: "/console/some-page"} - ActionText *string `json:"actionText,omitempty"` // Optional override for the action button text (defaults to OK) - Description string `json:"description"` // Notice content. Use {{Mattermost}} instead of plain text to support white-labeling. Text; supports Markdown. - Image *string `json:"image,omitempty"` - Title string `json:"title"` // Notice title. Use {{Mattermost}} instead of plain text to support white-labeling. Text; supports Markdown. -} -type NoticeMessages []NoticeMessage - -type NoticeMessage struct { - NoticeMessageInternal - ID string `json:"id"` - SysAdminOnly bool `json:"sysAdminOnly"` - TeamAdminOnly bool `json:"teamAdminOnly"` -} - -func (r *NoticeMessages) Marshal() ([]byte, error) { - return json.Marshal(r) -} - -func UnmarshalProductNoticeMessages(data io.Reader) (NoticeMessages, error) { - var r NoticeMessages - err := json.NewDecoder(data).Decode(&r) - return r, err -} - -// User role, i.e. who will see the notice. Defaults to "all" -type NoticeAudience string - -func NewNoticeAudience(s NoticeAudience) *NoticeAudience { - return &s -} - -func (a *NoticeAudience) Matches(sysAdmin bool, teamAdmin bool) bool { - switch *a { - case NoticeAudience_All: - return true - case NoticeAudience_Member: - return !sysAdmin && !teamAdmin - case NoticeAudience_Sysadmin: - return sysAdmin - case NoticeAudience_TeamAdmin: - return teamAdmin - } - return false -} - -const ( - NoticeAudience_All NoticeAudience = "all" - NoticeAudience_Member NoticeAudience = "member" - NoticeAudience_Sysadmin NoticeAudience = "sysadmin" - NoticeAudience_TeamAdmin NoticeAudience = "teamadmin" -) - -// Only show the notice on specific clients. Defaults to 'all' -// -// Client type. Defaults to "all" -type NoticeClientType string - -func NewNoticeClientType(s NoticeClientType) *NoticeClientType { return &s } - -func (c *NoticeClientType) Matches(other NoticeClientType) bool { - switch *c { - case NoticeClientType_All: - return true - case NoticeClientType_Mobile: - return other == NoticeClientType_MobileIos || other == NoticeClientType_MobileAndroid - default: - return *c == other - } -} - -const ( - NoticeClientType_All NoticeClientType = "all" - NoticeClientType_Desktop NoticeClientType = "desktop" - NoticeClientType_Mobile NoticeClientType = "mobile" - NoticeClientType_MobileAndroid NoticeClientType = "mobile-android" - NoticeClientType_MobileIos NoticeClientType = "mobile-ios" - NoticeClientType_Web NoticeClientType = "web" -) - -func NoticeClientTypeFromString(s string) (NoticeClientType, error) { - switch s { - case "web": - return NoticeClientType_Web, nil - case "mobile-ios": - return NoticeClientType_MobileIos, nil - case "mobile-android": - return NoticeClientType_MobileAndroid, nil - case "desktop": - return NoticeClientType_Desktop, nil - } - return NoticeClientType_All, errors.New("Invalid client type supplied") -} - -// Instance type. Defaults to "both" -type NoticeInstanceType string - -func NewNoticeInstanceType(n NoticeInstanceType) *NoticeInstanceType { return &n } -func (t *NoticeInstanceType) Matches(isCloud bool) bool { - if *t == NoticeInstanceType_Both { - return true - } - if *t == NoticeInstanceType_Cloud && !isCloud { - return false - } - if *t == NoticeInstanceType_OnPrem && isCloud { - return false - } - return true -} - -const ( - NoticeInstanceType_Both NoticeInstanceType = "both" - NoticeInstanceType_Cloud NoticeInstanceType = "cloud" - NoticeInstanceType_OnPrem NoticeInstanceType = "onprem" -) - -// SKU. Defaults to "all" -type NoticeSKU string - -func NewNoticeSKU(s NoticeSKU) *NoticeSKU { return &s } -func (c *NoticeSKU) Matches(s string) bool { - switch *c { - case NoticeSKU_All: - return true - case NoticeSKU_E0, NoticeSKU_Team: - return s == "" - default: - return s == string(*c) - } -} - -const ( - NoticeSKU_E0 NoticeSKU = "e0" - NoticeSKU_E10 NoticeSKU = "e10" - NoticeSKU_E20 NoticeSKU = "e20" - NoticeSKU_All NoticeSKU = "all" - NoticeSKU_Team NoticeSKU = "team" -) - -// Optional action to perform on action button click. (defaults to closing the notice) -// -// Possible actions to execute on button press -type NoticeAction string - -const ( - URL NoticeAction = "url" -) - -// Definition of the table keeping the 'viewed' state of each in-product notice per user -type ProductNoticeViewState struct { - UserId string - NoticeId string - Viewed int32 - Timestamp int64 -} - -type ExternalDependency struct { - Name string `json:"name"` - MinimumVersion string `json:"minimum_version"` -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/push_notification.go b/vendor/github.com/mattermost/mattermost-server/v5/model/push_notification.go deleted file mode 100644 index 2a0dc658..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/push_notification.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "errors" - "io" - "strings" -) - -const ( - 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" - PUSH_TYPE_UPDATE_BADGE = "update_badge" - PUSH_TYPE_SESSION = "session" - PUSH_MESSAGE_V2 = "v2" - - PUSH_SOUND_NONE = "none" - - // The category is set to handle a set of interactive Actions - // with the push notifications - CATEGORY_CAN_REPLY = "CAN_REPLY" - - MHPNS = "https://push.mattermost.com" - - PUSH_SEND_PREPARE = "Prepared to send" - PUSH_SEND_SUCCESS = "Successful" - PUSH_NOT_SENT = "Not Sent due to preferences" - PUSH_RECEIVED = "Received by device" -) - -type PushNotificationAck struct { - Id string `json:"id"` - ClientReceivedAt int64 `json:"received_at"` - ClientPlatform string `json:"platform"` - NotificationType string `json:"type"` - PostId string `json:"post_id,omitempty"` - IsIdLoaded bool `json:"is_id_loaded"` -} - -type PushNotification struct { - AckId string `json:"ack_id"` - Platform string `json:"platform"` - ServerId string `json:"server_id"` - DeviceId string `json:"device_id"` - PostId string `json:"post_id"` - Category string `json:"category,omitempty"` - Sound string `json:"sound,omitempty"` - Message string `json:"message,omitempty"` - Badge int `json:"badge,omitempty"` - ContentAvailable int `json:"cont_ava,omitempty"` - TeamId string `json:"team_id,omitempty"` - ChannelId string `json:"channel_id,omitempty"` - RootId string `json:"root_id,omitempty"` - ChannelName string `json:"channel_name,omitempty"` - Type string `json:"type,omitempty"` - SenderId string `json:"sender_id,omitempty"` - SenderName string `json:"sender_name,omitempty"` - OverrideUsername string `json:"override_username,omitempty"` - OverrideIconUrl string `json:"override_icon_url,omitempty"` - FromWebhook string `json:"from_webhook,omitempty"` - Version string `json:"version,omitempty"` - IsIdLoaded bool `json:"is_id_loaded"` -} - -func (pn *PushNotification) ToJson() string { - b, _ := json.Marshal(pn) - return string(b) -} - -func (pn *PushNotification) DeepCopy() *PushNotification { - copy := *pn - return © -} - -func (pn *PushNotification) SetDeviceIdAndPlatform(deviceId string) { - - index := strings.Index(deviceId, ":") - - if index > -1 { - pn.Platform = deviceId[:index] - pn.DeviceId = deviceId[index+1:] - } -} - -func PushNotificationFromJson(data io.Reader) (*PushNotification, error) { - if data == nil { - return nil, errors.New("push notification data can't be nil") - } - var pn *PushNotification - if err := json.NewDecoder(data).Decode(&pn); err != nil { - return nil, err - } - return pn, nil -} - -func PushNotificationAckFromJson(data io.Reader) (*PushNotificationAck, error) { - if data == nil { - return nil, errors.New("push notification data can't be nil") - } - var ack *PushNotificationAck - if err := json.NewDecoder(data).Decode(&ack); err != nil { - return nil, err - } - return ack, nil -} - -func (ack *PushNotificationAck) ToJson() string { - b, _ := json.Marshal(ack) - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/push_response.go b/vendor/github.com/mattermost/mattermost-server/v5/model/push_response.go deleted file mode 100644 index 227a089b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/push_response.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2015-present 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 (pr *PushResponse) ToJson() string { - b, _ := json.Marshal(pr) - 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) - } - return objmap -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/reaction.go b/vendor/github.com/mattermost/mattermost-server/v5/model/reaction.go deleted file mode 100644 index 6d0ea68d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/reaction.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "regexp" -) - -type Reaction struct { - UserId string `json:"user_id"` - PostId string `json:"post_id"` - EmojiName string `json:"emoji_name"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - RemoteId *string `json:"remote_id"` -} - -func (o *Reaction) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ReactionFromJson(data io.Reader) *Reaction { - var o Reaction - - if err := json.NewDecoder(data).Decode(&o); err != nil { - return nil - } - return &o -} - -func ReactionsToJson(o []*Reaction) string { - b, _ := json.Marshal(o) - return string(b) -} - -func MapPostIdToReactionsToJson(o map[string][]*Reaction) string { - b, _ := json.Marshal(o) - return string(b) -} - -func MapPostIdToReactionsFromJson(data io.Reader) map[string][]*Reaction { - decoder := json.NewDecoder(data) - - var objmap map[string][]*Reaction - if err := decoder.Decode(&objmap); err != nil { - return make(map[string][]*Reaction) - } - return objmap -} - -func ReactionsFromJson(data io.Reader) []*Reaction { - var o []*Reaction - - if err := json.NewDecoder(data).Decode(&o); err != nil { - return nil - } - return o -} - -func (o *Reaction) IsValid() *AppError { - if !IsValidId(o.UserId) { - return NewAppError("Reaction.IsValid", "model.reaction.is_valid.user_id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest) - } - - if !IsValidId(o.PostId) { - return NewAppError("Reaction.IsValid", "model.reaction.is_valid.post_id.app_error", nil, "post_id="+o.PostId, http.StatusBadRequest) - } - - validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`) - - if o.EmojiName == "" || len(o.EmojiName) > EMOJI_NAME_MAX_LENGTH || !validName.MatchString(o.EmojiName) { - return NewAppError("Reaction.IsValid", "model.reaction.is_valid.emoji_name.app_error", nil, "emoji_name="+o.EmojiName, http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("Reaction.IsValid", "model.reaction.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("Reaction.IsValid", "model.reaction.is_valid.update_at.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *Reaction) PreSave() { - if o.CreateAt == 0 { - o.CreateAt = GetMillis() - } - o.UpdateAt = GetMillis() - o.DeleteAt = 0 - - if o.RemoteId == nil { - o.RemoteId = NewString("") - } -} - -func (o *Reaction) PreUpdate() { - o.UpdateAt = GetMillis() - - if o.RemoteId == nil { - o.RemoteId = NewString("") - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/remote_cluster.go b/vendor/github.com/mattermost/mattermost-server/v5/model/remote_cluster.go deleted file mode 100644 index 2ec0cc9b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/remote_cluster.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/json" - "errors" - "io" - "net/http" - "regexp" - "strings" - - "golang.org/x/crypto/scrypt" -) - -const ( - RemoteOfflineAfterMillis = 1000 * 60 * 5 // 5 minutes - RemoteNameMinLength = 1 - RemoteNameMaxLength = 64 -) - -var ( - validRemoteNameChars = regexp.MustCompile(`^[a-zA-Z0-9\.\-\_]+$`) -) - -type RemoteCluster struct { - RemoteId string `json:"remote_id"` - RemoteTeamId string `json:"remote_team_id"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - SiteURL string `json:"site_url"` - CreateAt int64 `json:"create_at"` - LastPingAt int64 `json:"last_ping_at"` - Token string `json:"token"` - RemoteToken string `json:"remote_token"` - Topics string `json:"topics"` - CreatorId string `json:"creator_id"` -} - -func (rc *RemoteCluster) PreSave() { - if rc.RemoteId == "" { - rc.RemoteId = NewId() - } - - if rc.DisplayName == "" { - rc.DisplayName = rc.Name - } - - rc.Name = SanitizeUnicode(rc.Name) - rc.DisplayName = SanitizeUnicode(rc.DisplayName) - rc.Name = NormalizeRemoteName(rc.Name) - - if rc.Token == "" { - rc.Token = NewId() - } - - if rc.CreateAt == 0 { - rc.CreateAt = GetMillis() - } - rc.fixTopics() -} - -func (rc *RemoteCluster) IsValid() *AppError { - if !IsValidId(rc.RemoteId) { - return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.id.app_error", nil, "id="+rc.RemoteId, http.StatusBadRequest) - } - - if !IsValidRemoteName(rc.Name) { - return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.name.app_error", nil, "name="+rc.Name, http.StatusBadRequest) - } - - if rc.CreateAt == 0 { - return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.create_at.app_error", nil, "create_at=0", http.StatusBadRequest) - } - - if !IsValidId(rc.CreatorId) { - return NewAppError("RemoteCluster.IsValid", "model.cluster.is_valid.id.app_error", nil, "creator_id="+rc.CreatorId, http.StatusBadRequest) - } - return nil -} - -func IsValidRemoteName(s string) bool { - if len(s) < RemoteNameMinLength || len(s) > RemoteNameMaxLength { - return false - } - return validRemoteNameChars.MatchString(s) -} - -func (rc *RemoteCluster) PreUpdate() { - if rc.DisplayName == "" { - rc.DisplayName = rc.Name - } - - rc.Name = SanitizeUnicode(rc.Name) - rc.DisplayName = SanitizeUnicode(rc.DisplayName) - rc.Name = NormalizeRemoteName(rc.Name) - rc.fixTopics() -} - -func (rc *RemoteCluster) IsOnline() bool { - return rc.LastPingAt > GetMillis()-RemoteOfflineAfterMillis -} - -// fixTopics ensures all topics are separated by one, and only one, space. -func (rc *RemoteCluster) fixTopics() { - trimmed := strings.TrimSpace(rc.Topics) - if trimmed == "" || trimmed == "*" { - rc.Topics = trimmed - return - } - - var sb strings.Builder - sb.WriteString(" ") - - ss := strings.Split(rc.Topics, " ") - for _, c := range ss { - cc := strings.TrimSpace(c) - if cc != "" { - sb.WriteString(cc) - sb.WriteString(" ") - } - } - rc.Topics = sb.String() -} - -func (rc *RemoteCluster) ToJSON() (string, error) { - b, err := json.Marshal(rc) - if err != nil { - return "", err - } - return string(b), nil -} - -func (rc *RemoteCluster) ToRemoteClusterInfo() RemoteClusterInfo { - return RemoteClusterInfo{ - Name: rc.Name, - DisplayName: rc.DisplayName, - CreateAt: rc.CreateAt, - LastPingAt: rc.LastPingAt, - } -} - -func NormalizeRemoteName(name string) string { - return strings.ToLower(name) -} - -func RemoteClusterFromJSON(data io.Reader) (*RemoteCluster, *AppError) { - var rc RemoteCluster - err := json.NewDecoder(data).Decode(&rc) - if err != nil { - return nil, NewAppError("RemoteClusterFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest) - } - return &rc, nil -} - -// RemoteClusterInfo provides a subset of RemoteCluster fields suitable for sending to clients. -type RemoteClusterInfo struct { - Name string `json:"name"` - DisplayName string `json:"display_name"` - CreateAt int64 `json:"create_at"` - LastPingAt int64 `json:"last_ping_at"` -} - -// RemoteClusterFrame wraps a `RemoteClusterMsg` with credentials specific to a remote cluster. -type RemoteClusterFrame struct { - RemoteId string `json:"remote_id"` - Msg RemoteClusterMsg `json:"msg"` -} - -func (f *RemoteClusterFrame) IsValid() *AppError { - if !IsValidId(f.RemoteId) { - return NewAppError("RemoteClusterFrame.IsValid", "api.remote_cluster.invalid_id.app_error", nil, "RemoteId="+f.RemoteId, http.StatusBadRequest) - } - - if err := f.Msg.IsValid(); err != nil { - return err - } - - return nil -} - -func RemoteClusterFrameFromJSON(data io.Reader) (*RemoteClusterFrame, *AppError) { - var frame RemoteClusterFrame - err := json.NewDecoder(data).Decode(&frame) - if err != nil { - return nil, NewAppError("RemoteClusterFrameFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest) - } - return &frame, nil -} - -// RemoteClusterMsg represents a message that is sent and received between clusters. -// These are processed and routed via the RemoteClusters service. -type RemoteClusterMsg struct { - Id string `json:"id"` - Topic string `json:"topic"` - CreateAt int64 `json:"create_at"` - Payload json.RawMessage `json:"payload"` -} - -func NewRemoteClusterMsg(topic string, payload json.RawMessage) RemoteClusterMsg { - return RemoteClusterMsg{ - Id: NewId(), - Topic: topic, - CreateAt: GetMillis(), - Payload: payload, - } -} - -func (m RemoteClusterMsg) IsValid() *AppError { - if !IsValidId(m.Id) { - return NewAppError("RemoteClusterMsg.IsValid", "api.remote_cluster.invalid_id.app_error", nil, "Id="+m.Id, http.StatusBadRequest) - } - - if m.Topic == "" { - return NewAppError("RemoteClusterMsg.IsValid", "api.remote_cluster.invalid_topic.app_error", nil, "Topic empty", http.StatusBadRequest) - } - - if len(m.Payload) == 0 { - return NewAppError("RemoteClusterMsg.IsValid", "api.context.invalid_body_param.app_error", map[string]interface{}{"Name": "PayLoad"}, "", http.StatusBadRequest) - } - - return nil -} - -func RemoteClusterMsgFromJSON(data io.Reader) (RemoteClusterMsg, *AppError) { - var msg RemoteClusterMsg - err := json.NewDecoder(data).Decode(&msg) - if err != nil { - return RemoteClusterMsg{}, NewAppError("RemoteClusterMsgFromJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest) - } - return msg, nil -} - -// RemoteClusterPing represents a ping that is sent and received between clusters -// to indicate a connection is alive. This is the payload for a `RemoteClusterMsg`. -type RemoteClusterPing struct { - SentAt int64 `json:"sent_at"` - RecvAt int64 `json:"recv_at"` -} - -func RemoteClusterPingFromRawJSON(raw json.RawMessage) (RemoteClusterPing, *AppError) { - var ping RemoteClusterPing - err := json.Unmarshal(raw, &ping) - if err != nil { - return RemoteClusterPing{}, NewAppError("RemoteClusterPingFromRawJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest) - } - return ping, nil -} - -// RemoteClusterInvite represents an invitation to establish a simple trust with a remote cluster. -type RemoteClusterInvite struct { - RemoteId string `json:"remote_id"` - RemoteTeamId string `json:"remote_team_id"` - SiteURL string `json:"site_url"` - Token string `json:"token"` -} - -func RemoteClusterInviteFromRawJSON(raw json.RawMessage) (*RemoteClusterInvite, *AppError) { - var invite RemoteClusterInvite - err := json.Unmarshal(raw, &invite) - if err != nil { - return nil, NewAppError("RemoteClusterInviteFromRawJSON", "model.utils.decode_json.app_error", nil, err.Error(), http.StatusBadRequest) - } - return &invite, nil -} - -func (rci *RemoteClusterInvite) Encrypt(password string) ([]byte, error) { - raw, err := json.Marshal(&rci) - if err != nil { - return nil, err - } - - // create random salt to be prepended to the blob. - salt := make([]byte, 16) - if _, err = io.ReadFull(rand.Reader, salt); err != nil { - return nil, err - } - - key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32) - if err != nil { - return nil, err - } - - block, err := aes.NewCipher(key[:]) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - // create random nonce - nonce := make([]byte, gcm.NonceSize()) - if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return nil, err - } - - // prefix the nonce to the cyphertext so we don't need to keep track of it. - sealed := gcm.Seal(nonce, nonce, raw, nil) - - return append(salt, sealed...), nil -} - -func (rci *RemoteClusterInvite) Decrypt(encrypted []byte, password string) error { - if len(encrypted) <= 16 { - return errors.New("invalid length") - } - - // first 16 bytes is the salt that was used to derive a key - salt := encrypted[:16] - encrypted = encrypted[16:] - - key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32) - if err != nil { - return err - } - - block, err := aes.NewCipher(key[:]) - if err != nil { - return err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - - // nonce was prefixed to the cyphertext when encrypting so we need to extract it. - nonceSize := gcm.NonceSize() - nonce, cyphertext := encrypted[:nonceSize], encrypted[nonceSize:] - - plain, err := gcm.Open(nil, nonce, cyphertext, nil) - if err != nil { - return err - } - - // try to unmarshall the decrypted JSON to this invite struct. - return json.Unmarshal(plain, &rci) -} - -// RemoteClusterQueryFilter provides filter criteria for RemoteClusterStore.GetAll -type RemoteClusterQueryFilter struct { - ExcludeOffline bool - InChannel string - NotInChannel string - Topic string - CreatorId string - OnlyConfirmed bool -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/role.go b/vendor/github.com/mattermost/mattermost-server/v5/model/role.go deleted file mode 100644 index fc1606ce..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/role.go +++ /dev/null @@ -1,952 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "strings" -) - -// SysconsoleAncillaryPermissions maps the non-sysconsole permissions required by each sysconsole view. -var SysconsoleAncillaryPermissions map[string][]*Permission -var SystemManagerDefaultPermissions []string -var SystemUserManagerDefaultPermissions []string -var SystemReadOnlyAdminDefaultPermissions []string - -var BuiltInSchemeManagedRoleIDs []string - -var NewSystemRoleIDs []string - -func init() { - NewSystemRoleIDs = []string{ - SYSTEM_USER_MANAGER_ROLE_ID, - SYSTEM_READ_ONLY_ADMIN_ROLE_ID, - SYSTEM_MANAGER_ROLE_ID, - } - - BuiltInSchemeManagedRoleIDs = append([]string{ - SYSTEM_GUEST_ROLE_ID, - SYSTEM_USER_ROLE_ID, - SYSTEM_ADMIN_ROLE_ID, - SYSTEM_POST_ALL_ROLE_ID, - SYSTEM_POST_ALL_PUBLIC_ROLE_ID, - SYSTEM_USER_ACCESS_TOKEN_ROLE_ID, - - TEAM_GUEST_ROLE_ID, - TEAM_USER_ROLE_ID, - TEAM_ADMIN_ROLE_ID, - TEAM_POST_ALL_ROLE_ID, - TEAM_POST_ALL_PUBLIC_ROLE_ID, - - CHANNEL_GUEST_ROLE_ID, - CHANNEL_USER_ROLE_ID, - CHANNEL_ADMIN_ROLE_ID, - }, NewSystemRoleIDs...) - - // When updating the values here, the values in mattermost-redux must also be updated. - SysconsoleAncillaryPermissions = map[string][]*Permission{ - PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id: { - PERMISSION_READ_LICENSE_INFORMATION, - }, - PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE.Id: { - PERMISSION_MANAGE_LICENSE_INFORMATION, - }, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id: { - PERMISSION_READ_PUBLIC_CHANNEL, - PERMISSION_READ_CHANNEL, - PERMISSION_READ_PUBLIC_CHANNEL_GROUPS, - PERMISSION_READ_PRIVATE_CHANNEL_GROUPS, - }, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id: { - PERMISSION_READ_OTHER_USERS_TEAMS, - PERMISSION_GET_ANALYTICS, - }, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id: { - PERMISSION_LIST_PRIVATE_TEAMS, - PERMISSION_LIST_PUBLIC_TEAMS, - PERMISSION_VIEW_TEAM, - }, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id: { - PERMISSION_READ_ELASTICSEARCH_POST_INDEXING_JOB, - PERMISSION_READ_ELASTICSEARCH_POST_AGGREGATION_JOB, - }, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER.Id: { - PERMISSION_TEST_SITE_URL, - PERMISSION_RELOAD_CONFIG, - PERMISSION_INVALIDATE_CACHES, - }, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE.Id: { - PERMISSION_RECYCLE_DATABASE_CONNECTIONS, - }, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH.Id: { - PERMISSION_TEST_ELASTICSEARCH, - PERMISSION_CREATE_ELASTICSEARCH_POST_INDEXING_JOB, - PERMISSION_CREATE_ELASTICSEARCH_POST_AGGREGATION_JOB, - PERMISSION_PURGE_ELASTICSEARCH_INDEXES, - }, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE.Id: { - PERMISSION_TEST_S3, - }, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP.Id: { - PERMISSION_TEST_EMAIL, - }, - PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id: { - PERMISSION_GET_LOGS, - }, - PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id: { - PERMISSION_GET_ANALYTICS, - }, - PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id: { - PERMISSION_VIEW_TEAM, - }, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS.Id: { - PERMISSION_EDIT_OTHER_USERS, - PERMISSION_DEMOTE_TO_GUEST, - PERMISSION_PROMOTE_GUEST, - }, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id: { - PERMISSION_MANAGE_TEAM, - PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES, - PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES, - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS, - PERMISSION_DELETE_PRIVATE_CHANNEL, - PERMISSION_DELETE_PUBLIC_CHANNEL, - PERMISSION_MANAGE_CHANNEL_ROLES, - PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, - PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC, - }, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id: { - PERMISSION_MANAGE_TEAM, - PERMISSION_MANAGE_TEAM_ROLES, - PERMISSION_REMOVE_USER_FROM_TEAM, - PERMISSION_JOIN_PRIVATE_TEAMS, - PERMISSION_JOIN_PUBLIC_TEAMS, - PERMISSION_ADD_USER_TO_TEAM, - }, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id: { - PERMISSION_MANAGE_TEAM, - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS, - PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, - PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC, - }, - PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION.Id: { - PERMISSION_EDIT_BRAND, - }, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_DATA_RETENTION_POLICY.Id: { - PERMISSION_CREATE_DATA_RETENTION_JOB, - }, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY.Id: { - PERMISSION_READ_DATA_RETENTION_JOB, - }, - PERMISSION_SYSCONSOLE_WRITE_COMPLIANCE_COMPLIANCE_EXPORT.Id: { - PERMISSION_CREATE_COMPLIANCE_EXPORT_JOB, - PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT, - }, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT.Id: { - PERMISSION_READ_COMPLIANCE_EXPORT_JOB, - PERMISSION_DOWNLOAD_COMPLIANCE_EXPORT_RESULT, - }, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE.Id: { - PERMISSION_READ_AUDITS, - }, - PERMISSION_SYSCONSOLE_WRITE_EXPERIMENTAL_BLEVE.Id: { - PERMISSION_CREATE_POST_BLEVE_INDEXES_JOB, - PERMISSION_PURGE_BLEVE_INDEXES, - }, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_LDAP.Id: { - PERMISSION_CREATE_LDAP_SYNC_JOB, - PERMISSION_ADD_LDAP_PUBLIC_CERT, - PERMISSION_REMOVE_LDAP_PUBLIC_CERT, - PERMISSION_ADD_LDAP_PRIVATE_CERT, - PERMISSION_REMOVE_LDAP_PRIVATE_CERT, - }, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id: { - PERMISSION_TEST_LDAP, - PERMISSION_READ_LDAP_SYNC_JOB, - }, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_EMAIL.Id: { - PERMISSION_INVALIDATE_EMAIL_INVITE, - }, - PERMISSION_SYSCONSOLE_WRITE_AUTHENTICATION_SAML.Id: { - PERMISSION_GET_SAML_METADATA_FROM_IDP, - PERMISSION_ADD_SAML_PUBLIC_CERT, - PERMISSION_ADD_SAML_PRIVATE_CERT, - PERMISSION_ADD_SAML_IDP_CERT, - PERMISSION_REMOVE_SAML_PUBLIC_CERT, - PERMISSION_REMOVE_SAML_PRIVATE_CERT, - PERMISSION_REMOVE_SAML_IDP_CERT, - PERMISSION_GET_SAML_CERT_STATUS, - }, - } - - SystemUserManagerDefaultPermissions = []string{ - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id, - } - - SystemReadOnlyAdminDefaultPermissions = []string{ - PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER.Id, - PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION.Id, - PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION.Id, - PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER.Id, - PERMISSION_SYSCONSOLE_READ_SITE_EMOJI.Id, - PERMISSION_SYSCONSOLE_READ_SITE_POSTS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_NOTICES.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id, - PERMISSION_SYSCONSOLE_READ_PLUGINS.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_DATA_RETENTION_POLICY.Id, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_EXPORT.Id, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_COMPLIANCE_MONITORING.Id, - PERMISSION_SYSCONSOLE_READ_COMPLIANCE_CUSTOM_TERMS_OF_SERVICE.Id, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURES.Id, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_FEATURE_FLAGS.Id, - PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL_BLEVE.Id, - } - - SystemManagerDefaultPermissions = []string{ - PERMISSION_SYSCONSOLE_READ_ABOUT_EDITION_AND_LICENSE.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_SITE_STATISTICS.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_TEAM_STATISTICS.Id, - PERMISSION_SYSCONSOLE_READ_REPORTING_SERVER_LOGS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id, - PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id, - PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_WEB_SERVER.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DATABASE.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_ELASTICSEARCH.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_FILE_STORAGE.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_IMAGE_PROXY.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SMTP.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_HIGH_AVAILABILITY.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_LOGGING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_SESSION_LENGTHS.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_PERFORMANCE_MONITORING.Id, - PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_DEVELOPER.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_WEB_SERVER.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DATABASE.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_ELASTICSEARCH.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_FILE_STORAGE.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_IMAGE_PROXY.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SMTP.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PUSH_NOTIFICATION_SERVER.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_HIGH_AVAILABILITY.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_RATE_LIMITING.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_LOGGING.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_SESSION_LENGTHS.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_PERFORMANCE_MONITORING.Id, - PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT_DEVELOPER.Id, - PERMISSION_SYSCONSOLE_READ_SITE_CUSTOMIZATION.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_CUSTOMIZATION.Id, - PERMISSION_SYSCONSOLE_READ_SITE_LOCALIZATION.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_LOCALIZATION.Id, - PERMISSION_SYSCONSOLE_READ_SITE_USERS_AND_TEAMS.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_USERS_AND_TEAMS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_NOTIFICATIONS.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTIFICATIONS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_ANNOUNCEMENT_BANNER.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_ANNOUNCEMENT_BANNER.Id, - PERMISSION_SYSCONSOLE_READ_SITE_EMOJI.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_EMOJI.Id, - PERMISSION_SYSCONSOLE_READ_SITE_POSTS.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_POSTS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_FILE_SHARING_AND_DOWNLOADS.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_FILE_SHARING_AND_DOWNLOADS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_PUBLIC_LINKS.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_PUBLIC_LINKS.Id, - PERMISSION_SYSCONSOLE_READ_SITE_NOTICES.Id, - PERMISSION_SYSCONSOLE_WRITE_SITE_NOTICES.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SIGNUP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_EMAIL.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_PASSWORD.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_MFA.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_LDAP.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_SAML.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_OPENID.Id, - PERMISSION_SYSCONSOLE_READ_AUTHENTICATION_GUEST_ACCESS.Id, - PERMISSION_SYSCONSOLE_READ_PLUGINS.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_BOT_ACCOUNTS.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_GIF.Id, - PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_BOT_ACCOUNTS.Id, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_GIF.Id, - PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS.Id, - } - - // Add the ancillary permissions to each system role - SystemUserManagerDefaultPermissions = AddAncillaryPermissions(SystemUserManagerDefaultPermissions) - SystemReadOnlyAdminDefaultPermissions = AddAncillaryPermissions(SystemReadOnlyAdminDefaultPermissions) - SystemManagerDefaultPermissions = AddAncillaryPermissions(SystemManagerDefaultPermissions) -} - -type RoleType string -type RoleScope string - -const ( - SYSTEM_GUEST_ROLE_ID = "system_guest" - SYSTEM_USER_ROLE_ID = "system_user" - SYSTEM_ADMIN_ROLE_ID = "system_admin" - SYSTEM_POST_ALL_ROLE_ID = "system_post_all" - SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public" - SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token" - SYSTEM_USER_MANAGER_ROLE_ID = "system_user_manager" - SYSTEM_READ_ONLY_ADMIN_ROLE_ID = "system_read_only_admin" - SYSTEM_MANAGER_ROLE_ID = "system_manager" - - TEAM_GUEST_ROLE_ID = "team_guest" - TEAM_USER_ROLE_ID = "team_user" - TEAM_ADMIN_ROLE_ID = "team_admin" - TEAM_POST_ALL_ROLE_ID = "team_post_all" - TEAM_POST_ALL_PUBLIC_ROLE_ID = "team_post_all_public" - - CHANNEL_GUEST_ROLE_ID = "channel_guest" - CHANNEL_USER_ROLE_ID = "channel_user" - CHANNEL_ADMIN_ROLE_ID = "channel_admin" - - ROLE_NAME_MAX_LENGTH = 64 - ROLE_DISPLAY_NAME_MAX_LENGTH = 128 - ROLE_DESCRIPTION_MAX_LENGTH = 1024 - - RoleScopeSystem RoleScope = "System" - RoleScopeTeam RoleScope = "Team" - RoleScopeChannel RoleScope = "Channel" - - RoleTypeGuest RoleType = "Guest" - RoleTypeUser RoleType = "User" - RoleTypeAdmin RoleType = "Admin" -) - -type Role struct { - Id string `json:"id"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - Permissions []string `json:"permissions"` - SchemeManaged bool `json:"scheme_managed"` - BuiltIn bool `json:"built_in"` -} - -type RolePatch struct { - Permissions *[]string `json:"permissions"` -} - -type RolePermissions struct { - RoleID string - Permissions []string -} - -func (r *Role) ToJson() string { - b, _ := json.Marshal(r) - return string(b) -} - -func RoleFromJson(data io.Reader) *Role { - var r *Role - json.NewDecoder(data).Decode(&r) - return r -} - -func RoleListToJson(r []*Role) string { - b, _ := json.Marshal(r) - return string(b) -} - -func RoleListFromJson(data io.Reader) []*Role { - var roles []*Role - json.NewDecoder(data).Decode(&roles) - return roles -} - -func (r *RolePatch) ToJson() string { - b, _ := json.Marshal(r) - return string(b) -} - -func RolePatchFromJson(data io.Reader) *RolePatch { - var rolePatch *RolePatch - json.NewDecoder(data).Decode(&rolePatch) - return rolePatch -} - -func (r *Role) Patch(patch *RolePatch) { - if patch.Permissions != nil { - r.Permissions = *patch.Permissions - } -} - -// MergeChannelHigherScopedPermissions is meant to be invoked on a channel scheme's role and merges the higher-scoped -// channel role's permissions. -func (r *Role) MergeChannelHigherScopedPermissions(higherScopedPermissions *RolePermissions) { - mergedPermissions := []string{} - - higherScopedPermissionsMap := AsStringBoolMap(higherScopedPermissions.Permissions) - rolePermissionsMap := AsStringBoolMap(r.Permissions) - - for _, cp := range AllPermissions { - if cp.Scope != PermissionScopeChannel { - continue - } - - _, presentOnHigherScope := higherScopedPermissionsMap[cp.Id] - - // For the channel admin role always look to the higher scope to determine if the role has their permission. - // The channel admin is a special case because they're not part of the UI to be "channel moderated", only - // channel members and channel guests are. - if higherScopedPermissions.RoleID == CHANNEL_ADMIN_ROLE_ID && presentOnHigherScope { - mergedPermissions = append(mergedPermissions, cp.Id) - continue - } - - _, permissionIsModerated := ChannelModeratedPermissionsMap[cp.Id] - if permissionIsModerated { - _, presentOnRole := rolePermissionsMap[cp.Id] - if presentOnRole && presentOnHigherScope { - mergedPermissions = append(mergedPermissions, cp.Id) - } - } else { - if presentOnHigherScope { - mergedPermissions = append(mergedPermissions, cp.Id) - } - } - } - - r.Permissions = mergedPermissions -} - -// Returns an array of permissions that are in either role.Permissions -// or patch.Permissions, but not both. -func PermissionsChangedByPatch(role *Role, patch *RolePatch) []string { - var result []string - - if patch.Permissions == nil { - return result - } - - roleMap := make(map[string]bool) - patchMap := make(map[string]bool) - - for _, permission := range role.Permissions { - roleMap[permission] = true - } - - for _, permission := range *patch.Permissions { - patchMap[permission] = true - } - - for _, permission := range role.Permissions { - if !patchMap[permission] { - result = append(result, permission) - } - } - - for _, permission := range *patch.Permissions { - if !roleMap[permission] { - result = append(result, permission) - } - } - - return result -} - -func ChannelModeratedPermissionsChangedByPatch(role *Role, patch *RolePatch) []string { - var result []string - - if role == nil { - return result - } - - if patch.Permissions == nil { - return result - } - - roleMap := make(map[string]bool) - patchMap := make(map[string]bool) - - for _, permission := range role.Permissions { - if channelModeratedPermissionName, found := ChannelModeratedPermissionsMap[permission]; found { - roleMap[channelModeratedPermissionName] = true - } - } - - for _, permission := range *patch.Permissions { - if channelModeratedPermissionName, found := ChannelModeratedPermissionsMap[permission]; found { - patchMap[channelModeratedPermissionName] = true - } - } - - for permissionKey := range roleMap { - if !patchMap[permissionKey] { - result = append(result, permissionKey) - } - } - - for permissionKey := range patchMap { - if !roleMap[permissionKey] { - result = append(result, permissionKey) - } - } - - return result -} - -// GetChannelModeratedPermissions returns a map of channel moderated permissions that the role has access to -func (r *Role) GetChannelModeratedPermissions(channelType string) map[string]bool { - moderatedPermissions := make(map[string]bool) - for _, permission := range r.Permissions { - if _, found := ChannelModeratedPermissionsMap[permission]; !found { - continue - } - - for moderated, moderatedPermissionValue := range ChannelModeratedPermissionsMap { - // the moderated permission has already been found to be true so skip this iteration - if moderatedPermissions[moderatedPermissionValue] { - continue - } - - if moderated == permission { - // Special case where the channel moderated permission for `manage_members` is different depending on whether the channel is private or public - if moderated == PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id || moderated == PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id { - canManagePublic := channelType == CHANNEL_OPEN && moderated == PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id - canManagePrivate := channelType == CHANNEL_PRIVATE && moderated == PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id - moderatedPermissions[moderatedPermissionValue] = canManagePublic || canManagePrivate - } else { - moderatedPermissions[moderatedPermissionValue] = true - } - } - } - } - - return moderatedPermissions -} - -// RolePatchFromChannelModerationsPatch Creates and returns a RolePatch based on a slice of ChannelModerationPatchs, roleName is expected to be either "members" or "guests". -func (r *Role) RolePatchFromChannelModerationsPatch(channelModerationsPatch []*ChannelModerationPatch, roleName string) *RolePatch { - permissionsToAddToPatch := make(map[string]bool) - - // Iterate through the list of existing permissions on the role and append permissions that we want to keep. - for _, permission := range r.Permissions { - // Permission is not moderated so dont add it to the patch and skip the channelModerationsPatch - if _, isModerated := ChannelModeratedPermissionsMap[permission]; !isModerated { - continue - } - - permissionEnabled := true - // Check if permission has a matching moderated permission name inside the channel moderation patch - for _, channelModerationPatch := range channelModerationsPatch { - if *channelModerationPatch.Name == ChannelModeratedPermissionsMap[permission] { - // Permission key exists in patch with a value of false so skip over it - if roleName == "members" { - if channelModerationPatch.Roles.Members != nil && !*channelModerationPatch.Roles.Members { - permissionEnabled = false - } - } else if roleName == "guests" { - if channelModerationPatch.Roles.Guests != nil && !*channelModerationPatch.Roles.Guests { - permissionEnabled = false - } - } - } - } - - if permissionEnabled { - permissionsToAddToPatch[permission] = true - } - } - - // Iterate through the patch and add any permissions that dont already exist on the role - for _, channelModerationPatch := range channelModerationsPatch { - for permission, moderatedPermissionName := range ChannelModeratedPermissionsMap { - if roleName == "members" && channelModerationPatch.Roles.Members != nil && *channelModerationPatch.Roles.Members && *channelModerationPatch.Name == moderatedPermissionName { - permissionsToAddToPatch[permission] = true - } - - if roleName == "guests" && channelModerationPatch.Roles.Guests != nil && *channelModerationPatch.Roles.Guests && *channelModerationPatch.Name == moderatedPermissionName { - permissionsToAddToPatch[permission] = true - } - } - } - - patchPermissions := make([]string, 0, len(permissionsToAddToPatch)) - for permission := range permissionsToAddToPatch { - patchPermissions = append(patchPermissions, permission) - } - - return &RolePatch{Permissions: &patchPermissions} -} - -func (r *Role) IsValid() bool { - if !IsValidId(r.Id) { - return false - } - - return r.IsValidWithoutId() -} - -func (r *Role) IsValidWithoutId() bool { - if !IsValidRoleName(r.Name) { - return false - } - - if r.DisplayName == "" || len(r.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH { - return false - } - - if len(r.Description) > ROLE_DESCRIPTION_MAX_LENGTH { - return false - } - - check := func(perms []*Permission, permission string) bool { - for _, p := range perms { - if permission == p.Id { - return true - } - } - return false - } - for _, permission := range r.Permissions { - permissionValidated := check(AllPermissions, permission) || check(DeprecatedPermissions, permission) - if !permissionValidated { - return false - } - } - - return true -} - -func CleanRoleNames(roleNames []string) ([]string, bool) { - var cleanedRoleNames []string - for _, roleName := range roleNames { - if strings.TrimSpace(roleName) == "" { - continue - } - - if !IsValidRoleName(roleName) { - return roleNames, false - } - - cleanedRoleNames = append(cleanedRoleNames, roleName) - } - - return cleanedRoleNames, true -} - -func IsValidRoleName(roleName string) bool { - if roleName == "" || len(roleName) > ROLE_NAME_MAX_LENGTH { - return false - } - - if strings.TrimLeft(roleName, "abcdefghijklmnopqrstuvwxyz0123456789_") != "" { - return false - } - - return true -} - -func MakeDefaultRoles() map[string]*Role { - roles := make(map[string]*Role) - - roles[CHANNEL_GUEST_ROLE_ID] = &Role{ - Name: "channel_guest", - DisplayName: "authentication.roles.channel_guest.name", - Description: "authentication.roles.channel_guest.description", - Permissions: []string{ - PERMISSION_READ_CHANNEL.Id, - PERMISSION_ADD_REACTION.Id, - PERMISSION_REMOVE_REACTION.Id, - PERMISSION_UPLOAD_FILE.Id, - PERMISSION_EDIT_POST.Id, - PERMISSION_CREATE_POST.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - PERMISSION_USE_SLASH_COMMANDS.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[CHANNEL_USER_ROLE_ID] = &Role{ - Name: "channel_user", - DisplayName: "authentication.roles.channel_user.name", - Description: "authentication.roles.channel_user.description", - Permissions: []string{ - PERMISSION_READ_CHANNEL.Id, - PERMISSION_ADD_REACTION.Id, - PERMISSION_REMOVE_REACTION.Id, - PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, - PERMISSION_UPLOAD_FILE.Id, - PERMISSION_GET_PUBLIC_LINK.Id, - PERMISSION_CREATE_POST.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - PERMISSION_USE_SLASH_COMMANDS.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[CHANNEL_ADMIN_ROLE_ID] = &Role{ - Name: "channel_admin", - DisplayName: "authentication.roles.channel_admin.name", - Description: "authentication.roles.channel_admin.description", - Permissions: []string{ - PERMISSION_MANAGE_CHANNEL_ROLES.Id, - PERMISSION_USE_GROUP_MENTIONS.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[TEAM_GUEST_ROLE_ID] = &Role{ - Name: "team_guest", - DisplayName: "authentication.roles.team_guest.name", - Description: "authentication.roles.team_guest.description", - Permissions: []string{ - PERMISSION_VIEW_TEAM.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[TEAM_USER_ROLE_ID] = &Role{ - Name: "team_user", - DisplayName: "authentication.roles.team_user.name", - Description: "authentication.roles.team_user.description", - Permissions: []string{ - PERMISSION_LIST_TEAM_CHANNELS.Id, - PERMISSION_JOIN_PUBLIC_CHANNELS.Id, - PERMISSION_READ_PUBLIC_CHANNEL.Id, - PERMISSION_VIEW_TEAM.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[TEAM_POST_ALL_ROLE_ID] = &Role{ - Name: "team_post_all", - DisplayName: "authentication.roles.team_post_all.name", - Description: "authentication.roles.team_post_all.description", - Permissions: []string{ - PERMISSION_CREATE_POST.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - }, - SchemeManaged: false, - BuiltIn: true, - } - - roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ - Name: "team_post_all_public", - DisplayName: "authentication.roles.team_post_all_public.name", - Description: "authentication.roles.team_post_all_public.description", - Permissions: []string{ - PERMISSION_CREATE_POST_PUBLIC.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - }, - SchemeManaged: false, - BuiltIn: true, - } - - roles[TEAM_ADMIN_ROLE_ID] = &Role{ - Name: "team_admin", - DisplayName: "authentication.roles.team_admin.name", - Description: "authentication.roles.team_admin.description", - Permissions: []string{ - PERMISSION_REMOVE_USER_FROM_TEAM.Id, - PERMISSION_MANAGE_TEAM.Id, - PERMISSION_IMPORT_TEAM.Id, - PERMISSION_MANAGE_TEAM_ROLES.Id, - PERMISSION_MANAGE_CHANNEL_ROLES.Id, - PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS.Id, - PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS.Id, - PERMISSION_MANAGE_SLASH_COMMANDS.Id, - PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, - PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id, - PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, - PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE.Id, - PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[SYSTEM_GUEST_ROLE_ID] = &Role{ - Name: "system_guest", - DisplayName: "authentication.roles.global_guest.name", - Description: "authentication.roles.global_guest.description", - Permissions: []string{ - PERMISSION_CREATE_DIRECT_CHANNEL.Id, - PERMISSION_CREATE_GROUP_CHANNEL.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[SYSTEM_USER_ROLE_ID] = &Role{ - Name: "system_user", - DisplayName: "authentication.roles.global_user.name", - Description: "authentication.roles.global_user.description", - Permissions: []string{ - PERMISSION_LIST_PUBLIC_TEAMS.Id, - PERMISSION_JOIN_PUBLIC_TEAMS.Id, - PERMISSION_CREATE_DIRECT_CHANNEL.Id, - PERMISSION_CREATE_GROUP_CHANNEL.Id, - PERMISSION_VIEW_MEMBERS.Id, - }, - SchemeManaged: true, - BuiltIn: true, - } - - roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{ - Name: "system_post_all", - DisplayName: "authentication.roles.system_post_all.name", - Description: "authentication.roles.system_post_all.description", - Permissions: []string{ - PERMISSION_CREATE_POST.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - }, - SchemeManaged: false, - BuiltIn: true, - } - - roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ - Name: "system_post_all_public", - DisplayName: "authentication.roles.system_post_all_public.name", - Description: "authentication.roles.system_post_all_public.description", - Permissions: []string{ - PERMISSION_CREATE_POST_PUBLIC.Id, - PERMISSION_USE_CHANNEL_MENTIONS.Id, - }, - SchemeManaged: false, - BuiltIn: true, - } - - roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{ - Name: "system_user_access_token", - DisplayName: "authentication.roles.system_user_access_token.name", - Description: "authentication.roles.system_user_access_token.description", - Permissions: []string{ - PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, - PERMISSION_READ_USER_ACCESS_TOKEN.Id, - PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, - }, - SchemeManaged: false, - BuiltIn: true, - } - - roles[SYSTEM_USER_MANAGER_ROLE_ID] = &Role{ - Name: "system_user_manager", - DisplayName: "authentication.roles.system_user_manager.name", - Description: "authentication.roles.system_user_manager.description", - Permissions: SystemUserManagerDefaultPermissions, - SchemeManaged: false, - BuiltIn: true, - } - - roles[SYSTEM_READ_ONLY_ADMIN_ROLE_ID] = &Role{ - Name: "system_read_only_admin", - DisplayName: "authentication.roles.system_read_only_admin.name", - Description: "authentication.roles.system_read_only_admin.description", - Permissions: SystemReadOnlyAdminDefaultPermissions, - SchemeManaged: false, - BuiltIn: true, - } - - roles[SYSTEM_MANAGER_ROLE_ID] = &Role{ - Name: "system_manager", - DisplayName: "authentication.roles.system_manager.name", - Description: "authentication.roles.system_manager.description", - Permissions: SystemManagerDefaultPermissions, - SchemeManaged: false, - BuiltIn: true, - } - - allPermissionIDs := []string{} - for _, permission := range AllPermissions { - allPermissionIDs = append(allPermissionIDs, permission.Id) - } - - roles[SYSTEM_ADMIN_ROLE_ID] = &Role{ - Name: "system_admin", - DisplayName: "authentication.roles.global_admin.name", - Description: "authentication.roles.global_admin.description", - // System admins can do anything channel and team admins can do - // plus everything members of teams and channels can do to all teams - // and channels on the system - Permissions: allPermissionIDs, - SchemeManaged: true, - BuiltIn: true, - } - - return roles -} - -func AddAncillaryPermissions(permissions []string) []string { - for _, permission := range permissions { - if ancillaryPermissions, ok := SysconsoleAncillaryPermissions[permission]; ok { - for _, ancillaryPermission := range ancillaryPermissions { - permissions = append(permissions, ancillaryPermission.Id) - } - } - } - return permissions -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/saml.go b/vendor/github.com/mattermost/mattermost-server/v5/model/saml.go deleted file mode 100644 index feaf325a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/saml.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "encoding/xml" - "io" - "time" -) - -const ( - USER_AUTH_SERVICE_SAML = "saml" - USER_AUTH_SERVICE_SAML_TEXT = "SAML" - USER_AUTH_SERVICE_IS_SAML = "isSaml" - USER_AUTH_SERVICE_IS_MOBILE = "isMobile" - USER_AUTH_SERVICE_IS_OAUTH = "isOAuthUser" -) - -type SamlAuthRequest struct { - Base64AuthRequest string - URL string - RelayState string -} - -type SamlCertificateStatus struct { - IdpCertificateFile bool `json:"idp_certificate_file"` - PrivateKeyFile bool `json:"private_key_file"` - PublicCertificateFile bool `json:"public_certificate_file"` -} - -type SamlMetadataResponse struct { - IdpDescriptorUrl string `json:"idp_descriptor_url"` - IdpUrl string `json:"idp_url"` - IdpPublicCertificate string `json:"idp_public_certificate"` -} - -type NameIDFormat struct { - XMLName xml.Name - Format string `xml:",attr,omitempty"` - Value string `xml:",innerxml"` -} - -type NameID struct { - NameQualifier string `xml:",attr"` - SPNameQualifier string `xml:",attr"` - Format string `xml:",attr,omitempty"` - SPProvidedID string `xml:",attr"` - Value string `xml:",chardata"` -} - -type AttributeValue struct { - Type string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"` - Value string `xml:",chardata"` - NameID *NameID -} - -type Attribute struct { - XMLName xml.Name - FriendlyName string `xml:",attr"` - Name string `xml:",attr"` - NameFormat string `xml:",attr"` - Values []AttributeValue `xml:"AttributeValue"` -} - -type Endpoint struct { - XMLName xml.Name - Binding string `xml:"Binding,attr"` - Location string `xml:"Location,attr"` - ResponseLocation string `xml:"ResponseLocation,attr,omitempty"` -} - -type IndexedEndpoint struct { - XMLName xml.Name - Binding string `xml:"Binding,attr"` - Location string `xml:"Location,attr"` - ResponseLocation *string `xml:"ResponseLocation,attr,omitempty"` - Index int `xml:"index,attr"` - IsDefault *bool `xml:"isDefault,attr"` -} - -type IDPSSODescriptor struct { - XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"` - SSODescriptor - WantAuthnRequestsSigned *bool `xml:",attr"` - - SingleSignOnServices []Endpoint `xml:"SingleSignOnService"` - NameIDMappingServices []Endpoint `xml:"NameIDMappingService"` - AssertionIDRequestServices []Endpoint `xml:"AssertionIDRequestService"` - AttributeProfiles []string `xml:"AttributeProfile"` - Attributes []Attribute `xml:"Attribute"` -} - -type SSODescriptor struct { - XMLName xml.Name - RoleDescriptor - ArtifactResolutionServices []IndexedEndpoint `xml:"ArtifactResolutionService"` - SingleLogoutServices []Endpoint `xml:"SingleLogoutService"` - ManageNameIDServices []Endpoint `xml:"ManageNameIDService"` - NameIDFormats []NameIDFormat `xml:"NameIDFormat"` -} - -type X509Certificate struct { - XMLName xml.Name - Cert string `xml:",innerxml"` -} - -type X509Data struct { - XMLName xml.Name - X509Certificate X509Certificate `xml:"X509Certificate"` -} - -type KeyInfo struct { - XMLName xml.Name - DS string `xml:"xmlns:ds,attr"` - X509Data X509Data `xml:"X509Data"` -} -type EncryptionMethod struct { - Algorithm string `xml:"Algorithm,attr"` -} - -type KeyDescriptor struct { - XMLName xml.Name - Use string `xml:"use,attr,omitempty"` - KeyInfo KeyInfo `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo,omitempty"` -} - -type RoleDescriptor struct { - XMLName xml.Name - ID string `xml:",attr,omitempty"` - ValidUntil time.Time `xml:"validUntil,attr,omitempty"` - CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` - ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"` - ErrorURL string `xml:"errorURL,attr,omitempty"` - KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor,omitempty"` - Organization *Organization `xml:"Organization,omitempty"` - ContactPersons []ContactPerson `xml:"ContactPerson,omitempty"` -} - -type ContactPerson struct { - XMLName xml.Name - ContactType string `xml:"contactType,attr"` - Company string - GivenName string - SurName string - EmailAddresses []string `xml:"EmailAddress"` - TelephoneNumbers []string `xml:"TelephoneNumber"` -} - -type LocalizedName struct { - Lang string `xml:"xml lang,attr"` - Value string `xml:",chardata"` -} - -type LocalizedURI struct { - Lang string `xml:"xml lang,attr"` - Value string `xml:",chardata"` -} - -type Organization struct { - XMLName xml.Name - OrganizationNames []LocalizedName `xml:"OrganizationName"` - OrganizationDisplayNames []LocalizedName `xml:"OrganizationDisplayName"` - OrganizationURLs []LocalizedURI `xml:"OrganizationURL"` -} - -type EntityDescriptor struct { - XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"` - EntityID string `xml:"entityID,attr"` - ID string `xml:",attr,omitempty"` - ValidUntil time.Time `xml:"validUntil,attr,omitempty"` - CacheDuration time.Duration `xml:"cacheDuration,attr,omitempty"` - RoleDescriptors []RoleDescriptor `xml:"RoleDescriptor"` - IDPSSODescriptors []IDPSSODescriptor `xml:"IDPSSODescriptor"` - Organization Organization `xml:"Organization"` - ContactPerson ContactPerson `xml:"ContactPerson"` -} - -func (s *SamlCertificateStatus) ToJson() string { - b, _ := json.Marshal(s) - return string(b) -} - -func SamlCertificateStatusFromJson(data io.Reader) *SamlCertificateStatus { - var status *SamlCertificateStatus - json.NewDecoder(data).Decode(&status) - return status -} - -func (s *SamlMetadataResponse) ToJson() string { - b, _ := json.Marshal(s) - return string(b) -} - -func SamlMetadataResponseFromJson(data io.Reader) *SamlMetadataResponse { - var status *SamlMetadataResponse - json.NewDecoder(data).Decode(&status) - return status -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/scheduled_task.go b/vendor/github.com/mattermost/mattermost-server/v5/model/scheduled_task.go deleted file mode 100644 index cf20db63..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/scheduled_task.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "fmt" - "time" -) - -type TaskFunc func() - -type ScheduledTask struct { - Name string `json:"name"` - Interval time.Duration `json:"interval"` - Recurring bool `json:"recurring"` - function func() - cancel chan struct{} - cancelled chan struct{} - fromNextIntervalTime bool -} - -func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask { - return createTask(name, function, timeToExecution, false, false) -} - -func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask { - return createTask(name, function, interval, true, false) -} - -func CreateRecurringTaskFromNextIntervalTime(name string, function TaskFunc, interval time.Duration) *ScheduledTask { - return createTask(name, function, interval, true, true) -} - -func createTask(name string, function TaskFunc, interval time.Duration, recurring bool, fromNextIntervalTime bool) *ScheduledTask { - task := &ScheduledTask{ - Name: name, - Interval: interval, - Recurring: recurring, - function: function, - cancel: make(chan struct{}), - cancelled: make(chan struct{}), - fromNextIntervalTime: fromNextIntervalTime, - } - - go func() { - defer close(task.cancelled) - - var firstTick <-chan time.Time - var ticker *time.Ticker - - if task.fromNextIntervalTime { - currTime := time.Now() - first := currTime.Truncate(interval) - if first.Before(currTime) { - first = first.Add(interval) - } - firstTick = time.After(time.Until(first)) - ticker = &time.Ticker{C: nil} - } else { - firstTick = nil - ticker = time.NewTicker(interval) - } - defer func() { - ticker.Stop() - }() - - for { - select { - case <-firstTick: - ticker = time.NewTicker(interval) - function() - case <-ticker.C: - function() - case <-task.cancel: - return - } - - if !task.Recurring { - break - } - } - }() - - return task -} - -func (task *ScheduledTask) Cancel() { - close(task.cancel) - <-task.cancelled -} - -func (task *ScheduledTask) String() string { - return fmt.Sprintf( - "%s\nInterval: %s\nRecurring: %t\n", - task.Name, - task.Interval.String(), - task.Recurring, - ) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/scheme.go b/vendor/github.com/mattermost/mattermost-server/v5/model/scheme.go deleted file mode 100644 index b5bbf34a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/scheme.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "regexp" -) - -const ( - SCHEME_DISPLAY_NAME_MAX_LENGTH = 128 - SCHEME_NAME_MAX_LENGTH = 64 - SCHEME_DESCRIPTION_MAX_LENGTH = 1024 - SCHEME_SCOPE_TEAM = "team" - SCHEME_SCOPE_CHANNEL = "channel" -) - -type Scheme struct { - Id string `json:"id"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - Scope string `json:"scope"` - DefaultTeamAdminRole string `json:"default_team_admin_role"` - DefaultTeamUserRole string `json:"default_team_user_role"` - DefaultChannelAdminRole string `json:"default_channel_admin_role"` - DefaultChannelUserRole string `json:"default_channel_user_role"` - DefaultTeamGuestRole string `json:"default_team_guest_role"` - DefaultChannelGuestRole string `json:"default_channel_guest_role"` -} - -type SchemePatch struct { - Name *string `json:"name"` - DisplayName *string `json:"display_name"` - Description *string `json:"description"` -} - -type SchemeIDPatch struct { - SchemeID *string `json:"scheme_id"` -} - -// SchemeConveyor is used for importing and exporting a Scheme and its associated Roles. -type SchemeConveyor struct { - Name string `json:"name"` - DisplayName string `json:"display_name"` - Description string `json:"description"` - Scope string `json:"scope"` - TeamAdmin string `json:"default_team_admin_role"` - TeamUser string `json:"default_team_user_role"` - TeamGuest string `json:"default_team_guest_role"` - ChannelAdmin string `json:"default_channel_admin_role"` - ChannelUser string `json:"default_channel_user_role"` - ChannelGuest string `json:"default_channel_guest_role"` - Roles []*Role `json:"roles"` -} - -func (sc *SchemeConveyor) Scheme() *Scheme { - return &Scheme{ - DisplayName: sc.DisplayName, - Name: sc.Name, - Description: sc.Description, - Scope: sc.Scope, - DefaultTeamAdminRole: sc.TeamAdmin, - DefaultTeamUserRole: sc.TeamUser, - DefaultTeamGuestRole: sc.TeamGuest, - DefaultChannelAdminRole: sc.ChannelAdmin, - DefaultChannelUserRole: sc.ChannelUser, - DefaultChannelGuestRole: sc.ChannelGuest, - } -} - -type SchemeRoles struct { - SchemeAdmin bool `json:"scheme_admin"` - SchemeUser bool `json:"scheme_user"` - SchemeGuest bool `json:"scheme_guest"` -} - -func (scheme *Scheme) ToJson() string { - b, _ := json.Marshal(scheme) - return string(b) -} - -func SchemeFromJson(data io.Reader) *Scheme { - var scheme *Scheme - json.NewDecoder(data).Decode(&scheme) - return scheme -} - -func SchemesToJson(schemes []*Scheme) string { - b, _ := json.Marshal(schemes) - return string(b) -} - -func SchemesFromJson(data io.Reader) []*Scheme { - var schemes []*Scheme - if err := json.NewDecoder(data).Decode(&schemes); err == nil { - return schemes - } - return nil -} - -func (scheme *Scheme) IsValid() bool { - if !IsValidId(scheme.Id) { - return false - } - - return scheme.IsValidForCreate() -} - -func (scheme *Scheme) IsValidForCreate() bool { - if scheme.DisplayName == "" || len(scheme.DisplayName) > SCHEME_DISPLAY_NAME_MAX_LENGTH { - return false - } - - if !IsValidSchemeName(scheme.Name) { - return false - } - - if len(scheme.Description) > SCHEME_DESCRIPTION_MAX_LENGTH { - return false - } - - switch scheme.Scope { - case SCHEME_SCOPE_TEAM, SCHEME_SCOPE_CHANNEL: - default: - return false - } - - if !IsValidRoleName(scheme.DefaultChannelAdminRole) { - return false - } - - if !IsValidRoleName(scheme.DefaultChannelUserRole) { - return false - } - - if !IsValidRoleName(scheme.DefaultChannelGuestRole) { - return false - } - - if scheme.Scope == SCHEME_SCOPE_TEAM { - if !IsValidRoleName(scheme.DefaultTeamAdminRole) { - return false - } - - if !IsValidRoleName(scheme.DefaultTeamUserRole) { - return false - } - - if !IsValidRoleName(scheme.DefaultTeamGuestRole) { - return false - } - } - - if scheme.Scope == SCHEME_SCOPE_CHANNEL { - if scheme.DefaultTeamAdminRole != "" { - return false - } - - if scheme.DefaultTeamUserRole != "" { - return false - } - - if scheme.DefaultTeamGuestRole != "" { - return false - } - } - - return true -} - -func (scheme *Scheme) Patch(patch *SchemePatch) { - if patch.DisplayName != nil { - scheme.DisplayName = *patch.DisplayName - } - if patch.Name != nil { - scheme.Name = *patch.Name - } - if patch.Description != nil { - scheme.Description = *patch.Description - } -} - -func (patch *SchemePatch) ToJson() string { - b, _ := json.Marshal(patch) - return string(b) -} - -func SchemePatchFromJson(data io.Reader) *SchemePatch { - var patch *SchemePatch - json.NewDecoder(data).Decode(&patch) - return patch -} - -func SchemeIDFromJson(data io.Reader) *string { - var p *SchemeIDPatch - json.NewDecoder(data).Decode(&p) - return p.SchemeID -} - -func (p *SchemeIDPatch) ToJson() string { - b, _ := json.Marshal(p) - return string(b) -} - -func IsValidSchemeName(name string) bool { - re := regexp.MustCompile(fmt.Sprintf("^[a-z0-9_]{2,%d}$", SCHEME_NAME_MAX_LENGTH)) - return re.MatchString(name) -} - -func (schemeRoles *SchemeRoles) ToJson() string { - b, _ := json.Marshal(schemeRoles) - return string(b) -} - -func SchemeRolesFromJson(data io.Reader) *SchemeRoles { - var schemeRoles *SchemeRoles - json.NewDecoder(data).Decode(&schemeRoles) - return schemeRoles -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/search_params.go b/vendor/github.com/mattermost/mattermost-server/v5/model/search_params.go deleted file mode 100644 index 41a2db2a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/search_params.go +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "net/http" - "regexp" - "strings" - "time" -) - -var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`) -var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`) - -type SearchParams struct { - Terms string - ExcludedTerms string - IsHashtag bool - InChannels []string - ExcludedChannels []string - FromUsers []string - ExcludedUsers []string - AfterDate string - ExcludedAfterDate string - BeforeDate string - ExcludedBeforeDate string - Extensions []string - ExcludedExtensions []string - OnDate string - ExcludedDate string - OrTerms bool - IncludeDeletedChannels bool - TimeZoneOffset int - // True if this search doesn't originate from a "current user". - SearchWithoutUserId bool -} - -// Returns the epoch timestamp of the start of the day specified by SearchParams.AfterDate -func (p *SearchParams) GetAfterDateMillis() int64 { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.AfterDate)) - if err != nil { - date = time.Now() - } - - // travel forward 1 day - oneDay := time.Hour * 24 - afterDate := date.Add(oneDay) - return GetStartOfDayMillis(afterDate, p.TimeZoneOffset) -} - -// Returns the epoch timestamp of the start of the day specified by SearchParams.ExcludedAfterDate -func (p *SearchParams) GetExcludedAfterDateMillis() int64 { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedAfterDate)) - if err != nil { - date = time.Now() - } - - // travel forward 1 day - oneDay := time.Hour * 24 - afterDate := date.Add(oneDay) - return GetStartOfDayMillis(afterDate, p.TimeZoneOffset) -} - -// Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate -func (p *SearchParams) GetBeforeDateMillis() int64 { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate)) - if err != nil { - return 0 - } - - // travel back 1 day - oneDay := time.Hour * -24 - beforeDate := date.Add(oneDay) - return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset) -} - -// Returns the epoch timestamp of the end of the day specified by SearchParams.ExcludedBeforeDate -func (p *SearchParams) GetExcludedBeforeDateMillis() int64 { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedBeforeDate)) - if err != nil { - return 0 - } - - // travel back 1 day - oneDay := time.Hour * -24 - beforeDate := date.Add(oneDay) - return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset) -} - -// Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate -func (p *SearchParams) GetOnDateMillis() (int64, int64) { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate)) - if err != nil { - return 0, 0 - } - - return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset) -} - -// Returns the epoch timestamps of the start and end of the day specified by SearchParams.ExcludedDate -func (p *SearchParams) GetExcludedDateMillis() (int64, int64) { - date, err := time.Parse("2006-01-02", PadDateStringZeros(p.ExcludedDate)) - if err != nil { - return 0, 0 - } - - return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset) -} - -var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on", "ext"} - -type flag struct { - name string - value string - exclude bool -} - -type searchWord struct { - value string - exclude bool -} - -func splitWords(text string) []string { - words := []string{} - - foundQuote := false - location := 0 - for i, char := range text { - if char == '"' { - if foundQuote { - // Grab the quoted section - word := text[location : i+1] - words = append(words, word) - foundQuote = false - location = i + 1 - } else { - nextStart := i - if i > 0 && text[i-1] == '-' { - nextStart = i - 1 - } - words = append(words, strings.Fields(text[location:nextStart])...) - foundQuote = true - location = nextStart - } - } - } - - words = append(words, strings.Fields(text[location:])...) - - return words -} - -func parseSearchFlags(input []string) ([]searchWord, []flag) { - words := []searchWord{} - flags := []flag{} - - skipNextWord := false - for i, word := range input { - if skipNextWord { - skipNextWord = false - continue - } - - isFlag := false - - if colon := strings.Index(word, ":"); colon != -1 { - var flagName string - var exclude bool - if strings.HasPrefix(word, "-") { - flagName = word[1:colon] - exclude = true - } else { - flagName = word[:colon] - exclude = false - } - - value := word[colon+1:] - - for _, searchFlag := range searchFlags { - // check for case insensitive equality - if strings.EqualFold(flagName, searchFlag) { - if value != "" { - flags = append(flags, flag{ - searchFlag, - value, - exclude, - }) - isFlag = true - } else if i < len(input)-1 { - flags = append(flags, flag{ - searchFlag, - input[i+1], - exclude, - }) - skipNextWord = true - isFlag = true - } - - if isFlag { - break - } - } - } - } - - if !isFlag { - exclude := false - if strings.HasPrefix(word, "-") { - exclude = true - } - // trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards) - word = searchTermPuncStart.ReplaceAllString(word, "") - word = searchTermPuncEnd.ReplaceAllString(word, "") - - // and remove extra pound #s - word = hashtagStart.ReplaceAllString(word, "#") - - if word != "" { - words = append(words, searchWord{ - word, - exclude, - }) - } - } - } - - return words, flags -} - -func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams { - words, flags := parseSearchFlags(splitWords(text)) - - hashtagTermList := []string{} - excludedHashtagTermList := []string{} - plainTermList := []string{} - excludedPlainTermList := []string{} - - for _, word := range words { - if validHashtag.MatchString(word.value) { - if word.exclude { - excludedHashtagTermList = append(excludedHashtagTermList, word.value) - } else { - hashtagTermList = append(hashtagTermList, word.value) - } - } else { - if word.exclude { - excludedPlainTermList = append(excludedPlainTermList, word.value) - } else { - plainTermList = append(plainTermList, word.value) - } - } - } - - hashtagTerms := strings.Join(hashtagTermList, " ") - excludedHashtagTerms := strings.Join(excludedHashtagTermList, " ") - plainTerms := strings.Join(plainTermList, " ") - excludedPlainTerms := strings.Join(excludedPlainTermList, " ") - - inChannels := []string{} - excludedChannels := []string{} - fromUsers := []string{} - excludedUsers := []string{} - afterDate := "" - excludedAfterDate := "" - beforeDate := "" - excludedBeforeDate := "" - onDate := "" - excludedDate := "" - excludedExtensions := []string{} - extensions := []string{} - - for _, flag := range flags { - if flag.name == "in" || flag.name == "channel" { - if flag.exclude { - excludedChannels = append(excludedChannels, flag.value) - } else { - inChannels = append(inChannels, flag.value) - } - } else if flag.name == "from" { - if flag.exclude { - excludedUsers = append(excludedUsers, flag.value) - } else { - fromUsers = append(fromUsers, flag.value) - } - } else if flag.name == "after" { - if flag.exclude { - excludedAfterDate = flag.value - } else { - afterDate = flag.value - } - } else if flag.name == "before" { - if flag.exclude { - excludedBeforeDate = flag.value - } else { - beforeDate = flag.value - } - } else if flag.name == "on" { - if flag.exclude { - excludedDate = flag.value - } else { - onDate = flag.value - } - } else if flag.name == "ext" { - if flag.exclude { - excludedExtensions = append(excludedExtensions, flag.value) - } else { - extensions = append(extensions, flag.value) - } - } - } - - paramsList := []*SearchParams{} - - if plainTerms != "" || excludedPlainTerms != "" { - paramsList = append(paramsList, &SearchParams{ - Terms: plainTerms, - ExcludedTerms: excludedPlainTerms, - IsHashtag: false, - InChannels: inChannels, - ExcludedChannels: excludedChannels, - FromUsers: fromUsers, - ExcludedUsers: excludedUsers, - AfterDate: afterDate, - ExcludedAfterDate: excludedAfterDate, - BeforeDate: beforeDate, - ExcludedBeforeDate: excludedBeforeDate, - Extensions: extensions, - ExcludedExtensions: excludedExtensions, - OnDate: onDate, - ExcludedDate: excludedDate, - TimeZoneOffset: timeZoneOffset, - }) - } - - if hashtagTerms != "" || excludedHashtagTerms != "" { - paramsList = append(paramsList, &SearchParams{ - Terms: hashtagTerms, - ExcludedTerms: excludedHashtagTerms, - IsHashtag: true, - InChannels: inChannels, - ExcludedChannels: excludedChannels, - FromUsers: fromUsers, - ExcludedUsers: excludedUsers, - AfterDate: afterDate, - ExcludedAfterDate: excludedAfterDate, - BeforeDate: beforeDate, - ExcludedBeforeDate: excludedBeforeDate, - Extensions: extensions, - ExcludedExtensions: excludedExtensions, - OnDate: onDate, - ExcludedDate: excludedDate, - TimeZoneOffset: timeZoneOffset, - }) - } - - // special case for when no terms are specified but we still have a filter - if plainTerms == "" && hashtagTerms == "" && - excludedPlainTerms == "" && excludedHashtagTerms == "" && - (len(inChannels) != 0 || len(fromUsers) != 0 || - len(excludedChannels) != 0 || len(excludedUsers) != 0 || - len(extensions) != 0 || len(excludedExtensions) != 0 || - afterDate != "" || excludedAfterDate != "" || - beforeDate != "" || excludedBeforeDate != "" || - onDate != "" || excludedDate != "") { - paramsList = append(paramsList, &SearchParams{ - Terms: "", - ExcludedTerms: "", - IsHashtag: false, - InChannels: inChannels, - ExcludedChannels: excludedChannels, - FromUsers: fromUsers, - ExcludedUsers: excludedUsers, - AfterDate: afterDate, - ExcludedAfterDate: excludedAfterDate, - BeforeDate: beforeDate, - ExcludedBeforeDate: excludedBeforeDate, - Extensions: extensions, - ExcludedExtensions: excludedExtensions, - OnDate: onDate, - ExcludedDate: excludedDate, - TimeZoneOffset: timeZoneOffset, - }) - } - - return paramsList -} - -func IsSearchParamsListValid(paramsList []*SearchParams) *AppError { - // All SearchParams should have same IncludeDeletedChannels value. - for _, params := range paramsList { - if params.IncludeDeletedChannels != paramsList[0].IncludeDeletedChannels { - return NewAppError("IsSearchParamsListValid", "model.search_params_list.is_valid.include_deleted_channels.app_error", nil, "", http.StatusInternalServerError) - } - } - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/security_bulletin.go b/vendor/github.com/mattermost/mattermost-server/v5/model/security_bulletin.go deleted file mode 100644 index 66a65812..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/security_bulletin.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type SecurityBulletin struct { - Id string `json:"id"` - AppliesToVersion string `json:"applies_to_version"` -} - -type SecurityBulletins []SecurityBulletin - -func (sb *SecurityBulletin) ToJson() string { - b, _ := json.Marshal(sb) - return string(b) -} - -func SecurityBulletinFromJson(data io.Reader) *SecurityBulletin { - var o *SecurityBulletin - json.NewDecoder(data).Decode(&o) - return o -} - -func (sb SecurityBulletins) ToJson() string { - b, err := json.Marshal(sb) - if err != nil { - return "[]" - } - return string(b) -} - -func SecurityBulletinsFromJson(data io.Reader) SecurityBulletins { - var o SecurityBulletins - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/session.go b/vendor/github.com/mattermost/mattermost-server/v5/model/session.go deleted file mode 100644 index 334c7175..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/session.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "strconv" - "strings" - - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const ( - SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" - SESSION_COOKIE_USER = "MMUSERID" - SESSION_COOKIE_CSRF = "MMCSRF" - SESSION_CACHE_SIZE = 35000 - SESSION_PROP_PLATFORM = "platform" - SESSION_PROP_OS = "os" - SESSION_PROP_BROWSER = "browser" - SESSION_PROP_TYPE = "type" - SESSION_PROP_USER_ACCESS_TOKEN_ID = "user_access_token_id" - SESSION_PROP_IS_BOT = "is_bot" - SESSION_PROP_IS_BOT_VALUE = "true" - SESSION_TYPE_USER_ACCESS_TOKEN = "UserAccessToken" - SESSION_TYPE_CLOUD_KEY = "CloudKey" - SESSION_TYPE_REMOTECLUSTER_TOKEN = "RemoteClusterToken" - SESSION_PROP_IS_GUEST = "is_guest" - SESSION_ACTIVITY_TIMEOUT = 1000 * 60 * 5 // 5 minutes - SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years -) - -//msgp StringMap -type StringMap map[string]string - -//msgp:tuple Session - -// Session contains the user session details. -// This struct's serializer methods are auto-generated. If a new field is added/removed, -// please run make gen-serialized. -type Session struct { - Id string `json:"id"` - Token string `json:"token"` - CreateAt int64 `json:"create_at"` - ExpiresAt int64 `json:"expires_at"` - LastActivityAt int64 `json:"last_activity_at"` - UserId string `json:"user_id"` - DeviceId string `json:"device_id"` - Roles string `json:"roles"` - IsOAuth bool `json:"is_oauth"` - ExpiredNotify bool `json:"expired_notify"` - Props StringMap `json:"props"` - TeamMembers []*TeamMember `json:"team_members" db:"-"` - Local bool `json:"local" db:"-"` -} - -// Returns true if the session is unrestricted, which should grant it -// with all permissions. This is used for local mode sessions -func (s *Session) IsUnrestricted() bool { - return s.Local -} - -func (s *Session) DeepCopy() *Session { - copySession := *s - - if s.Props != nil { - copySession.Props = CopyStringMap(s.Props) - } - - if s.TeamMembers != nil { - copySession.TeamMembers = make([]*TeamMember, len(s.TeamMembers)) - for index, tm := range s.TeamMembers { - copySession.TeamMembers[index] = new(TeamMember) - *copySession.TeamMembers[index] = *tm - } - } - - return ©Session -} - -func (s *Session) ToJson() string { - b, _ := json.Marshal(s) - return string(b) -} - -func SessionFromJson(data io.Reader) *Session { - var s *Session - json.NewDecoder(data).Decode(&s) - return s -} - -func (s *Session) PreSave() { - if s.Id == "" { - s.Id = NewId() - } - - if s.Token == "" { - s.Token = NewId() - } - - s.CreateAt = GetMillis() - s.LastActivityAt = s.CreateAt - - if s.Props == nil { - s.Props = make(map[string]string) - } -} - -func (s *Session) Sanitize() { - s.Token = "" -} - -func (s *Session) IsExpired() bool { - - if s.ExpiresAt <= 0 { - return false - } - - if GetMillis() > s.ExpiresAt { - return true - } - - return false -} - -// Deprecated: SetExpireInDays is deprecated and should not be used. -// Use (*App).SetSessionExpireInDays instead which handles the -// cases where the new ExpiresAt is not relative to CreateAt. -func (s *Session) SetExpireInDays(days int) { - if s.CreateAt == 0 { - s.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days)) - } else { - s.ExpiresAt = s.CreateAt + (1000 * 60 * 60 * 24 * int64(days)) - } -} - -func (s *Session) AddProp(key string, value string) { - - if s.Props == nil { - s.Props = make(map[string]string) - } - - s.Props[key] = value -} - -func (s *Session) GetTeamByTeamId(teamId string) *TeamMember { - for _, team := range s.TeamMembers { - if team.TeamId == teamId { - return team - } - } - - return nil -} - -func (s *Session) IsMobileApp() bool { - return s.DeviceId != "" || s.IsMobile() -} - -func (s *Session) IsMobile() bool { - val, ok := s.Props[USER_AUTH_SERVICE_IS_MOBILE] - if !ok { - return false - } - isMobile, err := strconv.ParseBool(val) - if err != nil { - mlog.Debug("Error parsing boolean property from Session", mlog.Err(err)) - return false - } - return isMobile -} - -func (s *Session) IsSaml() bool { - val, ok := s.Props[USER_AUTH_SERVICE_IS_SAML] - if !ok { - return false - } - isSaml, err := strconv.ParseBool(val) - if err != nil { - mlog.Debug("Error parsing boolean property from Session", mlog.Err(err)) - return false - } - return isSaml -} - -func (s *Session) IsOAuthUser() bool { - val, ok := s.Props[USER_AUTH_SERVICE_IS_OAUTH] - if !ok { - return false - } - isOAuthUser, err := strconv.ParseBool(val) - if err != nil { - mlog.Debug("Error parsing boolean property from Session", mlog.Err(err)) - return false - } - return isOAuthUser -} - -func (s *Session) IsSSOLogin() bool { - return s.IsOAuthUser() || s.IsSaml() -} - -func (s *Session) GetUserRoles() []string { - return strings.Fields(s.Roles) -} - -func (s *Session) GenerateCSRF() string { - token := NewId() - s.AddProp("csrf", token) - return token -} - -func (s *Session) GetCSRF() string { - if s.Props == nil { - return "" - } - - return s.Props["csrf"] -} - -func SessionsToJson(o []*Session) string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func SessionsFromJson(data io.Reader) []*Session { - var o []*Session - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/session_serial_gen.go b/vendor/github.com/mattermost/mattermost-server/v5/model/session_serial_gen.go deleted file mode 100644 index 612bbb89..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/session_serial_gen.go +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "github.com/tinylib/msgp/msgp" -) - -// DecodeMsg implements msgp.Decodable -func (z *Session) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0001 uint32 - zb0001, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 13 { - err = msgp.ArrayError{Wanted: 13, Got: zb0001} - return - } - z.Id, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - z.Token, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Token") - return - } - z.CreateAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - z.ExpiresAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "ExpiresAt") - return - } - z.LastActivityAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - z.UserId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - z.DeviceId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "DeviceId") - return - } - z.Roles, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.IsOAuth, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "IsOAuth") - return - } - z.ExpiredNotify, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "ExpiredNotify") - return - } - var zb0002 uint32 - zb0002, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - if z.Props == nil { - z.Props = make(StringMap, zb0002) - } else if len(z.Props) > 0 { - for key := range z.Props { - delete(z.Props, key) - } - } - for zb0002 > 0 { - zb0002-- - var za0001 string - var za0002 string - za0001, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - za0002, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Props", za0001) - return - } - z.Props[za0001] = za0002 - } - var zb0003 uint32 - zb0003, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err, "TeamMembers") - return - } - if cap(z.TeamMembers) >= int(zb0003) { - z.TeamMembers = (z.TeamMembers)[:zb0003] - } else { - z.TeamMembers = make([]*TeamMember, zb0003) - } - for za0003 := range z.TeamMembers { - if dc.IsNil() { - err = dc.ReadNil() - if err != nil { - err = msgp.WrapError(err, "TeamMembers", za0003) - return - } - z.TeamMembers[za0003] = nil - } else { - if z.TeamMembers[za0003] == nil { - z.TeamMembers[za0003] = new(TeamMember) - } - err = z.TeamMembers[za0003].DecodeMsg(dc) - if err != nil { - err = msgp.WrapError(err, "TeamMembers", za0003) - return - } - } - } - z.Local, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "Local") - return - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *Session) EncodeMsg(en *msgp.Writer) (err error) { - // array header, size 13 - err = en.Append(0x9d) - if err != nil { - return - } - err = en.WriteString(z.Id) - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - err = en.WriteString(z.Token) - if err != nil { - err = msgp.WrapError(err, "Token") - return - } - err = en.WriteInt64(z.CreateAt) - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - err = en.WriteInt64(z.ExpiresAt) - if err != nil { - err = msgp.WrapError(err, "ExpiresAt") - return - } - err = en.WriteInt64(z.LastActivityAt) - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - err = en.WriteString(z.UserId) - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - err = en.WriteString(z.DeviceId) - if err != nil { - err = msgp.WrapError(err, "DeviceId") - return - } - err = en.WriteString(z.Roles) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - err = en.WriteBool(z.IsOAuth) - if err != nil { - err = msgp.WrapError(err, "IsOAuth") - return - } - err = en.WriteBool(z.ExpiredNotify) - if err != nil { - err = msgp.WrapError(err, "ExpiredNotify") - return - } - err = en.WriteMapHeader(uint32(len(z.Props))) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - for za0001, za0002 := range z.Props { - err = en.WriteString(za0001) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - err = en.WriteString(za0002) - if err != nil { - err = msgp.WrapError(err, "Props", za0001) - return - } - } - err = en.WriteArrayHeader(uint32(len(z.TeamMembers))) - if err != nil { - err = msgp.WrapError(err, "TeamMembers") - return - } - for za0003 := range z.TeamMembers { - if z.TeamMembers[za0003] == nil { - err = en.WriteNil() - if err != nil { - return - } - } else { - err = z.TeamMembers[za0003].EncodeMsg(en) - if err != nil { - err = msgp.WrapError(err, "TeamMembers", za0003) - return - } - } - } - err = en.WriteBool(z.Local) - if err != nil { - err = msgp.WrapError(err, "Local") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *Session) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // array header, size 13 - o = append(o, 0x9d) - o = msgp.AppendString(o, z.Id) - o = msgp.AppendString(o, z.Token) - o = msgp.AppendInt64(o, z.CreateAt) - o = msgp.AppendInt64(o, z.ExpiresAt) - o = msgp.AppendInt64(o, z.LastActivityAt) - o = msgp.AppendString(o, z.UserId) - o = msgp.AppendString(o, z.DeviceId) - o = msgp.AppendString(o, z.Roles) - o = msgp.AppendBool(o, z.IsOAuth) - o = msgp.AppendBool(o, z.ExpiredNotify) - o = msgp.AppendMapHeader(o, uint32(len(z.Props))) - for za0001, za0002 := range z.Props { - o = msgp.AppendString(o, za0001) - o = msgp.AppendString(o, za0002) - } - o = msgp.AppendArrayHeader(o, uint32(len(z.TeamMembers))) - for za0003 := range z.TeamMembers { - if z.TeamMembers[za0003] == nil { - o = msgp.AppendNil(o) - } else { - o, err = z.TeamMembers[za0003].MarshalMsg(o) - if err != nil { - err = msgp.WrapError(err, "TeamMembers", za0003) - return - } - } - } - o = msgp.AppendBool(o, z.Local) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *Session) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0001 uint32 - zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 13 { - err = msgp.ArrayError{Wanted: 13, Got: zb0001} - return - } - z.Id, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - z.Token, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Token") - return - } - z.CreateAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - z.ExpiresAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "ExpiresAt") - return - } - z.LastActivityAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - z.UserId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - z.DeviceId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "DeviceId") - return - } - z.Roles, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.IsOAuth, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "IsOAuth") - return - } - z.ExpiredNotify, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "ExpiredNotify") - return - } - var zb0002 uint32 - zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - if z.Props == nil { - z.Props = make(StringMap, zb0002) - } else if len(z.Props) > 0 { - for key := range z.Props { - delete(z.Props, key) - } - } - for zb0002 > 0 { - var za0001 string - var za0002 string - zb0002-- - za0001, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - za0002, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Props", za0001) - return - } - z.Props[za0001] = za0002 - } - var zb0003 uint32 - zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "TeamMembers") - return - } - if cap(z.TeamMembers) >= int(zb0003) { - z.TeamMembers = (z.TeamMembers)[:zb0003] - } else { - z.TeamMembers = make([]*TeamMember, zb0003) - } - for za0003 := range z.TeamMembers { - if msgp.IsNil(bts) { - bts, err = msgp.ReadNilBytes(bts) - if err != nil { - return - } - z.TeamMembers[za0003] = nil - } else { - if z.TeamMembers[za0003] == nil { - z.TeamMembers[za0003] = new(TeamMember) - } - bts, err = z.TeamMembers[za0003].UnmarshalMsg(bts) - if err != nil { - err = msgp.WrapError(err, "TeamMembers", za0003) - return - } - } - } - z.Local, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Local") - return - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *Session) Msgsize() (s int) { - s = 1 + msgp.StringPrefixSize + len(z.Id) + msgp.StringPrefixSize + len(z.Token) + msgp.Int64Size + msgp.Int64Size + msgp.Int64Size + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.DeviceId) + msgp.StringPrefixSize + len(z.Roles) + msgp.BoolSize + msgp.BoolSize + msgp.MapHeaderSize - if z.Props != nil { - for za0001, za0002 := range z.Props { - _ = za0002 - s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) - } - } - s += msgp.ArrayHeaderSize - for za0003 := range z.TeamMembers { - if z.TeamMembers[za0003] == nil { - s += msgp.NilSize - } else { - s += z.TeamMembers[za0003].Msgsize() - } - } - s += msgp.BoolSize - return -} - -// DecodeMsg implements msgp.Decodable -func (z *StringMap) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0003 uint32 - zb0003, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(StringMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - zb0003-- - var zb0001 string - var zb0002 string - zb0001, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err) - return - } - zb0002, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - (*z)[zb0001] = zb0002 - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z StringMap) EncodeMsg(en *msgp.Writer) (err error) { - err = en.WriteMapHeader(uint32(len(z))) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0004, zb0005 := range z { - err = en.WriteString(zb0004) - if err != nil { - err = msgp.WrapError(err) - return - } - err = en.WriteString(zb0005) - if err != nil { - err = msgp.WrapError(err, zb0004) - return - } - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z StringMap) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - o = msgp.AppendMapHeader(o, uint32(len(z))) - for zb0004, zb0005 := range z { - o = msgp.AppendString(o, zb0004) - o = msgp.AppendString(o, zb0005) - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *StringMap) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0003 uint32 - zb0003, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(StringMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - var zb0001 string - var zb0002 string - zb0003-- - zb0001, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - zb0002, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - (*z)[zb0001] = zb0002 - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z StringMap) Msgsize() (s int) { - s = msgp.MapHeaderSize - if z != nil { - for zb0004, zb0005 := range z { - _ = zb0005 - s += msgp.StringPrefixSize + len(zb0004) + msgp.StringPrefixSize + len(zb0005) - } - } - return -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/shared_channel.go b/vendor/github.com/mattermost/mattermost-server/v5/model/shared_channel.go deleted file mode 100644 index e3643812..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/shared_channel.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" - "unicode/utf8" -) - -// SharedChannel represents a channel that can be synchronized with a remote cluster. -// If "home" is true, then the shared channel is homed locally and "SharedChannelRemote" -// table contains the remote clusters that have been invited. -// If "home" is false, then the shared channel is homed remotely, and "RemoteId" -// field points to the remote cluster connection in "RemoteClusters" table. -type SharedChannel struct { - ChannelId string `json:"id"` - TeamId string `json:"team_id"` - Home bool `json:"home"` - ReadOnly bool `json:"readonly"` - ShareName string `json:"name"` - ShareDisplayName string `json:"display_name"` - SharePurpose string `json:"purpose"` - ShareHeader string `json:"header"` - CreatorId string `json:"creator_id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - RemoteId string `json:"remote_id,omitempty"` // if not "home" - Type string `db:"-"` -} - -func (sc *SharedChannel) ToJson() string { - b, _ := json.Marshal(sc) - return string(b) -} - -func SharedChannelFromJson(data io.Reader) (*SharedChannel, error) { - var sc *SharedChannel - err := json.NewDecoder(data).Decode(&sc) - return sc, err -} - -func (sc *SharedChannel) IsValid() *AppError { - if !IsValidId(sc.ChannelId) { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+sc.ChannelId, http.StatusBadRequest) - } - - if sc.Type != CHANNEL_DIRECT && !IsValidId(sc.TeamId) { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "TeamId="+sc.TeamId, http.StatusBadRequest) - } - - if sc.CreateAt == 0 { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if sc.UpdateAt == 0 { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if utf8.RuneCountInString(sc.ShareDisplayName) > CHANNEL_DISPLAY_NAME_MAX_RUNES { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if !IsValidChannelIdentifier(sc.ShareName) { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if utf8.RuneCountInString(sc.ShareHeader) > CHANNEL_HEADER_MAX_RUNES { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if utf8.RuneCountInString(sc.SharePurpose) > CHANNEL_PURPOSE_MAX_RUNES { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if !IsValidId(sc.CreatorId) { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "CreatorId="+sc.CreatorId, http.StatusBadRequest) - } - - if !sc.Home { - if !IsValidId(sc.RemoteId) { - return NewAppError("SharedChannel.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+sc.RemoteId, http.StatusBadRequest) - } - } - return nil -} - -func (sc *SharedChannel) PreSave() { - sc.ShareName = SanitizeUnicode(sc.ShareName) - sc.ShareDisplayName = SanitizeUnicode(sc.ShareDisplayName) - - sc.CreateAt = GetMillis() - sc.UpdateAt = sc.CreateAt -} - -func (sc *SharedChannel) PreUpdate() { - sc.UpdateAt = GetMillis() - sc.ShareName = SanitizeUnicode(sc.ShareName) - sc.ShareDisplayName = SanitizeUnicode(sc.ShareDisplayName) -} - -// SharedChannelRemote represents a remote cluster that has been invited -// to a shared channel. -type SharedChannelRemote struct { - Id string `json:"id"` - ChannelId string `json:"channel_id"` - CreatorId string `json:"creator_id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - IsInviteAccepted bool `json:"is_invite_accepted"` - IsInviteConfirmed bool `json:"is_invite_confirmed"` - RemoteId string `json:"remote_id"` - LastPostUpdateAt int64 `json:"last_post_update_at"` - LastPostId string `json:"last_post_id"` -} - -func (sc *SharedChannelRemote) ToJson() string { - b, _ := json.Marshal(sc) - return string(b) -} - -func SharedChannelRemoteFromJson(data io.Reader) (*SharedChannelRemote, error) { - var sc *SharedChannelRemote - err := json.NewDecoder(data).Decode(&sc) - return sc, err -} - -func (sc *SharedChannelRemote) IsValid() *AppError { - if !IsValidId(sc.Id) { - return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+sc.Id, http.StatusBadRequest) - } - - if !IsValidId(sc.ChannelId) { - return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+sc.ChannelId, http.StatusBadRequest) - } - - if sc.CreateAt == 0 { - return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if sc.UpdateAt == 0 { - return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+sc.ChannelId, http.StatusBadRequest) - } - - if !IsValidId(sc.CreatorId) { - return NewAppError("SharedChannelRemote.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "id="+sc.CreatorId, http.StatusBadRequest) - } - return nil -} - -func (sc *SharedChannelRemote) PreSave() { - if sc.Id == "" { - sc.Id = NewId() - } - sc.CreateAt = GetMillis() - sc.UpdateAt = sc.CreateAt -} - -func (sc *SharedChannelRemote) PreUpdate() { - sc.UpdateAt = GetMillis() -} - -type SharedChannelRemoteStatus struct { - ChannelId string `json:"channel_id"` - DisplayName string `json:"display_name"` - SiteURL string `json:"site_url"` - LastPingAt int64 `json:"last_ping_at"` - NextSyncAt int64 `json:"next_sync_at"` - ReadOnly bool `json:"readonly"` - IsInviteAccepted bool `json:"is_invite_accepted"` - Token string `json:"token"` -} - -// SharedChannelUser stores a lastSyncAt timestamp on behalf of a remote cluster for -// each user that has been synchronized. -type SharedChannelUser struct { - Id string `json:"id"` - UserId string `json:"user_id"` - ChannelId string `json:"channel_id"` - RemoteId string `json:"remote_id"` - CreateAt int64 `json:"create_at"` - LastSyncAt int64 `json:"last_sync_at"` -} - -func (scu *SharedChannelUser) PreSave() { - scu.Id = NewId() - scu.CreateAt = GetMillis() -} - -func (scu *SharedChannelUser) IsValid() *AppError { - if !IsValidId(scu.Id) { - return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+scu.Id, http.StatusBadRequest) - } - - if !IsValidId(scu.UserId) { - return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "UserId="+scu.UserId, http.StatusBadRequest) - } - - if !IsValidId(scu.ChannelId) { - return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "ChannelId="+scu.ChannelId, http.StatusBadRequest) - } - - if !IsValidId(scu.RemoteId) { - return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+scu.RemoteId, http.StatusBadRequest) - } - - if scu.CreateAt == 0 { - return NewAppError("SharedChannelUser.IsValid", "model.channel.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - return nil -} - -type GetUsersForSyncFilter struct { - CheckProfileImage bool - ChannelID string - Limit uint64 -} - -// SharedChannelAttachment stores a lastSyncAt timestamp on behalf of a remote cluster for -// each file attachment that has been synchronized. -type SharedChannelAttachment struct { - Id string `json:"id"` - FileId string `json:"file_id"` - RemoteId string `json:"remote_id"` - CreateAt int64 `json:"create_at"` - LastSyncAt int64 `json:"last_sync_at"` -} - -func (scf *SharedChannelAttachment) PreSave() { - if scf.Id == "" { - scf.Id = NewId() - } - if scf.CreateAt == 0 { - scf.CreateAt = GetMillis() - scf.LastSyncAt = scf.CreateAt - } else { - scf.LastSyncAt = GetMillis() - } -} - -func (scf *SharedChannelAttachment) IsValid() *AppError { - if !IsValidId(scf.Id) { - return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "Id="+scf.Id, http.StatusBadRequest) - } - - if !IsValidId(scf.FileId) { - return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "FileId="+scf.FileId, http.StatusBadRequest) - } - - if !IsValidId(scf.RemoteId) { - return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.id.app_error", nil, "RemoteId="+scf.RemoteId, http.StatusBadRequest) - } - - if scf.CreateAt == 0 { - return NewAppError("SharedChannelAttachment.IsValid", "model.channel.is_valid.create_at.app_error", nil, "", http.StatusBadRequest) - } - return nil -} - -type SharedChannelFilterOpts struct { - TeamId string - CreatorId string - ExcludeHome bool - ExcludeRemote bool -} - -type SharedChannelRemoteFilterOpts struct { - ChannelId string - RemoteId string - InclUnconfirmed bool -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/slack_attachment.go b/vendor/github.com/mattermost/mattermost-server/v5/model/slack_attachment.go deleted file mode 100644 index 371baaab..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/slack_attachment.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "fmt" - "regexp" -) - -var linkWithTextRegex = regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`) - -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 - Actions []*PostAction `json:"actions,omitempty"` -} - -func (s *SlackAttachment) Equals(input *SlackAttachment) bool { - // Direct comparison of simple types - - if s.Id != input.Id { - return false - } - - if s.Fallback != input.Fallback { - return false - } - - if s.Color != input.Color { - return false - } - - if s.Pretext != input.Pretext { - return false - } - - if s.AuthorName != input.AuthorName { - return false - } - - if s.AuthorLink != input.AuthorLink { - return false - } - - if s.AuthorIcon != input.AuthorIcon { - return false - } - - if s.Title != input.Title { - return false - } - - if s.TitleLink != input.TitleLink { - return false - } - - if s.Text != input.Text { - return false - } - - if s.ImageURL != input.ImageURL { - return false - } - - if s.ThumbURL != input.ThumbURL { - return false - } - - if s.Footer != input.Footer { - return false - } - - if s.FooterIcon != input.FooterIcon { - return false - } - - // Compare length & slice values of fields - if len(s.Fields) != len(input.Fields) { - return false - } - - for j := range s.Fields { - if !s.Fields[j].Equals(input.Fields[j]) { - return false - } - } - - // Compare length & slice values of actions - if len(s.Actions) != len(input.Actions) { - return false - } - - for j := range s.Actions { - if !s.Actions[j].Equals(input.Actions[j]) { - return false - } - } - - return s.Timestamp == input.Timestamp -} - -type SlackAttachmentField struct { - Title string `json:"title"` - Value interface{} `json:"value"` - Short SlackCompatibleBool `json:"short"` -} - -func (s *SlackAttachmentField) Equals(input *SlackAttachmentField) bool { - if s.Title != input.Title { - return false - } - - if s.Value != input.Value { - return false - } - - if s.Short != input.Short { - return false - } - - return true -} - -func StringifySlackFieldValue(a []*SlackAttachment) []*SlackAttachment { - var nonNilAttachments []*SlackAttachment - for _, attachment := range a { - if attachment == nil { - continue - } - nonNilAttachments = append(nonNilAttachments, attachment) - - var nonNilFields []*SlackAttachmentField - for _, field := range attachment.Fields { - if field == nil { - continue - } - nonNilFields = append(nonNilFields, field) - - if field.Value != nil { - // Ensure the value is set to a string if it is set - field.Value = fmt.Sprintf("%v", field.Value) - } - } - attachment.Fields = nonNilFields - } - return nonNilAttachments -} - -// This method only parses and processes the attachments, -// all else should be set in the post which is passed -func ParseSlackAttachment(post *Post, attachments []*SlackAttachment) { - if post.Type == "" { - post.Type = POST_SLACK_ATTACHMENT - } - - postAttachments := []*SlackAttachment{} - - for _, attachment := range attachments { - if attachment == nil { - continue - } - - attachment.Text = ParseSlackLinksToMarkdown(attachment.Text) - attachment.Pretext = ParseSlackLinksToMarkdown(attachment.Pretext) - - for _, field := range attachment.Fields { - if field == nil { - continue - } - if value, ok := field.Value.(string); ok { - field.Value = ParseSlackLinksToMarkdown(value) - } - } - postAttachments = append(postAttachments, attachment) - } - post.AddProp("attachments", postAttachments) -} - -func ParseSlackLinksToMarkdown(text string) string { - return linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})") -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/slack_compatibility.go b/vendor/github.com/mattermost/mattermost-server/v5/model/slack_compatibility.go deleted file mode 100644 index 2d3e2878..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/slack_compatibility.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "fmt" - "strings" -) - -// SlackCompatibleBool is an alias for bool that implements json.Unmarshaler -type SlackCompatibleBool bool - -// UnmarshalJSON implements json.Unmarshaler -// -// Slack allows bool values to be represented as strings ("true"/"false") or -// literals (true/false). To maintain compatibility, we define an Unmarshaler -// that supports both. -func (b *SlackCompatibleBool) UnmarshalJSON(data []byte) error { - value := strings.ToLower(string(data)) - if value == "true" || value == `"true"` { - *b = true - } else if value == "false" || value == `"false"` { - *b = false - } else { - return fmt.Errorf("unmarshal: unable to convert %s to bool", data) - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/status.go b/vendor/github.com/mattermost/mattermost-server/v5/model/status.go deleted file mode 100644 index f6f3a67a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/status.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const ( - STATUS_OUT_OF_OFFICE = "ooo" - STATUS_OFFLINE = "offline" - STATUS_AWAY = "away" - STATUS_DND = "dnd" - STATUS_ONLINE = "online" - STATUS_CACHE_SIZE = SESSION_CACHE_SIZE - STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds - STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes -) - -type Status struct { - UserId string `json:"user_id"` - Status string `json:"status"` - Manual bool `json:"manual"` - LastActivityAt int64 `json:"last_activity_at"` - ActiveChannel string `json:"active_channel,omitempty" db:"-"` - DNDEndTime int64 `json:"dnd_end_time"` - PrevStatus string `json:"-"` -} - -func (o *Status) ToJson() string { - oCopy := *o - oCopy.ActiveChannel = "" - b, _ := json.Marshal(oCopy) - return string(b) -} - -func (o *Status) ToClusterJson() string { - oCopy := *o - b, _ := json.Marshal(oCopy) - return string(b) -} - -func StatusFromJson(data io.Reader) *Status { - var o *Status - json.NewDecoder(data).Decode(&o) - return o -} - -func StatusListToJson(u []*Status) string { - uCopy := make([]Status, len(u)) - for i, s := range u { - sCopy := *s - sCopy.ActiveChannel = "" - uCopy[i] = sCopy - } - - b, _ := json.Marshal(uCopy) - return string(b) -} - -func StatusListFromJson(data io.Reader) []*Status { - var statuses []*Status - json.NewDecoder(data).Decode(&statuses) - return statuses -} - -func StatusMapToInterfaceMap(statusMap map[string]*Status) map[string]interface{} { - interfaceMap := map[string]interface{}{} - for _, s := range statusMap { - // Omitted statues mean offline - if s.Status != STATUS_OFFLINE { - interfaceMap[s.UserId] = s.Status - } - } - return interfaceMap -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/suggest_command.go b/vendor/github.com/mattermost/mattermost-server/v5/model/suggest_command.go deleted file mode 100644 index 45a7af38..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/suggest_command.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type SuggestCommand struct { - Suggestion string `json:"suggestion"` - Description string `json:"description"` -} - -func (o *SuggestCommand) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func SuggestCommandFromJson(data io.Reader) *SuggestCommand { - var o *SuggestCommand - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/switch_request.go b/vendor/github.com/mattermost/mattermost-server/v5/model/switch_request.go deleted file mode 100644 index bdb90045..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/switch_request.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type SwitchRequest struct { - CurrentService string `json:"current_service"` - NewService string `json:"new_service"` - Email string `json:"email"` - Password string `json:"password"` - NewPassword string `json:"new_password"` - MfaCode string `json:"mfa_code"` - LdapLoginId string `json:"ldap_id"` -} - -func (o *SwitchRequest) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func SwitchRequestFromJson(data io.Reader) *SwitchRequest { - var o *SwitchRequest - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *SwitchRequest) EmailToOAuth() bool { - return o.CurrentService == USER_AUTH_SERVICE_EMAIL && - (o.NewService == USER_AUTH_SERVICE_SAML || - o.NewService == USER_AUTH_SERVICE_GITLAB || - o.NewService == SERVICE_GOOGLE || - o.NewService == SERVICE_OFFICE365 || - o.NewService == SERVICE_OPENID) -} - -func (o *SwitchRequest) OAuthToEmail() bool { - return (o.CurrentService == USER_AUTH_SERVICE_SAML || - o.CurrentService == USER_AUTH_SERVICE_GITLAB || - o.CurrentService == SERVICE_GOOGLE || - o.CurrentService == SERVICE_OFFICE365 || - o.CurrentService == SERVICE_OPENID) && o.NewService == USER_AUTH_SERVICE_EMAIL -} - -func (o *SwitchRequest) EmailToLdap() bool { - return o.CurrentService == USER_AUTH_SERVICE_EMAIL && o.NewService == USER_AUTH_SERVICE_LDAP -} - -func (o *SwitchRequest) LdapToEmail() bool { - return o.CurrentService == USER_AUTH_SERVICE_LDAP && o.NewService == USER_AUTH_SERVICE_EMAIL -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/system.go b/vendor/github.com/mattermost/mattermost-server/v5/model/system.go deleted file mode 100644 index b7cda1ef..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/system.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "math/big" -) - -const ( - SYSTEM_TELEMETRY_ID = "DiagnosticId" - SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" - SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" - SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" - SYSTEM_LICENSE_RENEWAL_TOKEN = "LicenseRenewalToken" - SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" - SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey" - SYSTEM_POST_ACTION_COOKIE_SECRET = "PostActionCookieSecret" - SYSTEM_INSTALLATION_DATE_KEY = "InstallationDate" - SYSTEM_FIRST_SERVER_RUN_TIMESTAMP_KEY = "FirstServerRunTimestamp" - SYSTEM_CLUSTER_ENCRYPTION_KEY = "ClusterEncryptionKey" - SYSTEM_UPGRADED_FROM_TE_ID = "UpgradedFromTE" - SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5 = "warn_metric_number_of_teams_5" - SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50 = "warn_metric_number_of_channels_50" - SYSTEM_WARN_METRIC_MFA = "warn_metric_mfa" - SYSTEM_WARN_METRIC_EMAIL_DOMAIN = "warn_metric_email_domain" - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100 = "warn_metric_number_of_active_users_100" - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200 = "warn_metric_number_of_active_users_200" - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300 = "warn_metric_number_of_active_users_300" - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500 = "warn_metric_number_of_active_users_500" - SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M = "warn_metric_number_of_posts_2M" - SYSTEM_WARN_METRIC_LAST_RUN_TIMESTAMP_KEY = "LastWarnMetricRunTimestamp" - SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED = "warn_metric_support_email_not_configured" - SYSTEM_FIRST_ADMIN_VISIT_MARKETPLACE = "FirstAdminVisitMarketplace" - AWS_METERING_REPORT_INTERVAL = 1 - AWS_METERING_DIMENSION_USAGE_HRS = "UsageHrs" - USER_LIMIT_OVERAGE_CYCLE_END_DATE = "UserLimitOverageCycleEndDate" - OVER_USER_LIMIT_FORGIVEN_COUNT = "OverUserLimitForgivenCount" - OVER_USER_LIMIT_LAST_EMAIL_SENT = "OverUserLimitLastEmailSent" -) - -const ( - WARN_METRIC_STATUS_LIMIT_REACHED = "true" - WARN_METRIC_STATUS_RUNONCE = "runonce" - WARN_METRIC_STATUS_ACK = "ack" - WARN_METRIC_STATUS_STORE_PREFIX = "warn_metric_" - WARN_METRIC_JOB_INTERVAL = 24 * 7 - WARN_METRIC_NUMBER_OF_ACTIVE_USERS_25 = 25 - WARN_METRIC_JOB_WAIT_TIME = 1000 * 3600 * 24 * 7 // 7 days -) - -type System struct { - Name string `json:"name"` - Value string `json:"value"` -} - -func (o *System) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func SystemFromJson(data io.Reader) *System { - var o *System - json.NewDecoder(data).Decode(&o) - return o -} - -type SystemPostActionCookieSecret struct { - Secret []byte `json:"key,omitempty"` -} - -type SystemAsymmetricSigningKey struct { - ECDSAKey *SystemECDSAKey `json:"ecdsa_key,omitempty"` -} - -type SystemECDSAKey struct { - Curve string `json:"curve"` - X *big.Int `json:"x"` - Y *big.Int `json:"y"` - D *big.Int `json:"d,omitempty"` -} - -// ServerBusyState provides serialization for app.Busy. -type ServerBusyState struct { - Busy bool `json:"busy"` - Expires int64 `json:"expires"` - Expires_ts string `json:"expires_ts,omitempty"` -} - -type SupportPacket struct { - ServerOS string `yaml:"server_os"` - ServerArchitecture string `yaml:"server_architecture"` - DatabaseType string `yaml:"database_type"` - DatabaseVersion string `yaml:"database_version"` - LdapVendorName string `yaml:"ldap_vendor_name,omitempty"` - LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"` - ElasticServerVersion string `yaml:"elastic_server_version,omitempty"` - ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"` -} - -type FileData struct { - Filename string - Body []byte -} - -func (sbs *ServerBusyState) ToJson() string { - b, _ := json.Marshal(sbs) - return string(b) -} - -func ServerBusyStateFromJson(r io.Reader) *ServerBusyState { - var sbs *ServerBusyState - json.NewDecoder(r).Decode(&sbs) - return sbs -} - -var WarnMetricsTable = map[string]WarnMetric{ - SYSTEM_WARN_METRIC_MFA: { - Id: SYSTEM_WARN_METRIC_MFA, - Limit: -1, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_EMAIL_DOMAIN: { - Id: SYSTEM_WARN_METRIC_EMAIL_DOMAIN, - Limit: -1, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5, - Limit: 5, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50, - Limit: 50, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100, - Limit: 100, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200, - Limit: 200, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300, - Limit: 300, - IsBotOnly: true, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500, - Limit: 500, - IsBotOnly: false, - IsRunOnce: true, - }, - SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M: { - Id: SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M, - Limit: 2000000, - IsBotOnly: false, - IsRunOnce: true, - }, - SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED: { - Id: SYSTEM_METRIC_SUPPORT_EMAIL_NOT_CONFIGURED, - Limit: -1, - IsBotOnly: true, - IsRunOnce: false, - SkipAction: true, - }, -} - -type WarnMetric struct { - Id string - Limit int64 - IsBotOnly bool - IsRunOnce bool - SkipAction bool -} - -type WarnMetricDisplayTexts struct { - BotTitle string - BotMessageBody string - BotSuccessMessage string - EmailBody string -} -type WarnMetricStatus struct { - Id string `json:"id"` - Limit int64 `json:"limit"` - Acked bool `json:"acked"` - StoreStatus string `json:"store_status,omitempty"` -} - -func (wms *WarnMetricStatus) ToJson() string { - b, _ := json.Marshal(wms) - return string(b) -} - -func WarnMetricStatusFromJson(data io.Reader) *WarnMetricStatus { - var o WarnMetricStatus - if err := json.NewDecoder(data).Decode(&o); err != nil { - return nil - } - return &o -} - -func MapWarnMetricStatusToJson(o map[string]*WarnMetricStatus) string { - b, _ := json.Marshal(o) - return string(b) -} - -type SendWarnMetricAck struct { - ForceAck bool `json:"forceAck"` -} - -func (swma *SendWarnMetricAck) ToJson() string { - b, _ := json.Marshal(swma) - return string(b) -} - -func SendWarnMetricAckFromJson(r io.Reader) *SendWarnMetricAck { - var swma *SendWarnMetricAck - json.NewDecoder(r).Decode(&swma) - return swma -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/team.go b/vendor/github.com/mattermost/mattermost-server/v5/model/team.go deleted file mode 100644 index fc752f30..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/team.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "regexp" - "strings" - "unicode/utf8" -) - -const ( - 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 { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - DeleteAt int64 `json:"delete_at"` - DisplayName string `json:"display_name"` - Name string `json:"name"` - Description string `json:"description"` - Email string `json:"email"` - Type string `json:"type"` - CompanyName string `json:"company_name"` - AllowedDomains string `json:"allowed_domains"` - InviteId string `json:"invite_id"` - AllowOpenInvite bool `json:"allow_open_invite"` - LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"` - SchemeId *string `json:"scheme_id"` - GroupConstrained *bool `json:"group_constrained"` - PolicyID *string `json:"policy_id" db:"-"` -} - -type TeamPatch struct { - DisplayName *string `json:"display_name"` - Description *string `json:"description"` - CompanyName *string `json:"company_name"` - AllowedDomains *string `json:"allowed_domains"` - AllowOpenInvite *bool `json:"allow_open_invite"` - GroupConstrained *bool `json:"group_constrained"` -} - -type TeamForExport struct { - Team - SchemeName *string -} - -type Invites struct { - Invites []map[string]string `json:"invites"` -} - -type TeamsWithCount struct { - Teams []*Team `json:"teams"` - TotalCount int64 `json:"total_count"` -} - -func InvitesFromJson(data io.Reader) *Invites { - var o *Invites - json.NewDecoder(data).Decode(&o) - return o -} - -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, _ := json.Marshal(o) - return string(b) -} - -func (o *Team) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func TeamFromJson(data io.Reader) *Team { - var o *Team - json.NewDecoder(data).Decode(&o) - return o -} - -func TeamMapToJson(u map[string]*Team) string { - b, _ := json.Marshal(u) - return string(b) -} - -func TeamMapFromJson(data io.Reader) map[string]*Team { - var teams map[string]*Team - json.NewDecoder(data).Decode(&teams) - return teams -} - -func TeamListToJson(t []*Team) string { - b, _ := json.Marshal(t) - return string(b) -} - -func TeamsWithCountToJson(tlc *TeamsWithCount) []byte { - b, _ := json.Marshal(tlc) - return b -} - -func TeamsWithCountFromJson(data io.Reader) *TeamsWithCount { - var twc *TeamsWithCount - json.NewDecoder(data).Decode(&twc) - return twc -} - -func TeamListFromJson(data io.Reader) []*Team { - var teams []*Team - json.NewDecoder(data).Decode(&teams) - return teams -} - -func (o *Team) Etag() string { - return Etag(o.Id, o.UpdateAt) -} - -func (o *Team) IsValid() *AppError { - - if !IsValidId(o.Id) { - return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if o.CreateAt == 0 { - return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.UpdateAt == 0 { - return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - 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 o.Email != "" && !IsValidEmail(o.Email) { - 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) > 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) > 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) > TEAM_DESCRIPTION_MAX_LENGTH { - return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if o.InviteId == "" { - return NewAppError("Team.IsValid", "model.team.is_valid.invite_id.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if IsReservedTeamName(o.Name) { - return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - if !IsValidTeamName(o.Name) { - 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 NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - 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) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH { - return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest) - } - - return nil -} - -func (o *Team) PreSave() { - if o.Id == "" { - o.Id = NewId() - } - - o.CreateAt = GetMillis() - o.UpdateAt = o.CreateAt - - o.Name = SanitizeUnicode(o.Name) - o.DisplayName = SanitizeUnicode(o.DisplayName) - o.Description = SanitizeUnicode(o.Description) - o.CompanyName = SanitizeUnicode(o.CompanyName) - - if o.InviteId == "" { - o.InviteId = NewId() - } -} - -func (o *Team) PreUpdate() { - o.UpdateAt = GetMillis() - o.Name = SanitizeUnicode(o.Name) - o.DisplayName = SanitizeUnicode(o.DisplayName) - o.Description = SanitizeUnicode(o.Description) - o.CompanyName = SanitizeUnicode(o.CompanyName) -} - -func IsReservedTeamName(s string) bool { - s = strings.ToLower(s) - - for _, value := range reservedName { - if strings.Index(s, value) == 0 { - return true - } - } - - return false -} - -func IsValidTeamName(s string) bool { - if !IsValidAlphaNum(s) { - return false - } - - if len(s) < TEAM_NAME_MIN_LENGTH { - return false - } - - return true -} - -var validTeamNameCharacter = regexp.MustCompile(`^[a-z0-9-]$`) - -func CleanTeamName(s string) string { - s = strings.ToLower(strings.Replace(s, " ", "-", -1)) - - for _, value := range reservedName { - if strings.Index(s, value) == 0 { - s = strings.Replace(s, value, "", -1) - } - } - - s = strings.TrimSpace(s) - - for _, c := range s { - char := fmt.Sprintf("%c", c) - if !validTeamNameCharacter.MatchString(char) { - s = strings.Replace(s, char, "", -1) - } - } - - s = strings.Trim(s, "-") - - if !IsValidTeamName(s) { - s = NewId() - } - - return s -} - -func (o *Team) Sanitize() { - o.Email = "" - o.InviteId = "" -} - -func (o *Team) Patch(patch *TeamPatch) { - if patch.DisplayName != nil { - o.DisplayName = *patch.DisplayName - } - - if patch.Description != nil { - o.Description = *patch.Description - } - - if patch.CompanyName != nil { - o.CompanyName = *patch.CompanyName - } - - if patch.AllowedDomains != nil { - o.AllowedDomains = *patch.AllowedDomains - } - - if patch.AllowOpenInvite != nil { - o.AllowOpenInvite = *patch.AllowOpenInvite - } - - if patch.GroupConstrained != nil { - o.GroupConstrained = patch.GroupConstrained - } -} - -func (o *Team) IsGroupConstrained() bool { - return o.GroupConstrained != nil && *o.GroupConstrained -} - -func (t *TeamPatch) ToJson() string { - b, err := json.Marshal(t) - if err != nil { - return "" - } - - return string(b) -} - -func TeamPatchFromJson(data io.Reader) *TeamPatch { - decoder := json.NewDecoder(data) - var team TeamPatch - err := decoder.Decode(&team) - if err != nil { - return nil - } - - return &team -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/team_member.go b/vendor/github.com/mattermost/mattermost-server/v5/model/team_member.go deleted file mode 100644 index f5f1cc61..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/team_member.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "strings" -) - -const ( - USERNAME = "Username" -) - -//msgp:tuple TeamMember -// This struct's serializer methods are auto-generated. If a new field is added/removed, -// please run make gen-serialized. -type TeamMember struct { - TeamId string `json:"team_id"` - UserId string `json:"user_id"` - Roles string `json:"roles"` - DeleteAt int64 `json:"delete_at"` - SchemeGuest bool `json:"scheme_guest"` - SchemeUser bool `json:"scheme_user"` - SchemeAdmin bool `json:"scheme_admin"` - ExplicitRoles string `json:"explicit_roles"` -} - -//msgp:ignore TeamUnread -type TeamUnread struct { - TeamId string `json:"team_id"` - MsgCount int64 `json:"msg_count"` - MentionCount int64 `json:"mention_count"` - MentionCountRoot int64 `json:"mention_count_root"` - MsgCountRoot int64 `json:"msg_count_root"` - ThreadCount int64 `json:"thread_count"` - ThreadMentionCount int64 `json:"thread_mention_count"` -} - -//msgp:ignore TeamMemberForExport -type TeamMemberForExport struct { - TeamMember - TeamName string -} - -//msgp:ignore TeamMemberWithError -type TeamMemberWithError struct { - UserId string `json:"user_id"` - Member *TeamMember `json:"member"` - Error *AppError `json:"error"` -} - -//msgp:ignore EmailInviteWithError -type EmailInviteWithError struct { - Email string `json:"email"` - Error *AppError `json:"error"` -} - -//msgp:ignore TeamMembersGetOptions -type TeamMembersGetOptions struct { - // Sort the team members. Accepts "Username", but defaults to "Id". - Sort string - - // If true, exclude team members whose corresponding user is deleted. - ExcludeDeletedUsers bool - - // Restrict to search in a list of teams and channels - ViewRestrictions *ViewUsersRestrictions -} - -func (o *TeamMember) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *TeamUnread) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func TeamMemberFromJson(data io.Reader) *TeamMember { - var o *TeamMember - json.NewDecoder(data).Decode(&o) - return o -} - -func TeamUnreadFromJson(data io.Reader) *TeamUnread { - var o *TeamUnread - json.NewDecoder(data).Decode(&o) - return o -} - -func EmailInviteWithErrorFromJson(data io.Reader) []*EmailInviteWithError { - var o []*EmailInviteWithError - json.NewDecoder(data).Decode(&o) - return o -} - -func EmailInviteWithErrorToEmails(o []*EmailInviteWithError) []string { - var ret []string - for _, o := range o { - if o.Error == nil { - ret = append(ret, o.Email) - } - } - return ret -} - -func EmailInviteWithErrorToJson(o []*EmailInviteWithError) string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func EmailInviteWithErrorToString(o *EmailInviteWithError) string { - return fmt.Sprintf("%s:%s", o.Email, o.Error.Error()) -} - -func TeamMembersWithErrorToTeamMembers(o []*TeamMemberWithError) []*TeamMember { - var ret []*TeamMember - for _, o := range o { - if o.Error == nil { - ret = append(ret, o.Member) - } - } - return ret -} - -func TeamMembersWithErrorToJson(o []*TeamMemberWithError) string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func TeamMemberWithErrorToString(o *TeamMemberWithError) string { - return fmt.Sprintf("%s:%s", o.UserId, o.Error.Error()) -} - -func TeamMembersWithErrorFromJson(data io.Reader) []*TeamMemberWithError { - var o []*TeamMemberWithError - json.NewDecoder(data).Decode(&o) - return o -} - -func TeamMembersToJson(o []*TeamMember) string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func TeamMembersFromJson(data io.Reader) []*TeamMember { - var o []*TeamMember - json.NewDecoder(data).Decode(&o) - return o -} - -func TeamsUnreadToJson(o []*TeamUnread) string { - b, err := json.Marshal(o) - if err != nil { - return "[]" - } - return string(b) -} - -func TeamsUnreadFromJson(data io.Reader) []*TeamUnread { - var o []*TeamUnread - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *TeamMember) IsValid() *AppError { - - if !IsValidId(o.TeamId) { - return NewAppError("TeamMember.IsValid", "model.team_member.is_valid.team_id.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(o.UserId) { - return NewAppError("TeamMember.IsValid", "model.team_member.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (o *TeamMember) PreUpdate() { -} - -func (o *TeamMember) GetRoles() []string { - return strings.Fields(o.Roles) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/team_member_serial_gen.go b/vendor/github.com/mattermost/mattermost-server/v5/model/team_member_serial_gen.go deleted file mode 100644 index 044a608a..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/team_member_serial_gen.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "github.com/tinylib/msgp/msgp" -) - -// DecodeMsg implements msgp.Decodable -func (z *TeamMember) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0001 uint32 - zb0001, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 8 { - err = msgp.ArrayError{Wanted: 8, Got: zb0001} - return - } - z.TeamId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "TeamId") - return - } - z.UserId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - z.Roles, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.DeleteAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - z.SchemeGuest, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "SchemeGuest") - return - } - z.SchemeUser, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "SchemeUser") - return - } - z.SchemeAdmin, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "SchemeAdmin") - return - } - z.ExplicitRoles, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "ExplicitRoles") - return - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *TeamMember) EncodeMsg(en *msgp.Writer) (err error) { - // array header, size 8 - err = en.Append(0x98) - if err != nil { - return - } - err = en.WriteString(z.TeamId) - if err != nil { - err = msgp.WrapError(err, "TeamId") - return - } - err = en.WriteString(z.UserId) - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - err = en.WriteString(z.Roles) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - err = en.WriteInt64(z.DeleteAt) - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - err = en.WriteBool(z.SchemeGuest) - if err != nil { - err = msgp.WrapError(err, "SchemeGuest") - return - } - err = en.WriteBool(z.SchemeUser) - if err != nil { - err = msgp.WrapError(err, "SchemeUser") - return - } - err = en.WriteBool(z.SchemeAdmin) - if err != nil { - err = msgp.WrapError(err, "SchemeAdmin") - return - } - err = en.WriteString(z.ExplicitRoles) - if err != nil { - err = msgp.WrapError(err, "ExplicitRoles") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *TeamMember) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // array header, size 8 - o = append(o, 0x98) - o = msgp.AppendString(o, z.TeamId) - o = msgp.AppendString(o, z.UserId) - o = msgp.AppendString(o, z.Roles) - o = msgp.AppendInt64(o, z.DeleteAt) - o = msgp.AppendBool(o, z.SchemeGuest) - o = msgp.AppendBool(o, z.SchemeUser) - o = msgp.AppendBool(o, z.SchemeAdmin) - o = msgp.AppendString(o, z.ExplicitRoles) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *TeamMember) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0001 uint32 - zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 8 { - err = msgp.ArrayError{Wanted: 8, Got: zb0001} - return - } - z.TeamId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "TeamId") - return - } - z.UserId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "UserId") - return - } - z.Roles, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.DeleteAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - z.SchemeGuest, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "SchemeGuest") - return - } - z.SchemeUser, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "SchemeUser") - return - } - z.SchemeAdmin, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "SchemeAdmin") - return - } - z.ExplicitRoles, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "ExplicitRoles") - return - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *TeamMember) Msgsize() (s int) { - s = 1 + msgp.StringPrefixSize + len(z.TeamId) + msgp.StringPrefixSize + len(z.UserId) + msgp.StringPrefixSize + len(z.Roles) + msgp.Int64Size + msgp.BoolSize + msgp.BoolSize + msgp.BoolSize + msgp.StringPrefixSize + len(z.ExplicitRoles) - return -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/team_search.go b/vendor/github.com/mattermost/mattermost-server/v5/model/team_search.go deleted file mode 100644 index e24b8438..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/team_search.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type TeamSearch struct { - Term string `json:"term"` - Page *int `json:"page,omitempty"` - PerPage *int `json:"per_page,omitempty"` - AllowOpenInvite *bool `json:"allow_open_invite,omitempty"` - GroupConstrained *bool `json:"group_constrained,omitempty"` - IncludeGroupConstrained *bool `json:"include_group_constrained,omitempty"` - PolicyID *string `json:"policy_id,omitempty"` - ExcludePolicyConstrained *bool `json:"exclude_policy_constrained,omitempty"` - IncludePolicyID *bool `json:"-"` - IncludeDeleted *bool `json:"-"` - TeamType *string `json:"-"` -} - -func (t *TeamSearch) IsPaginated() bool { - return t.Page != nil && t.PerPage != nil -} - -// ToJson convert a TeamSearch to json string -func (t *TeamSearch) ToJson() string { - b, err := json.Marshal(t) - if err != nil { - return "" - } - - return string(b) -} - -// TeamSearchFromJson decodes the input and returns a TeamSearch -func TeamSearchFromJson(data io.Reader) *TeamSearch { - decoder := json.NewDecoder(data) - var cs TeamSearch - err := decoder.Decode(&cs) - if err == nil { - return &cs - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/team_stats.go b/vendor/github.com/mattermost/mattermost-server/v5/model/team_stats.go deleted file mode 100644 index 9209a0cf..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/team_stats.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type TeamStats struct { - TeamId string `json:"team_id"` - TotalMemberCount int64 `json:"total_member_count"` - ActiveMemberCount int64 `json:"active_member_count"` -} - -func (o *TeamStats) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func TeamStatsFromJson(data io.Reader) *TeamStats { - var o *TeamStats - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/terms_of_service.go b/vendor/github.com/mattermost/mattermost-server/v5/model/terms_of_service.go deleted file mode 100644 index 8ce5d350..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/terms_of_service.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "unicode/utf8" -) - -const TERMS_OF_SERVICE_CACHE_SIZE = 1 - -type TermsOfService struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at"` - UserId string `json:"user_id"` - Text string `json:"text"` -} - -func (t *TermsOfService) IsValid() *AppError { - if !IsValidId(t.Id) { - return InvalidTermsOfServiceError("id", "") - } - - if t.CreateAt == 0 { - return InvalidTermsOfServiceError("create_at", t.Id) - } - - if !IsValidId(t.UserId) { - return InvalidTermsOfServiceError("user_id", t.Id) - } - - if utf8.RuneCountInString(t.Text) > POST_MESSAGE_MAX_RUNES_V2 { - return InvalidTermsOfServiceError("text", t.Id) - } - - return nil -} - -func (t *TermsOfService) ToJson() string { - b, _ := json.Marshal(t) - return string(b) -} - -func TermsOfServiceFromJson(data io.Reader) *TermsOfService { - var termsOfService *TermsOfService - json.NewDecoder(data).Decode(&termsOfService) - return termsOfService -} - -func InvalidTermsOfServiceError(fieldName string, termsOfServiceId string) *AppError { - id := fmt.Sprintf("model.terms_of_service.is_valid.%s.app_error", fieldName) - details := "" - if termsOfServiceId != "" { - details = "terms_of_service_id=" + termsOfServiceId - } - return NewAppError("TermsOfService.IsValid", id, map[string]interface{}{"MaxLength": POST_MESSAGE_MAX_RUNES_V2}, details, http.StatusBadRequest) -} - -func (t *TermsOfService) PreSave() { - if t.Id == "" { - t.Id = NewId() - } - - t.CreateAt = GetMillis() -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/thread.go b/vendor/github.com/mattermost/mattermost-server/v5/model/thread.go deleted file mode 100644 index fe4a4014..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/thread.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" -) - -type Thread struct { - PostId string `json:"id"` - ChannelId string `json:"channel_id"` - ReplyCount int64 `json:"reply_count"` - LastReplyAt int64 `json:"last_reply_at"` - Participants StringArray `json:"participants"` -} - -type ThreadResponse struct { - PostId string `json:"id"` - ReplyCount int64 `json:"reply_count"` - LastReplyAt int64 `json:"last_reply_at"` - LastViewedAt int64 `json:"last_viewed_at"` - Participants []*User `json:"participants"` - Post *Post `json:"post"` - UnreadReplies int64 `json:"unread_replies"` - UnreadMentions int64 `json:"unread_mentions"` -} - -type Threads struct { - Total int64 `json:"total"` - TotalUnreadThreads int64 `json:"total_unread_threads"` - TotalUnreadMentions int64 `json:"total_unread_mentions"` - Threads []*ThreadResponse `json:"threads"` -} - -type GetUserThreadsOpts struct { - // PageSize specifies the size of the returned chunk of results. Default = 30 - PageSize uint64 - - // Extended will enrich the response with participant details. Default = false - Extended bool - - // Deleted will specify that even deleted threads should be returned (For mobile sync). Default = false - Deleted bool - - // Since filters the threads based on their LastUpdateAt timestamp. - Since uint64 - - // Before specifies thread id as a cursor for pagination and will return `PageSize` threads before the cursor - Before string - - // After specifies thread id as a cursor for pagination and will return `PageSize` threads after the cursor - After string - - // Unread will make sure that only threads with unread replies are returned - Unread bool - - // TotalsOnly will not fetch any threads and just fetch the total counts - TotalsOnly bool - - // TeamOnly will only fetch threads and unreads for the specified team and excludes DMs/GMs - TeamOnly bool -} - -func (o *ThreadResponse) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ThreadResponseFromJson(s string) (*ThreadResponse, error) { - var t ThreadResponse - err := json.Unmarshal([]byte(s), &t) - return &t, err -} - -func (o *Threads) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func (o *Thread) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func ThreadFromJson(s string) (*Thread, error) { - var t Thread - err := json.Unmarshal([]byte(s), &t) - return &t, err -} - -func (o *Thread) Etag() string { - return Etag(o.PostId, o.LastReplyAt) -} - -type ThreadMembership struct { - PostId string `json:"post_id"` - UserId string `json:"user_id"` - Following bool `json:"following"` - LastViewed int64 `json:"last_view_at"` - LastUpdated int64 `json:"last_update_at"` - UnreadMentions int64 `json:"unread_mentions"` -} - -func (o *ThreadMembership) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/token.go b/vendor/github.com/mattermost/mattermost-server/v5/model/token.go deleted file mode 100644 index 2dcf4143..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/token.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "net/http" -) - -const ( - TOKEN_SIZE = 64 - MAX_TOKEN_EXIPRY_TIME = 1000 * 60 * 60 * 48 // 48 hour - TOKEN_TYPE_OAUTH = "oauth" -) - -type Token struct { - Token string - CreateAt int64 - Type string - Extra string -} - -func NewToken(tokentype, extra string) *Token { - return &Token{ - Token: NewRandomString(TOKEN_SIZE), - CreateAt: GetMillis(), - Type: tokentype, - Extra: extra, - } -} - -func (t *Token) IsValid() *AppError { - if len(t.Token) != TOKEN_SIZE { - return NewAppError("Token.IsValid", "model.token.is_valid.size", nil, "", http.StatusInternalServerError) - } - - if t.CreateAt == 0 { - return NewAppError("Token.IsValid", "model.token.is_valid.expiry", nil, "", http.StatusInternalServerError) - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/typing_request.go b/vendor/github.com/mattermost/mattermost-server/v5/model/typing_request.go deleted file mode 100644 index e2e9d3bf..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/typing_request.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type TypingRequest struct { - ChannelId string `json:"channel_id"` - ParentId string `json:"parent_id"` -} - -func (o *TypingRequest) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func TypingRequestFromJson(data io.Reader) *TypingRequest { - var o *TypingRequest - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/upload_session.go b/vendor/github.com/mattermost/mattermost-server/v5/model/upload_session.go deleted file mode 100644 index c5f09083..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/upload_session.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" -) - -// UploadType defines the type of an upload. -type UploadType string - -const ( - UploadTypeAttachment UploadType = "attachment" - UploadTypeImport UploadType = "import" -) - -// UploadNoUserID is a "fake" user id used by the API layer when in local mode. -const UploadNoUserID = "nouser" - -// UploadSession contains information used to keep track of a file upload. -type UploadSession struct { - // The unique identifier for the session. - Id string `json:"id"` - // The type of the upload. - Type UploadType `json:"type"` - // The timestamp of creation. - CreateAt int64 `json:"create_at"` - // The id of the user performing the upload. - UserId string `json:"user_id"` - // The id of the channel to upload to. - ChannelId string `json:"channel_id,omitempty"` - // The name of the file to upload. - Filename string `json:"filename"` - // The path where the file is stored. - Path string `json:"-"` - // The size of the file to upload. - FileSize int64 `json:"file_size"` - // The amount of received data in bytes. If equal to FileSize it means the - // upload has finished. - FileOffset int64 `json:"file_offset"` - // Id of remote cluster if uploading for shared channel - RemoteId string `json:"remote_id"` - // Requested file id if uploading for shared channel - ReqFileId string `json:"req_file_id"` -} - -// ToJson serializes the UploadSession into JSON and returns it as string. -func (us *UploadSession) ToJson() string { - b, _ := json.Marshal(us) - return string(b) -} - -// UploadSessionsToJson serializes a list of UploadSession into JSON and -// returns it as string. -func UploadSessionsToJson(uss []*UploadSession) string { - b, _ := json.Marshal(uss) - return string(b) -} - -// UploadSessionsFromJson deserializes a list of UploadSession from JSON data. -func UploadSessionsFromJson(data io.Reader) []*UploadSession { - decoder := json.NewDecoder(data) - var uss []*UploadSession - if err := decoder.Decode(&uss); err != nil { - return nil - } - return uss -} - -// UploadSessionFromJson deserializes the UploadSession from JSON data. -func UploadSessionFromJson(data io.Reader) *UploadSession { - decoder := json.NewDecoder(data) - var us UploadSession - if err := decoder.Decode(&us); err != nil { - return nil - } - return &us -} - -// PreSave is a utility function used to fill required information. -func (us *UploadSession) PreSave() { - if us.Id == "" { - us.Id = NewId() - } - - if us.CreateAt == 0 { - us.CreateAt = GetMillis() - } -} - -// IsValid validates an UploadType. It returns an error in case of -// failure. -func (t UploadType) IsValid() error { - switch t { - case UploadTypeAttachment: - return nil - case UploadTypeImport: - return nil - default: - } - return fmt.Errorf("invalid UploadType %s", t) -} - -// IsValid validates an UploadSession. It returns an error in case of -// failure. -func (us *UploadSession) IsValid() *AppError { - if !IsValidId(us.Id) { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if err := us.Type.IsValid(); err != nil { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.type.app_error", nil, err.Error(), http.StatusBadRequest) - } - - if !IsValidId(us.UserId) && us.UserId != UploadNoUserID { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.user_id.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.Type == UploadTypeAttachment && !IsValidId(us.ChannelId) { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.channel_id.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.CreateAt == 0 { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.create_at.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.Filename == "" { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.filename.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.FileSize <= 0 { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.file_size.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.FileOffset < 0 || us.FileOffset > us.FileSize { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.file_offset.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - if us.Path == "" { - return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.path.app_error", nil, "id="+us.Id, http.StatusBadRequest) - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user.go deleted file mode 100644 index 1745d726..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user.go +++ /dev/null @@ -1,1002 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "crypto/sha256" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "regexp" - "sort" - "strings" - "unicode/utf8" - - "golang.org/x/crypto/bcrypt" - "golang.org/x/text/language" - - "github.com/mattermost/mattermost-server/v5/services/timezones" - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const ( - ME = "me" - USER_NOTIFY_ALL = "all" - USER_NOTIFY_HERE = "here" - USER_NOTIFY_MENTION = "mention" - USER_NOTIFY_NONE = "none" - DESKTOP_NOTIFY_PROP = "desktop" - DESKTOP_SOUND_NOTIFY_PROP = "desktop_sound" - MARK_UNREAD_NOTIFY_PROP = "mark_unread" - PUSH_NOTIFY_PROP = "push" - PUSH_STATUS_NOTIFY_PROP = "push_status" - EMAIL_NOTIFY_PROP = "email" - CHANNEL_MENTIONS_NOTIFY_PROP = "channel" - COMMENTS_NOTIFY_PROP = "comments" - MENTION_KEYS_NOTIFY_PROP = "mention_keys" - COMMENTS_NOTIFY_NEVER = "never" - COMMENTS_NOTIFY_ROOT = "root" - COMMENTS_NOTIFY_ANY = "any" - FIRST_NAME_NOTIFY_PROP = "first_name" - AUTO_RESPONDER_ACTIVE_NOTIFY_PROP = "auto_responder_active" - AUTO_RESPONDER_MESSAGE_NOTIFY_PROP = "auto_responder_message" - - DEFAULT_LOCALE = "en" - USER_AUTH_SERVICE_EMAIL = "email" - - USER_EMAIL_MAX_LENGTH = 128 - USER_NICKNAME_MAX_RUNES = 64 - USER_POSITION_MAX_RUNES = 128 - 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 - USER_PASSWORD_MAX_LENGTH = 72 - USER_LOCALE_MAX_LENGTH = 5 - USER_TIMEZONE_MAX_RUNES = 256 -) - -//msgp:tuple User - -// User contains the details about the user. -// This struct's serializer methods are auto-generated. If a new field is added/removed, -// please run make gen-serialized. -type User struct { - Id string `json:"id"` - CreateAt int64 `json:"create_at,omitempty"` - UpdateAt int64 `json:"update_at,omitempty"` - DeleteAt int64 `json:"delete_at"` - Username string `json:"username"` - Password string `json:"password,omitempty"` - AuthData *string `json:"auth_data,omitempty"` - AuthService string `json:"auth_service"` - Email string `json:"email"` - EmailVerified bool `json:"email_verified,omitempty"` - Nickname string `json:"nickname"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Position string `json:"position"` - Roles string `json:"roles"` - AllowMarketing bool `json:"allow_marketing,omitempty"` - Props StringMap `json:"props,omitempty"` - NotifyProps StringMap `json:"notify_props,omitempty"` - LastPasswordUpdate int64 `json:"last_password_update,omitempty"` - LastPictureUpdate int64 `json:"last_picture_update,omitempty"` - FailedAttempts int `json:"failed_attempts,omitempty"` - Locale string `json:"locale"` - Timezone StringMap `json:"timezone"` - MfaActive bool `json:"mfa_active,omitempty"` - MfaSecret string `json:"mfa_secret,omitempty"` - RemoteId *string `json:"remote_id,omitempty"` - LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"` - IsBot bool `db:"-" json:"is_bot,omitempty"` - BotDescription string `db:"-" json:"bot_description,omitempty"` - BotLastIconUpdate int64 `db:"-" json:"bot_last_icon_update,omitempty"` - TermsOfServiceId string `db:"-" json:"terms_of_service_id,omitempty"` - TermsOfServiceCreateAt int64 `db:"-" json:"terms_of_service_create_at,omitempty"` - DisableWelcomeEmail bool `db:"-" json:"disable_welcome_email"` -} - -//msgp UserMap - -// UserMap is a map from a userId to a user object. -// It is used to generate methods which can be used for fast serialization/de-serialization. -type UserMap map[string]*User - -//msgp:ignore UserUpdate -type UserUpdate struct { - Old *User - New *User -} - -//msgp:ignore UserPatch -type UserPatch struct { - Username *string `json:"username"` - Password *string `json:"password,omitempty"` - 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"` - Timezone StringMap `json:"timezone"` - RemoteId *string `json:"remote_id"` -} - -//msgp:ignore UserAuth -type UserAuth struct { - Password string `json:"password,omitempty"` // DEPRECATED: It is not used. - AuthData *string `json:"auth_data,omitempty"` - AuthService string `json:"auth_service,omitempty"` -} - -//msgp:ignore UserForIndexing -type UserForIndexing struct { - Id string `json:"id"` - Username string `json:"username"` - Nickname string `json:"nickname"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Roles string `json:"roles"` - CreateAt int64 `json:"create_at"` - DeleteAt int64 `json:"delete_at"` - TeamsIds []string `json:"team_id"` - ChannelsIds []string `json:"channel_id"` -} - -//msgp:ignore ViewUsersRestrictions -type ViewUsersRestrictions struct { - Teams []string - Channels []string -} - -func (r *ViewUsersRestrictions) Hash() string { - if r == nil { - return "" - } - ids := append(r.Teams, r.Channels...) - sort.Strings(ids) - hash := sha256.New() - hash.Write([]byte(strings.Join(ids, ""))) - return fmt.Sprintf("%x", hash.Sum(nil)) -} - -//msgp:ignore UserSlice -type UserSlice []*User - -func (u UserSlice) Usernames() []string { - usernames := []string{} - for _, user := range u { - usernames = append(usernames, user.Username) - } - sort.Strings(usernames) - return usernames -} - -func (u UserSlice) IDs() []string { - ids := []string{} - for _, user := range u { - ids = append(ids, user.Id) - } - return ids -} - -func (u UserSlice) FilterWithoutBots() UserSlice { - var matches []*User - - for _, user := range u { - if !user.IsBot { - matches = append(matches, user) - } - } - return UserSlice(matches) -} - -func (u UserSlice) FilterByActive(active bool) UserSlice { - var matches []*User - - for _, user := range u { - if user.DeleteAt == 0 && active { - matches = append(matches, user) - } else if user.DeleteAt != 0 && !active { - matches = append(matches, user) - } - } - return UserSlice(matches) -} - -func (u UserSlice) FilterByID(ids []string) UserSlice { - var matches []*User - for _, user := range u { - for _, id := range ids { - if id == user.Id { - matches = append(matches, user) - } - } - } - return UserSlice(matches) -} - -func (u UserSlice) FilterWithoutID(ids []string) UserSlice { - var keep []*User - for _, user := range u { - present := false - for _, id := range ids { - if id == user.Id { - present = true - } - } - if !present { - keep = append(keep, user) - } - } - return UserSlice(keep) -} - -func (u *User) DeepCopy() *User { - copyUser := *u - if u.AuthData != nil { - copyUser.AuthData = NewString(*u.AuthData) - } - if u.Props != nil { - copyUser.Props = CopyStringMap(u.Props) - } - if u.NotifyProps != nil { - copyUser.NotifyProps = CopyStringMap(u.NotifyProps) - } - if u.Timezone != nil { - copyUser.Timezone = CopyStringMap(u.Timezone) - } - return ©User -} - -// IsValid validates the user and returns an error if it isn't configured -// correctly. -func (u *User) IsValid() *AppError { - - if !IsValidId(u.Id) { - return InvalidUserError("id", "") - } - - if u.CreateAt == 0 { - return InvalidUserError("create_at", u.Id) - } - - if u.UpdateAt == 0 { - return InvalidUserError("update_at", u.Id) - } - - if u.IsRemote() { - if !IsValidUsernameAllowRemote(u.Username) { - return InvalidUserError("username", u.Id) - } - } else { - if !IsValidUsername(u.Username) { - return InvalidUserError("username", u.Id) - } - } - - if len(u.Email) > USER_EMAIL_MAX_LENGTH || u.Email == "" || !IsValidEmail(u.Email) { - return InvalidUserError("email", u.Id) - } - - if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES { - return InvalidUserError("nickname", u.Id) - } - - if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES { - return InvalidUserError("position", u.Id) - } - - if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES { - return InvalidUserError("first_name", u.Id) - } - - if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES { - return InvalidUserError("last_name", u.Id) - } - - if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH { - return InvalidUserError("auth_data", u.Id) - } - - if u.AuthData != nil && *u.AuthData != "" && u.AuthService == "" { - return InvalidUserError("auth_data_type", u.Id) - } - - if u.Password != "" && u.AuthData != nil && *u.AuthData != "" { - return InvalidUserError("auth_data_pwd", u.Id) - } - - if len(u.Password) > USER_PASSWORD_MAX_LENGTH { - return InvalidUserError("password_limit", u.Id) - } - - if !IsValidLocale(u.Locale) { - return InvalidUserError("locale", u.Id) - } - - if len(u.Timezone) > 0 { - if tzJSON, err := json.Marshal(u.Timezone); err != nil { - return NewAppError("User.IsValid", "model.user.is_valid.marshal.app_error", nil, err.Error(), http.StatusInternalServerError) - } else if utf8.RuneCount(tzJSON) > USER_TIMEZONE_MAX_RUNES { - return InvalidUserError("timezone_limit", u.Id) - } - } - - return nil -} - -func InvalidUserError(fieldName string, userId string) *AppError { - id := fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName) - details := "" - if userId != "" { - details = "user_id=" + userId - } - return NewAppError("User.IsValid", id, nil, details, http.StatusBadRequest) -} - -func NormalizeUsername(username string) string { - return strings.ToLower(username) -} - -func NormalizeEmail(email string) string { - return strings.ToLower(email) -} - -// PreSave will set the Id and Username if missing. It will also fill -// in the CreateAt, UpdateAt times. It will also hash the password. It should -// be run before saving the user to the db. -func (u *User) PreSave() { - if u.Id == "" { - u.Id = NewId() - } - - if u.Username == "" { - u.Username = NewId() - } - - if u.AuthData != nil && *u.AuthData == "" { - u.AuthData = nil - } - - u.Username = SanitizeUnicode(u.Username) - u.FirstName = SanitizeUnicode(u.FirstName) - u.LastName = SanitizeUnicode(u.LastName) - u.Nickname = SanitizeUnicode(u.Nickname) - - u.Username = NormalizeUsername(u.Username) - u.Email = NormalizeEmail(u.Email) - - u.CreateAt = GetMillis() - u.UpdateAt = u.CreateAt - - u.LastPasswordUpdate = u.CreateAt - - u.MfaActive = false - - if u.Locale == "" { - u.Locale = DEFAULT_LOCALE - } - - if u.Props == nil { - u.Props = make(map[string]string) - } - - if u.NotifyProps == nil || len(u.NotifyProps) == 0 { - u.SetDefaultNotifications() - } - - if u.Timezone == nil { - u.Timezone = timezones.DefaultUserTimezone() - } - - if u.Password != "" { - u.Password = HashPassword(u.Password) - } -} - -// PreUpdate should be run before updating the user in the db. -func (u *User) PreUpdate() { - u.Username = SanitizeUnicode(u.Username) - u.FirstName = SanitizeUnicode(u.FirstName) - u.LastName = SanitizeUnicode(u.LastName) - u.Nickname = SanitizeUnicode(u.Nickname) - u.BotDescription = SanitizeUnicode(u.BotDescription) - - u.Username = NormalizeUsername(u.Username) - u.Email = NormalizeEmail(u.Email) - u.UpdateAt = GetMillis() - - u.FirstName = SanitizeUnicode(u.FirstName) - u.LastName = SanitizeUnicode(u.LastName) - u.Nickname = SanitizeUnicode(u.Nickname) - u.BotDescription = SanitizeUnicode(u.BotDescription) - - if u.AuthData != nil && *u.AuthData == "" { - u.AuthData = nil - } - - if u.NotifyProps == nil || len(u.NotifyProps) == 0 { - u.SetDefaultNotifications() - } else if _, ok := u.NotifyProps[MENTION_KEYS_NOTIFY_PROP]; ok { - // Remove any blank mention keys - splitKeys := strings.Split(u.NotifyProps[MENTION_KEYS_NOTIFY_PROP], ",") - goodKeys := []string{} - for _, key := range splitKeys { - if key != "" { - goodKeys = append(goodKeys, strings.ToLower(key)) - } - } - u.NotifyProps[MENTION_KEYS_NOTIFY_PROP] = strings.Join(goodKeys, ",") - } -} - -func (u *User) SetDefaultNotifications() { - u.NotifyProps = make(map[string]string) - u.NotifyProps[EMAIL_NOTIFY_PROP] = "true" - u.NotifyProps[PUSH_NOTIFY_PROP] = USER_NOTIFY_MENTION - u.NotifyProps[DESKTOP_NOTIFY_PROP] = USER_NOTIFY_MENTION - u.NotifyProps[DESKTOP_SOUND_NOTIFY_PROP] = "true" - u.NotifyProps[MENTION_KEYS_NOTIFY_PROP] = "" - u.NotifyProps[CHANNEL_MENTIONS_NOTIFY_PROP] = "true" - u.NotifyProps[PUSH_STATUS_NOTIFY_PROP] = STATUS_AWAY - u.NotifyProps[COMMENTS_NOTIFY_PROP] = COMMENTS_NOTIFY_NEVER - u.NotifyProps[FIRST_NAME_NOTIFY_PROP] = "false" -} - -func (u *User) UpdateMentionKeysFromUsername(oldUsername string) { - nonUsernameKeys := []string{} - for _, key := range u.GetMentionKeys() { - if key != oldUsername && key != "@"+oldUsername { - nonUsernameKeys = append(nonUsernameKeys, key) - } - } - - u.NotifyProps[MENTION_KEYS_NOTIFY_PROP] = "" - if len(nonUsernameKeys) > 0 { - u.NotifyProps[MENTION_KEYS_NOTIFY_PROP] += "," + strings.Join(nonUsernameKeys, ",") - } -} - -func (u *User) GetMentionKeys() []string { - var keys []string - - for _, key := range strings.Split(u.NotifyProps[MENTION_KEYS_NOTIFY_PROP], ",") { - trimmedKey := strings.TrimSpace(key) - - if trimmedKey == "" { - continue - } - - keys = append(keys, trimmedKey) - } - - return keys -} - -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 - } - - if patch.Timezone != nil { - u.Timezone = patch.Timezone - } - - if patch.RemoteId != nil { - u.RemoteId = patch.RemoteId - } -} - -// ToJson convert a User to a json string -func (u *User) ToJson() string { - b, _ := json.Marshal(u) - return string(b) -} - -func (u *UserPatch) ToJson() string { - b, _ := json.Marshal(u) - return string(b) -} - -func (u *UserAuth) ToJson() string { - b, _ := json.Marshal(u) - 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, u.TermsOfServiceId, u.TermsOfServiceCreateAt, showFullName, showEmail, u.BotLastIconUpdate) -} - -// Remove any private data from the user object -func (u *User) Sanitize(options map[string]bool) { - u.Password = "" - u.AuthData = NewString("") - u.MfaSecret = "" - - if len(options) != 0 && !options["email"] { - u.Email = "" - } - if len(options) != 0 && !options["fullname"] { - u.FirstName = "" - u.LastName = "" - } - if len(options) != 0 && !options["passwordupdate"] { - u.LastPasswordUpdate = 0 - } - if len(options) != 0 && !options["authservice"] { - u.AuthService = "" - } -} - -// Remove any input data from the user object that is not user controlled -func (u *User) SanitizeInput(isAdmin bool) { - if !isAdmin { - u.AuthData = NewString("") - u.AuthService = "" - u.EmailVerified = false - } - u.LastPasswordUpdate = 0 - u.LastPictureUpdate = 0 - u.FailedAttempts = 0 - u.MfaActive = false - u.MfaSecret = "" - u.Email = strings.TrimSpace(u.Email) -} - -func (u *User) ClearNonProfileFields() { - u.Password = "" - u.AuthData = NewString("") - u.MfaSecret = "" - u.EmailVerified = false - u.AllowMarketing = false - u.NotifyProps = StringMap{} - u.LastPasswordUpdate = 0 - u.FailedAttempts = 0 -} - -func (u *User) SanitizeProfile(options map[string]bool) { - u.ClearNonProfileFields() - - u.Sanitize(options) -} - -func (u *User) MakeNonNil() { - if u.Props == nil { - u.Props = make(map[string]string) - } - - if u.NotifyProps == nil { - u.NotifyProps = make(map[string]string) - } -} - -func (u *User) AddNotifyProp(key string, value string) { - u.MakeNonNil() - - u.NotifyProps[key] = value -} - -func (u *User) SetCustomStatus(cs *CustomStatus) { - u.MakeNonNil() - u.Props[UserPropsKeyCustomStatus] = cs.ToJson() -} - -func (u *User) ClearCustomStatus() { - u.MakeNonNil() - u.Props[UserPropsKeyCustomStatus] = "" -} - -func (u *User) GetFullName() string { - if u.FirstName != "" && u.LastName != "" { - return u.FirstName + " " + u.LastName - } else if u.FirstName != "" { - return u.FirstName - } else if u.LastName != "" { - return u.LastName - } else { - return "" - } -} - -func (u *User) getDisplayName(baseName, nameFormat string) string { - displayName := baseName - - if nameFormat == SHOW_NICKNAME_FULLNAME { - if u.Nickname != "" { - displayName = u.Nickname - } else if fullName := u.GetFullName(); fullName != "" { - displayName = fullName - } - } else if nameFormat == SHOW_FULLNAME { - if fullName := u.GetFullName(); fullName != "" { - displayName = fullName - } - } - - return displayName -} - -func (u *User) GetDisplayName(nameFormat string) string { - displayName := u.Username - - return u.getDisplayName(displayName, nameFormat) -} - -func (u *User) GetDisplayNameWithPrefix(nameFormat, prefix string) string { - displayName := prefix + u.Username - - return u.getDisplayName(displayName, nameFormat) -} - -func (u *User) GetRoles() []string { - return strings.Fields(u.Roles) -} - -func (u *User) GetRawRoles() string { - return u.Roles -} - -func IsValidUserRoles(userRoles string) bool { - - roles := strings.Fields(userRoles) - - for _, r := range roles { - if !IsValidRoleName(r) { - return false - } - } - - // Exclude just the system_admin role explicitly to prevent mistakes - if len(roles) == 1 && roles[0] == "system_admin" { - return false - } - - return true -} - -// Make sure you acually want to use this function. In context.go there are functions to check permissions -// This function should not be used to check permissions. -func (u *User) IsGuest() bool { - return IsInRole(u.Roles, SYSTEM_GUEST_ROLE_ID) -} - -func (u *User) IsSystemAdmin() bool { - return IsInRole(u.Roles, SYSTEM_ADMIN_ROLE_ID) -} - -// Make sure you acually want to use this function. In context.go there are functions to check permissions -// This function should not be used to check permissions. -func (u *User) IsInRole(inRole string) bool { - return IsInRole(u.Roles, inRole) -} - -// Make sure you acually want to use this function. In context.go there are functions to check permissions -// This function should not be used to check permissions. -func IsInRole(userRoles string, inRole string) bool { - roles := strings.Split(userRoles, " ") - - for _, r := range roles { - if r == inRole { - return true - } - } - - return false -} - -func (u *User) IsSSOUser() bool { - return u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL -} - -func (u *User) IsOAuthUser() bool { - return u.AuthService == SERVICE_GITLAB || - u.AuthService == SERVICE_GOOGLE || - u.AuthService == SERVICE_OFFICE365 || - u.AuthService == SERVICE_OPENID -} - -func (u *User) IsLDAPUser() bool { - return u.AuthService == USER_AUTH_SERVICE_LDAP -} - -func (u *User) IsSAMLUser() bool { - return u.AuthService == USER_AUTH_SERVICE_SAML -} - -func (u *User) GetPreferredTimezone() string { - return GetPreferredTimezone(u.Timezone) -} - -// IsRemote returns true if the user belongs to a remote cluster (has RemoteId). -func (u *User) IsRemote() bool { - return u.RemoteId != nil && *u.RemoteId != "" -} - -// GetRemoteID returns the remote id for this user or "" if not a remote user. -func (u *User) GetRemoteID() string { - if u.RemoteId != nil { - return *u.RemoteId - } - return "" -} - -// GetProp fetches a prop value by name. -func (u *User) GetProp(name string) (string, bool) { - val, ok := u.Props[name] - return val, ok -} - -// SetProp sets a prop value by name, creating the map if nil. -// Not thread safe. -func (u *User) SetProp(name string, value string) { - if u.Props == nil { - u.Props = make(map[string]string) - } - u.Props[name] = value -} - -func (u *User) ToPatch() *UserPatch { - return &UserPatch{ - Username: &u.Username, Password: &u.Password, - Nickname: &u.Nickname, FirstName: &u.FirstName, LastName: &u.LastName, - Position: &u.Position, Email: &u.Email, - Props: u.Props, NotifyProps: u.NotifyProps, - Locale: &u.Locale, Timezone: u.Timezone, - } -} - -func (u *UserPatch) SetField(fieldName string, fieldValue string) { - switch fieldName { - case "FirstName": - u.FirstName = &fieldValue - case "LastName": - u.LastName = &fieldValue - case "Nickname": - u.Nickname = &fieldValue - case "Email": - u.Email = &fieldValue - case "Position": - u.Position = &fieldValue - case "Username": - u.Username = &fieldValue - } -} - -// UserFromJson will decode the input and return a User -func UserFromJson(data io.Reader) *User { - var user *User - json.NewDecoder(data).Decode(&user) - return user -} - -func UserPatchFromJson(data io.Reader) *UserPatch { - var user *UserPatch - json.NewDecoder(data).Decode(&user) - return user -} - -func UserAuthFromJson(data io.Reader) *UserAuth { - var user *UserAuth - json.NewDecoder(data).Decode(&user) - return user -} - -func UserMapToJson(u map[string]*User) string { - b, _ := json.Marshal(u) - return string(b) -} - -func UserMapFromJson(data io.Reader) map[string]*User { - var users map[string]*User - json.NewDecoder(data).Decode(&users) - return users -} - -func UserListToJson(u []*User) string { - b, _ := json.Marshal(u) - return string(b) -} - -func UserListFromJson(data io.Reader) []*User { - var users []*User - json.NewDecoder(data).Decode(&users) - return users -} - -// HashPassword generates a hash using the bcrypt.GenerateFromPassword -func HashPassword(password string) string { - hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) - if err != nil { - panic(err) - } - - return string(hash) -} - -// ComparePassword compares the hash -// This function is deprecated and will be removed in a future release. -func ComparePassword(hash string, password string) bool { - - if password == "" || hash == "" { - return false - } - - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} - -var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`) -var validUsernameCharsForRemote = regexp.MustCompile(`^[a-z0-9\.\-_:]+$`) - -var restrictedUsernames = map[string]struct{}{ - "all": {}, - "channel": {}, - "matterbot": {}, - "system": {}, -} - -func IsValidUsername(s string) bool { - if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH { - return false - } - - if !validUsernameChars.MatchString(s) { - return false - } - - _, found := restrictedUsernames[s] - return !found -} - -func IsValidUsernameAllowRemote(s string) bool { - if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH { - return false - } - - if !validUsernameCharsForRemote.MatchString(s) { - return false - } - - _, found := restrictedUsernames[s] - return !found -} - -func CleanUsername(username string) string { - s := NormalizeUsername(strings.Replace(username, " ", "-", -1)) - - for _, value := range reservedName { - if s == value { - s = strings.Replace(s, value, "", -1) - } - } - - s = strings.TrimSpace(s) - - for _, c := range s { - char := fmt.Sprintf("%c", c) - if !validUsernameChars.MatchString(char) { - s = strings.Replace(s, char, "-", -1) - } - } - - s = strings.Trim(s, "-") - - if !IsValidUsername(s) { - s = "a" + NewId() - mlog.Warn("Generating new username since provided username was invalid", - mlog.String("provided_username", username), mlog.String("new_username", s)) - } - - return s -} - -func IsValidUserNotifyLevel(notifyLevel string) bool { - return notifyLevel == CHANNEL_NOTIFY_ALL || - notifyLevel == CHANNEL_NOTIFY_MENTION || - notifyLevel == CHANNEL_NOTIFY_NONE -} - -func IsValidPushStatusNotifyLevel(notifyLevel string) bool { - return notifyLevel == STATUS_ONLINE || - notifyLevel == STATUS_AWAY || - notifyLevel == STATUS_OFFLINE -} - -func IsValidCommentsNotifyLevel(notifyLevel string) bool { - return notifyLevel == COMMENTS_NOTIFY_ANY || - notifyLevel == COMMENTS_NOTIFY_ROOT || - notifyLevel == COMMENTS_NOTIFY_NEVER -} - -func IsValidEmailBatchingInterval(emailInterval string) bool { - return emailInterval == PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY || - emailInterval == PREFERENCE_EMAIL_INTERVAL_FIFTEEN || - emailInterval == PREFERENCE_EMAIL_INTERVAL_HOUR -} - -func IsValidLocale(locale string) bool { - if locale != "" { - if len(locale) > USER_LOCALE_MAX_LENGTH { - return false - } else if _, err := language.Parse(locale); err != nil { - return false - } - } - - return true -} - -//msgp:ignore UserWithGroups -type UserWithGroups struct { - User - GroupIDs *string `json:"-"` - Groups []*Group `json:"groups"` - SchemeGuest bool `json:"scheme_guest"` - SchemeUser bool `json:"scheme_user"` - SchemeAdmin bool `json:"scheme_admin"` -} - -func (u *UserWithGroups) GetGroupIDs() []string { - if u.GroupIDs == nil { - return nil - } - trimmed := strings.TrimSpace(*u.GroupIDs) - if trimmed == "" { - return nil - } - return strings.Split(trimmed, ",") -} - -//msgp:ignore UsersWithGroupsAndCount -type UsersWithGroupsAndCount struct { - Users []*UserWithGroups `json:"users"` - Count int64 `json:"total_count"` -} - -func UsersWithGroupsAndCountFromJson(data io.Reader) *UsersWithGroupsAndCount { - uwg := &UsersWithGroupsAndCount{} - bodyBytes, _ := ioutil.ReadAll(data) - json.Unmarshal(bodyBytes, uwg) - return uwg -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token.go deleted file mode 100644 index f458a6d9..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - "net/http" -) - -type UserAccessToken struct { - Id string `json:"id"` - Token string `json:"token,omitempty"` - UserId string `json:"user_id"` - Description string `json:"description"` - IsActive bool `json:"is_active"` -} - -func (t *UserAccessToken) IsValid() *AppError { - if !IsValidId(t.Id) { - return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.id.app_error", nil, "", http.StatusBadRequest) - } - - if len(t.Token) != 26 { - return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.token.app_error", nil, "", http.StatusBadRequest) - } - - if !IsValidId(t.UserId) { - return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) - } - - if len(t.Description) > 255 { - return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.description.app_error", nil, "", http.StatusBadRequest) - } - - return nil -} - -func (t *UserAccessToken) PreSave() { - t.Id = NewId() - t.IsActive = true -} - -func (t *UserAccessToken) ToJson() string { - b, _ := json.Marshal(t) - return string(b) -} - -func UserAccessTokenFromJson(data io.Reader) *UserAccessToken { - var t *UserAccessToken - json.NewDecoder(data).Decode(&t) - return t -} - -func UserAccessTokenListToJson(t []*UserAccessToken) string { - b, _ := json.Marshal(t) - return string(b) -} - -func UserAccessTokenListFromJson(data io.Reader) []*UserAccessToken { - var t []*UserAccessToken - json.NewDecoder(data).Decode(&t) - return t -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token_search.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token_search.go deleted file mode 100644 index a692f692..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_access_token_search.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type UserAccessTokenSearch struct { - Term string `json:"term"` -} - -// ToJson convert a UserAccessTokenSearch to json string -func (c *UserAccessTokenSearch) ToJson() string { - b, err := json.Marshal(c) - if err != nil { - return "" - } - - return string(b) -} - -// UserAccessTokenSearchJson decodes the input and returns a UserAccessTokenSearch -func UserAccessTokenSearchFromJson(data io.Reader) *UserAccessTokenSearch { - decoder := json.NewDecoder(data) - var cs UserAccessTokenSearch - err := decoder.Decode(&cs) - if err == nil { - return &cs - } - - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_autocomplete.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_autocomplete.go deleted file mode 100644 index 118e138d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_autocomplete.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type UserAutocompleteInChannel struct { - InChannel []*User `json:"in_channel"` - OutOfChannel []*User `json:"out_of_channel"` -} - -type UserAutocompleteInTeam struct { - InTeam []*User `json:"in_team"` -} - -type UserAutocomplete struct { - Users []*User `json:"users"` - OutOfChannel []*User `json:"out_of_channel,omitempty"` -} - -func (o *UserAutocomplete) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func UserAutocompleteFromJson(data io.Reader) *UserAutocomplete { - decoder := json.NewDecoder(data) - autocomplete := new(UserAutocomplete) - err := decoder.Decode(&autocomplete) - if err != nil { - return nil - } - return autocomplete -} - -func (o *UserAutocompleteInChannel) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func UserAutocompleteInChannelFromJson(data io.Reader) *UserAutocompleteInChannel { - var o *UserAutocompleteInChannel - json.NewDecoder(data).Decode(&o) - return o -} - -func (o *UserAutocompleteInTeam) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func UserAutocompleteInTeamFromJson(data io.Reader) *UserAutocompleteInTeam { - var o *UserAutocompleteInTeam - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_count.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_count.go deleted file mode 100644 index ee474883..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_count.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -// Options for counting users -type UserCountOptions struct { - // Should include users that are bots - IncludeBotAccounts bool - // Should include deleted users (of any type) - IncludeDeleted bool - // Exclude regular users - ExcludeRegularUsers bool - // Only include users on a specific team. "" for any team. - TeamId string - // Only include users on a specific channel. "" for any channel. - ChannelId string - // Restrict to search in a list of teams and channels - ViewRestrictions *ViewUsersRestrictions - // Only include users matching any of the given system wide roles. - Roles []string - // Only include users matching any of the given channel roles, must be used with ChannelId. - ChannelRoles []string - // Only include users matching any of the given team roles, must be used with TeamId. - TeamRoles []string -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_get.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_get.go deleted file mode 100644 index 2748d735..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_get.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -type UserGetOptions struct { - // Filters the users in the team - InTeamId string - // Filters the users not in the team - NotInTeamId string - // Filters the users in the channel - InChannelId string - // Filters the users not in the channel - NotInChannelId string - // Filters the users in the group - InGroupId string - // Filters the users group constrained - GroupConstrained bool - // Filters the users without a team - WithoutTeam bool - // Filters the inactive users - Inactive bool - // Filters the active users - Active bool - // Filters for the given role - Role string - // Filters for users matching any of the given system wide roles - Roles []string - // Filters for users matching any of the given channel roles, must be used with InChannelId - ChannelRoles []string - // Filters for users matching any of the given team roles, must be used with InTeamId - TeamRoles []string - // Sorting option - Sort string - // Restrict to search in a list of teams and channels - ViewRestrictions *ViewUsersRestrictions - // Page - Page int - // Page size - PerPage int -} - -type UserGetByIdsOptions struct { - // Since filters the users based on their UpdateAt timestamp. - Since int64 -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_search.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_search.go deleted file mode 100644 index 0a721eac..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_search.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -const USER_SEARCH_MAX_LIMIT = 1000 -const USER_SEARCH_DEFAULT_LIMIT = 100 - -// UserSearch captures the parameters provided by a client for initiating a user search. -type UserSearch struct { - Term string `json:"term"` - TeamId string `json:"team_id"` - NotInTeamId string `json:"not_in_team_id"` - InChannelId string `json:"in_channel_id"` - NotInChannelId string `json:"not_in_channel_id"` - InGroupId string `json:"in_group_id"` - GroupConstrained bool `json:"group_constrained"` - AllowInactive bool `json:"allow_inactive"` - WithoutTeam bool `json:"without_team"` - Limit int `json:"limit"` - Role string `json:"role"` - Roles []string `json:"roles"` - ChannelRoles []string `json:"channel_roles"` - TeamRoles []string `json:"team_roles"` -} - -// ToJson convert a User to a json string -func (u *UserSearch) ToJson() []byte { - b, _ := json.Marshal(u) - return b -} - -// UserSearchFromJson will decode the input and return a User -func UserSearchFromJson(data io.Reader) *UserSearch { - us := UserSearch{} - json.NewDecoder(data).Decode(&us) - - if us.Limit == 0 { - us.Limit = USER_SEARCH_DEFAULT_LIMIT - } - - return &us -} - -// UserSearchOptions captures internal parameters derived from the user's permissions and a -// UserSearch request. -type UserSearchOptions struct { - // IsAdmin tracks whether or not the search is being conducted by an administrator. - IsAdmin bool - // AllowEmails allows search to examine the emails of users. - AllowEmails bool - // AllowFullNames allows search to examine the full names of users, vs. just usernames and nicknames. - AllowFullNames bool - // AllowInactive configures whether or not to return inactive users in the search results. - AllowInactive bool - // Narrows the search to the group constrained users - GroupConstrained bool - // Limit limits the total number of results returned. - Limit int - // Filters for the given role - Role string - // Filters for users that have any of the given system roles - Roles []string - // Filters for users that have the given channel roles to be used when searching in a channel - ChannelRoles []string - // Filters for users that have the given team roles to be used when searching in a team - TeamRoles []string - // Restrict to search in a list of teams and channels - ViewRestrictions *ViewUsersRestrictions - // List of allowed channels - ListOfAllowedChannels []string -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_serial_gen.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_serial_gen.go deleted file mode 100644 index fb40b577..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_serial_gen.go +++ /dev/null @@ -1,826 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "github.com/tinylib/msgp/msgp" -) - -// DecodeMsg implements msgp.Decodable -func (z *User) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0001 uint32 - zb0001, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 32 { - err = msgp.ArrayError{Wanted: 32, Got: zb0001} - return - } - z.Id, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - z.CreateAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - z.UpdateAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "UpdateAt") - return - } - z.DeleteAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - z.Username, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Username") - return - } - z.Password, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Password") - return - } - if dc.IsNil() { - err = dc.ReadNil() - if err != nil { - err = msgp.WrapError(err, "AuthData") - return - } - z.AuthData = nil - } else { - if z.AuthData == nil { - z.AuthData = new(string) - } - *z.AuthData, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "AuthData") - return - } - } - z.AuthService, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "AuthService") - return - } - z.Email, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Email") - return - } - z.EmailVerified, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "EmailVerified") - return - } - z.Nickname, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Nickname") - return - } - z.FirstName, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "FirstName") - return - } - z.LastName, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "LastName") - return - } - z.Position, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Position") - return - } - z.Roles, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.AllowMarketing, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "AllowMarketing") - return - } - err = z.Props.DecodeMsg(dc) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - err = z.NotifyProps.DecodeMsg(dc) - if err != nil { - err = msgp.WrapError(err, "NotifyProps") - return - } - z.LastPasswordUpdate, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "LastPasswordUpdate") - return - } - z.LastPictureUpdate, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "LastPictureUpdate") - return - } - z.FailedAttempts, err = dc.ReadInt() - if err != nil { - err = msgp.WrapError(err, "FailedAttempts") - return - } - z.Locale, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Locale") - return - } - err = z.Timezone.DecodeMsg(dc) - if err != nil { - err = msgp.WrapError(err, "Timezone") - return - } - z.MfaActive, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "MfaActive") - return - } - z.MfaSecret, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "MfaSecret") - return - } - if dc.IsNil() { - err = dc.ReadNil() - if err != nil { - err = msgp.WrapError(err, "RemoteId") - return - } - z.RemoteId = nil - } else { - if z.RemoteId == nil { - z.RemoteId = new(string) - } - *z.RemoteId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "RemoteId") - return - } - } - z.LastActivityAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - z.IsBot, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "IsBot") - return - } - z.BotDescription, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "BotDescription") - return - } - z.BotLastIconUpdate, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "BotLastIconUpdate") - return - } - z.TermsOfServiceId, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceId") - return - } - z.TermsOfServiceCreateAt, err = dc.ReadInt64() - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceCreateAt") - return - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *User) EncodeMsg(en *msgp.Writer) (err error) { - // array header, size 32 - err = en.Append(0xdc, 0x0, 0x20) - if err != nil { - return - } - err = en.WriteString(z.Id) - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - err = en.WriteInt64(z.CreateAt) - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - err = en.WriteInt64(z.UpdateAt) - if err != nil { - err = msgp.WrapError(err, "UpdateAt") - return - } - err = en.WriteInt64(z.DeleteAt) - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - err = en.WriteString(z.Username) - if err != nil { - err = msgp.WrapError(err, "Username") - return - } - err = en.WriteString(z.Password) - if err != nil { - err = msgp.WrapError(err, "Password") - return - } - if z.AuthData == nil { - err = en.WriteNil() - if err != nil { - return - } - } else { - err = en.WriteString(*z.AuthData) - if err != nil { - err = msgp.WrapError(err, "AuthData") - return - } - } - err = en.WriteString(z.AuthService) - if err != nil { - err = msgp.WrapError(err, "AuthService") - return - } - err = en.WriteString(z.Email) - if err != nil { - err = msgp.WrapError(err, "Email") - return - } - err = en.WriteBool(z.EmailVerified) - if err != nil { - err = msgp.WrapError(err, "EmailVerified") - return - } - err = en.WriteString(z.Nickname) - if err != nil { - err = msgp.WrapError(err, "Nickname") - return - } - err = en.WriteString(z.FirstName) - if err != nil { - err = msgp.WrapError(err, "FirstName") - return - } - err = en.WriteString(z.LastName) - if err != nil { - err = msgp.WrapError(err, "LastName") - return - } - err = en.WriteString(z.Position) - if err != nil { - err = msgp.WrapError(err, "Position") - return - } - err = en.WriteString(z.Roles) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - err = en.WriteBool(z.AllowMarketing) - if err != nil { - err = msgp.WrapError(err, "AllowMarketing") - return - } - err = z.Props.EncodeMsg(en) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - err = z.NotifyProps.EncodeMsg(en) - if err != nil { - err = msgp.WrapError(err, "NotifyProps") - return - } - err = en.WriteInt64(z.LastPasswordUpdate) - if err != nil { - err = msgp.WrapError(err, "LastPasswordUpdate") - return - } - err = en.WriteInt64(z.LastPictureUpdate) - if err != nil { - err = msgp.WrapError(err, "LastPictureUpdate") - return - } - err = en.WriteInt(z.FailedAttempts) - if err != nil { - err = msgp.WrapError(err, "FailedAttempts") - return - } - err = en.WriteString(z.Locale) - if err != nil { - err = msgp.WrapError(err, "Locale") - return - } - err = z.Timezone.EncodeMsg(en) - if err != nil { - err = msgp.WrapError(err, "Timezone") - return - } - err = en.WriteBool(z.MfaActive) - if err != nil { - err = msgp.WrapError(err, "MfaActive") - return - } - err = en.WriteString(z.MfaSecret) - if err != nil { - err = msgp.WrapError(err, "MfaSecret") - return - } - if z.RemoteId == nil { - err = en.WriteNil() - if err != nil { - return - } - } else { - err = en.WriteString(*z.RemoteId) - if err != nil { - err = msgp.WrapError(err, "RemoteId") - return - } - } - err = en.WriteInt64(z.LastActivityAt) - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - err = en.WriteBool(z.IsBot) - if err != nil { - err = msgp.WrapError(err, "IsBot") - return - } - err = en.WriteString(z.BotDescription) - if err != nil { - err = msgp.WrapError(err, "BotDescription") - return - } - err = en.WriteInt64(z.BotLastIconUpdate) - if err != nil { - err = msgp.WrapError(err, "BotLastIconUpdate") - return - } - err = en.WriteString(z.TermsOfServiceId) - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceId") - return - } - err = en.WriteInt64(z.TermsOfServiceCreateAt) - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceCreateAt") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *User) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // array header, size 32 - o = append(o, 0xdc, 0x0, 0x20) - o = msgp.AppendString(o, z.Id) - o = msgp.AppendInt64(o, z.CreateAt) - o = msgp.AppendInt64(o, z.UpdateAt) - o = msgp.AppendInt64(o, z.DeleteAt) - o = msgp.AppendString(o, z.Username) - o = msgp.AppendString(o, z.Password) - if z.AuthData == nil { - o = msgp.AppendNil(o) - } else { - o = msgp.AppendString(o, *z.AuthData) - } - o = msgp.AppendString(o, z.AuthService) - o = msgp.AppendString(o, z.Email) - o = msgp.AppendBool(o, z.EmailVerified) - o = msgp.AppendString(o, z.Nickname) - o = msgp.AppendString(o, z.FirstName) - o = msgp.AppendString(o, z.LastName) - o = msgp.AppendString(o, z.Position) - o = msgp.AppendString(o, z.Roles) - o = msgp.AppendBool(o, z.AllowMarketing) - o, err = z.Props.MarshalMsg(o) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - o, err = z.NotifyProps.MarshalMsg(o) - if err != nil { - err = msgp.WrapError(err, "NotifyProps") - return - } - o = msgp.AppendInt64(o, z.LastPasswordUpdate) - o = msgp.AppendInt64(o, z.LastPictureUpdate) - o = msgp.AppendInt(o, z.FailedAttempts) - o = msgp.AppendString(o, z.Locale) - o, err = z.Timezone.MarshalMsg(o) - if err != nil { - err = msgp.WrapError(err, "Timezone") - return - } - o = msgp.AppendBool(o, z.MfaActive) - o = msgp.AppendString(o, z.MfaSecret) - if z.RemoteId == nil { - o = msgp.AppendNil(o) - } else { - o = msgp.AppendString(o, *z.RemoteId) - } - o = msgp.AppendInt64(o, z.LastActivityAt) - o = msgp.AppendBool(o, z.IsBot) - o = msgp.AppendString(o, z.BotDescription) - o = msgp.AppendInt64(o, z.BotLastIconUpdate) - o = msgp.AppendString(o, z.TermsOfServiceId) - o = msgp.AppendInt64(o, z.TermsOfServiceCreateAt) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *User) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0001 uint32 - zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0001 != 32 { - err = msgp.ArrayError{Wanted: 32, Got: zb0001} - return - } - z.Id, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Id") - return - } - z.CreateAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "CreateAt") - return - } - z.UpdateAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "UpdateAt") - return - } - z.DeleteAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "DeleteAt") - return - } - z.Username, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Username") - return - } - z.Password, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Password") - return - } - if msgp.IsNil(bts) { - bts, err = msgp.ReadNilBytes(bts) - if err != nil { - return - } - z.AuthData = nil - } else { - if z.AuthData == nil { - z.AuthData = new(string) - } - *z.AuthData, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "AuthData") - return - } - } - z.AuthService, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "AuthService") - return - } - z.Email, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Email") - return - } - z.EmailVerified, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "EmailVerified") - return - } - z.Nickname, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Nickname") - return - } - z.FirstName, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "FirstName") - return - } - z.LastName, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "LastName") - return - } - z.Position, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Position") - return - } - z.Roles, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Roles") - return - } - z.AllowMarketing, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "AllowMarketing") - return - } - bts, err = z.Props.UnmarshalMsg(bts) - if err != nil { - err = msgp.WrapError(err, "Props") - return - } - bts, err = z.NotifyProps.UnmarshalMsg(bts) - if err != nil { - err = msgp.WrapError(err, "NotifyProps") - return - } - z.LastPasswordUpdate, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "LastPasswordUpdate") - return - } - z.LastPictureUpdate, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "LastPictureUpdate") - return - } - z.FailedAttempts, bts, err = msgp.ReadIntBytes(bts) - if err != nil { - err = msgp.WrapError(err, "FailedAttempts") - return - } - z.Locale, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Locale") - return - } - bts, err = z.Timezone.UnmarshalMsg(bts) - if err != nil { - err = msgp.WrapError(err, "Timezone") - return - } - z.MfaActive, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "MfaActive") - return - } - z.MfaSecret, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "MfaSecret") - return - } - if msgp.IsNil(bts) { - bts, err = msgp.ReadNilBytes(bts) - if err != nil { - return - } - z.RemoteId = nil - } else { - if z.RemoteId == nil { - z.RemoteId = new(string) - } - *z.RemoteId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "RemoteId") - return - } - } - z.LastActivityAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "LastActivityAt") - return - } - z.IsBot, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "IsBot") - return - } - z.BotDescription, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "BotDescription") - return - } - z.BotLastIconUpdate, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "BotLastIconUpdate") - return - } - z.TermsOfServiceId, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceId") - return - } - z.TermsOfServiceCreateAt, bts, err = msgp.ReadInt64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "TermsOfServiceCreateAt") - return - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *User) Msgsize() (s int) { - s = 3 + msgp.StringPrefixSize + len(z.Id) + msgp.Int64Size + msgp.Int64Size + msgp.Int64Size + msgp.StringPrefixSize + len(z.Username) + msgp.StringPrefixSize + len(z.Password) - if z.AuthData == nil { - s += msgp.NilSize - } else { - s += msgp.StringPrefixSize + len(*z.AuthData) - } - s += msgp.StringPrefixSize + len(z.AuthService) + msgp.StringPrefixSize + len(z.Email) + msgp.BoolSize + msgp.StringPrefixSize + len(z.Nickname) + msgp.StringPrefixSize + len(z.FirstName) + msgp.StringPrefixSize + len(z.LastName) + msgp.StringPrefixSize + len(z.Position) + msgp.StringPrefixSize + len(z.Roles) + msgp.BoolSize + z.Props.Msgsize() + z.NotifyProps.Msgsize() + msgp.Int64Size + msgp.Int64Size + msgp.IntSize + msgp.StringPrefixSize + len(z.Locale) + z.Timezone.Msgsize() + msgp.BoolSize + msgp.StringPrefixSize + len(z.MfaSecret) - if z.RemoteId == nil { - s += msgp.NilSize - } else { - s += msgp.StringPrefixSize + len(*z.RemoteId) - } - s += msgp.Int64Size + msgp.BoolSize + msgp.StringPrefixSize + len(z.BotDescription) + msgp.Int64Size + msgp.StringPrefixSize + len(z.TermsOfServiceId) + msgp.Int64Size - return -} - -// DecodeMsg implements msgp.Decodable -func (z *UserMap) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0003 uint32 - zb0003, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(UserMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - zb0003-- - var zb0001 string - var zb0002 *User - zb0001, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err) - return - } - if dc.IsNil() { - err = dc.ReadNil() - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - zb0002 = nil - } else { - if zb0002 == nil { - zb0002 = new(User) - } - err = zb0002.DecodeMsg(dc) - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - } - (*z)[zb0001] = zb0002 - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z UserMap) EncodeMsg(en *msgp.Writer) (err error) { - err = en.WriteMapHeader(uint32(len(z))) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0004, zb0005 := range z { - err = en.WriteString(zb0004) - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0005 == nil { - err = en.WriteNil() - if err != nil { - return - } - } else { - err = zb0005.EncodeMsg(en) - if err != nil { - err = msgp.WrapError(err, zb0004) - return - } - } - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z UserMap) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - o = msgp.AppendMapHeader(o, uint32(len(z))) - for zb0004, zb0005 := range z { - o = msgp.AppendString(o, zb0004) - if zb0005 == nil { - o = msgp.AppendNil(o) - } else { - o, err = zb0005.MarshalMsg(o) - if err != nil { - err = msgp.WrapError(err, zb0004) - return - } - } - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *UserMap) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0003 uint32 - zb0003, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(UserMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - var zb0001 string - var zb0002 *User - zb0003-- - zb0001, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if msgp.IsNil(bts) { - bts, err = msgp.ReadNilBytes(bts) - if err != nil { - return - } - zb0002 = nil - } else { - if zb0002 == nil { - zb0002 = new(User) - } - bts, err = zb0002.UnmarshalMsg(bts) - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - } - (*z)[zb0001] = zb0002 - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z UserMap) Msgsize() (s int) { - s = msgp.MapHeaderSize - if z != nil { - for zb0004, zb0005 := range z { - _ = zb0005 - s += msgp.StringPrefixSize + len(zb0004) - if zb0005 == nil { - s += msgp.NilSize - } else { - s += zb0005.Msgsize() - } - } - } - return -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/user_terms_of_service.go b/vendor/github.com/mattermost/mattermost-server/v5/model/user_terms_of_service.go deleted file mode 100644 index 9a0f4f18..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/user_terms_of_service.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "fmt" - "io" - "net/http" -) - -type UserTermsOfService struct { - UserId string `json:"user_id"` - TermsOfServiceId string `json:"terms_of_service_id"` - CreateAt int64 `json:"create_at"` -} - -func (ut *UserTermsOfService) IsValid() *AppError { - if !IsValidId(ut.UserId) { - return InvalidUserTermsOfServiceError("user_id", ut.UserId) - } - - if !IsValidId(ut.TermsOfServiceId) { - return InvalidUserTermsOfServiceError("terms_of_service_id", ut.UserId) - } - - if ut.CreateAt == 0 { - return InvalidUserTermsOfServiceError("create_at", ut.UserId) - } - - return nil -} - -func (ut *UserTermsOfService) ToJson() string { - b, _ := json.Marshal(ut) - return string(b) -} - -func (ut *UserTermsOfService) PreSave() { - if ut.UserId == "" { - ut.UserId = NewId() - } - - ut.CreateAt = GetMillis() -} - -func UserTermsOfServiceFromJson(data io.Reader) *UserTermsOfService { - var userTermsOfService *UserTermsOfService - json.NewDecoder(data).Decode(&userTermsOfService) - return userTermsOfService -} - -func InvalidUserTermsOfServiceError(fieldName string, userTermsOfServiceId string) *AppError { - id := fmt.Sprintf("model.user_terms_of_service.is_valid.%s.app_error", fieldName) - details := "" - if userTermsOfServiceId != "" { - details = "user_terms_of_service_user_id=" + userTermsOfServiceId - } - return NewAppError("UserTermsOfService.IsValid", id, nil, details, http.StatusBadRequest) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/users_stats.go b/vendor/github.com/mattermost/mattermost-server/v5/model/users_stats.go deleted file mode 100644 index 3e18296e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/users_stats.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" -) - -type UsersStats struct { - TotalUsersCount int64 `json:"total_users_count"` -} - -func (o *UsersStats) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func UsersStatsFromJson(data io.Reader) *UsersStats { - var o *UsersStats - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/utils.go b/vendor/github.com/mattermost/mattermost-server/v5/model/utils.go deleted file mode 100644 index 0c5a272c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/utils.go +++ /dev/null @@ -1,726 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "crypto/rand" - "encoding/base32" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/mail" - "net/url" - "regexp" - "strconv" - "strings" - "sync" - "time" - "unicode" - - "github.com/mattermost/mattermost-server/v5/shared/i18n" - "github.com/pborman/uuid" -) - -const ( - LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz" - UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - NUMBERS = "0123456789" - SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~" - MB = 1 << 20 -) - -type StringInterface map[string]interface{} -type StringArray []string - -func (sa StringArray) Remove(input string) StringArray { - for index := range sa { - if sa[index] == input { - ret := make(StringArray, 0, len(sa)-1) - ret = append(ret, sa[:index]...) - return append(ret, sa[index+1:]...) - } - } - return sa -} - -func (sa StringArray) Contains(input string) bool { - for index := range sa { - if sa[index] == input { - return true - } - } - - return false -} -func (sa StringArray) Equals(input StringArray) bool { - - if len(sa) != len(input) { - return false - } - - for index := range sa { - - if sa[index] != input[index] { - return false - } - } - - return true -} - -var translateFunc i18n.TranslateFunc -var translateFuncOnce sync.Once - -func AppErrorInit(t i18n.TranslateFunc) { - translateFuncOnce.Do(func() { - translateFunc = t - }) -} - -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{} -} - -func (er *AppError) Error() string { - return er.Where + ": " + er.Message + ", " + er.DetailedError -} - -func (er *AppError) Translate(T i18n.TranslateFunc) { - if T == nil { - er.Message = er.Id - return - } - - if er.params == nil { - er.Message = T(er.Id) - } else { - er.Message = T(er.Id, er.params) - } -} - -func (er *AppError) SystemMessage(T i18n.TranslateFunc) string { - if er.params == nil { - return T(er.Id) - } - return T(er.Id, er.params) -} - -func (er *AppError) ToJson() string { - b, _ := json.Marshal(er) - return string(b) -} - -// AppErrorFromJson will decode the input and return an AppError -func AppErrorFromJson(data io.Reader) *AppError { - str := "" - bytes, rerr := ioutil.ReadAll(data) - if rerr != nil { - str = rerr.Error() - } else { - str = string(bytes) - } - - decoder := json.NewDecoder(strings.NewReader(str)) - var er AppError - err := decoder.Decode(&er) - if err != nil { - return NewAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, "body: "+str, http.StatusInternalServerError) - } - return &er -} - -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 - ap.Translate(translateFunc) - return ap -} - -var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769") - -// NewId is a globally unique identifier. It is a [A-Z0-9] string 26 -// characters long. It is a UUID version 4 Guid that is zbased32 encoded -// with the padding stripped off. -func NewId() string { - var b bytes.Buffer - encoder := base32.NewEncoder(encoding, &b) - encoder.Write(uuid.NewRandom()) - encoder.Close() - b.Truncate(26) // removes the '==' padding - return b.String() -} - -// NewRandomTeamName is a NewId that will be a valid team name. -func NewRandomTeamName() string { - teamName := NewId() - for IsReservedTeamName(teamName) { - teamName = NewId() - } - return teamName -} - -// NewRandomString returns a random string of the given length. -// The resulting entropy will be (5 * length) bits. -func NewRandomString(length int) string { - data := make([]byte, 1+(length*5/8)) - rand.Read(data) - return encoding.EncodeToString(data)[:length] -} - -// GetMillis is a convenience method to get milliseconds since epoch. -func GetMillis() int64 { - return time.Now().UnixNano() / int64(time.Millisecond) -} - -// GetMillisForTime is a convenience method to get milliseconds since epoch for provided Time. -func GetMillisForTime(thisTime time.Time) int64 { - return thisTime.UnixNano() / int64(time.Millisecond) -} - -// GetTimeForMillis is a convenience method to get time.Time for milliseconds since epoch. -func GetTimeForMillis(millis int64) time.Time { - return time.Unix(0, millis*int64(time.Millisecond)) -} - -// PadDateStringZeros is a convenience method to pad 2 digit date parts with zeros to meet ISO 8601 format -func PadDateStringZeros(dateString string) string { - parts := strings.Split(dateString, "-") - for index, part := range parts { - if len(part) == 1 { - parts[index] = "0" + part - } - } - dateString = strings.Join(parts[:], "-") - return dateString -} - -// GetStartOfDayMillis is a convenience method to get milliseconds since epoch for provided date's start of day -func GetStartOfDayMillis(thisTime time.Time, timeZoneOffset int) int64 { - localSearchTimeZone := time.FixedZone("Local Search Time Zone", timeZoneOffset) - resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 0, 0, 0, 0, localSearchTimeZone) - return GetMillisForTime(resultTime) -} - -// GetEndOfDayMillis is a convenience method to get milliseconds since epoch for provided date's end of day -func GetEndOfDayMillis(thisTime time.Time, timeZoneOffset int) int64 { - localSearchTimeZone := time.FixedZone("Local Search Time Zone", timeZoneOffset) - resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 23, 59, 59, 999999999, localSearchTimeZone) - return GetMillisForTime(resultTime) -} - -func CopyStringMap(originalMap map[string]string) map[string]string { - copyMap := make(map[string]string) - for k, v := range originalMap { - copyMap[k] = v - } - return copyMap -} - -// MapToJson converts a map to a json string -func MapToJson(objmap map[string]string) string { - b, _ := json.Marshal(objmap) - return string(b) -} - -// MapBoolToJson converts a map to a json string -func MapBoolToJson(objmap map[string]bool) string { - b, _ := json.Marshal(objmap) - return string(b) -} - -// MapFromJson will decode the key/value pair map -func MapFromJson(data io.Reader) map[string]string { - decoder := json.NewDecoder(data) - - var objmap map[string]string - if err := decoder.Decode(&objmap); err != nil { - return make(map[string]string) - } - return objmap -} - -// MapFromJson will decode the key/value pair map -func MapBoolFromJson(data io.Reader) map[string]bool { - decoder := json.NewDecoder(data) - - var objmap map[string]bool - if err := decoder.Decode(&objmap); err != nil { - return make(map[string]bool) - } - return objmap -} - -func ArrayToJson(objmap []string) string { - b, _ := json.Marshal(objmap) - return string(b) -} - -func ArrayFromJson(data io.Reader) []string { - decoder := json.NewDecoder(data) - - var objmap []string - if err := decoder.Decode(&objmap); err != nil { - return make([]string, 0) - } - return objmap -} - -func ArrayFromInterface(data interface{}) []string { - stringArray := []string{} - - dataArray, ok := data.([]interface{}) - if !ok { - return stringArray - } - - for _, v := range dataArray { - if str, ok := v.(string); ok { - stringArray = append(stringArray, str) - } - } - - return stringArray -} - -func StringInterfaceToJson(objmap map[string]interface{}) string { - b, _ := json.Marshal(objmap) - return string(b) -} - -func StringInterfaceFromJson(data io.Reader) map[string]interface{} { - decoder := json.NewDecoder(data) - - var objmap map[string]interface{} - if err := decoder.Decode(&objmap); err != nil { - return make(map[string]interface{}) - } - return objmap -} - -func StringToJson(s string) string { - b, _ := json.Marshal(s) - return string(b) -} - -func StringFromJson(data io.Reader) string { - decoder := json.NewDecoder(data) - - var s string - if err := decoder.Decode(&s); err != nil { - return "" - } - return s -} - -// ToJson serializes an arbitrary data type to JSON, discarding the error. -func ToJson(v interface{}) []byte { - b, _ := json.Marshal(v) - return b -} - -func GetServerIpAddress(iface string) string { - var addrs []net.Addr - if iface == "" { - var err error - addrs, err = net.InterfaceAddrs() - if err != nil { - return "" - } - } else { - interfaces, err := net.Interfaces() - if err != nil { - return "" - } - for _, i := range interfaces { - if i.Name == iface { - addrs, err = i.Addrs() - if err != nil { - return "" - } - break - } - } - } - - for _, addr := range addrs { - - if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && !ip.IP.IsLinkLocalUnicast() && !ip.IP.IsLinkLocalMulticast() { - if ip.IP.To4() != nil { - return ip.IP.String() - } - } - } - - return "" -} - -func IsLower(s string) bool { - return strings.ToLower(s) == s -} - -func IsValidEmail(email string) bool { - if !IsLower(email) { - return false - } - - if addr, err := mail.ParseAddress(email); err != nil { - return false - } else if addr.Name != "" { - // mail.ParseAddress accepts input of the form "Billy Bob <billy@example.com>" which we don't allow - return false - } - - return true -} - -var reservedName = []string{ - "admin", - "api", - "channel", - "claim", - "error", - "files", - "help", - "landing", - "login", - "mfa", - "oauth", - "plug", - "plugins", - "post", - "signup", -} - -func IsValidChannelIdentifier(s string) bool { - - if !IsValidAlphaNumHyphenUnderscore(s, true) { - return false - } - - if len(s) < CHANNEL_NAME_MIN_LENGTH { - return false - } - - return true -} - -func IsValidAlphaNum(s string) bool { - validAlphaNum := regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) - - return validAlphaNum.MatchString(s) -} - -func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool { - if withFormat { - validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`) - return validAlphaNumHyphenUnderscore.MatchString(s) - } - - validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) - return validSimpleAlphaNumHyphenUnderscore.MatchString(s) -} - -func IsValidAlphaNumHyphenUnderscorePlus(s string) bool { - - validSimpleAlphaNumHyphenUnderscorePlus := regexp.MustCompile(`^[a-zA-Z0-9+_-]+$`) - return validSimpleAlphaNumHyphenUnderscorePlus.MatchString(s) -} - -func Etag(parts ...interface{}) string { - - etag := CurrentVersion - - for _, part := range parts { - etag += fmt.Sprintf(".%v", part) - } - - return etag -} - -var validHashtag = regexp.MustCompile(`^(#\pL[\pL\d\-_.]*[\pL\d])$`) -var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`) -var hashtagStart = regexp.MustCompile(`^#{2,}`) -var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`) - -func ParseHashtags(text string) (string, string) { - words := strings.Fields(text) - - hashtagString := "" - plainString := "" - for _, word := range words { - // trim off surrounding punctuation - word = puncStart.ReplaceAllString(word, "") - word = puncEnd.ReplaceAllString(word, "") - - // and remove extra pound #s - word = hashtagStart.ReplaceAllString(word, "#") - - if validHashtag.MatchString(word) { - hashtagString += " " + word - } else { - plainString += " " + word - } - } - - if len(hashtagString) > 1000 { - hashtagString = hashtagString[:999] - lastSpace := strings.LastIndex(hashtagString, " ") - if lastSpace > -1 { - hashtagString = hashtagString[:lastSpace] - } else { - hashtagString = "" - } - } - - return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString) -} - -func ClearMentionTags(post string) string { - post = strings.Replace(post, "<mention>", "", -1) - post = strings.Replace(post, "</mention>", "", -1) - return post -} - -func IsValidHttpUrl(rawUrl string) bool { - if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { - return false - } - - if u, err := url.ParseRequestURI(rawUrl); err != nil || u.Scheme == "" || u.Host == "" { - return false - } - - return true -} - -func IsValidTurnOrStunServer(rawUri string) bool { - if strings.Index(rawUri, "turn:") != 0 && strings.Index(rawUri, "stun:") != 0 { - return false - } - - if _, err := url.ParseRequestURI(rawUri); err != nil { - return false - } - - return true -} - -func IsSafeLink(link *string) bool { - if link != nil { - if IsValidHttpUrl(*link) { - return true - } else if strings.HasPrefix(*link, "/") { - return true - } else { - return false - } - } - - return true -} - -func IsValidWebsocketUrl(rawUrl string) bool { - if strings.Index(rawUrl, "ws://") != 0 && strings.Index(rawUrl, "wss://") != 0 { - return false - } - - if _, err := url.ParseRequestURI(rawUrl); err != nil { - return false - } - - return true -} - -func IsValidTrueOrFalseString(value string) bool { - return value == "true" || value == "false" -} - -func IsValidNumberString(value string) bool { - if _, err := strconv.Atoi(value); err != nil { - return false - } - - return true -} - -func IsValidId(value string) bool { - if len(value) != 26 { - return false - } - - for _, r := range value { - if !unicode.IsLetter(r) && !unicode.IsNumber(r) { - return false - } - } - - return true -} - -// Copied from https://golang.org/src/net/dnsclient.go#L119 -func IsDomainName(s string) bool { - // See RFC 1035, RFC 3696. - // Presentation format has dots before every label except the first, and the - // terminal empty label is optional here because we assume fully-qualified - // (absolute) input. We must therefore reserve space for the first and last - // labels' length octets in wire format, where they are necessary and the - // maximum total length is 255. - // So our _effective_ maximum is 253, but 254 is not rejected if the last - // character is a dot. - l := len(s) - if l == 0 || l > 254 || l == 254 && s[l-1] != '.' { - return false - } - - last := byte('.') - ok := false // Ok once we've seen a letter. - partlen := 0 - for i := 0; i < len(s); i++ { - c := s[i] - switch { - default: - return false - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': - ok = true - partlen++ - case '0' <= c && c <= '9': - // fine - partlen++ - case c == '-': - // Byte before dash cannot be dot. - if last == '.' { - return false - } - partlen++ - case c == '.': - // Byte before dot cannot be dot, dash. - if last == '.' || last == '-' { - return false - } - if partlen > 63 || partlen == 0 { - return false - } - partlen = 0 - } - last = c - } - if last == '-' || partlen > 63 { - return false - } - - return ok -} - -func RemoveDuplicateStrings(in []string) []string { - out := []string{} - seen := make(map[string]bool, len(in)) - - for _, item := range in { - if !seen[item] { - out = append(out, item) - - seen[item] = true - } - } - - return out -} - -func GetPreferredTimezone(timezone StringMap) string { - if timezone["useAutomaticTimezone"] == "true" { - return timezone["automaticTimezone"] - } - - return timezone["manualTimezone"] -} - -// IsSamlFile checks if filename is a SAML file. -func IsSamlFile(saml *SamlSettings, filename string) bool { - return filename == *saml.PublicCertificateFile || filename == *saml.PrivateKeyFile || filename == *saml.IdpCertificateFile -} - -func AsStringBoolMap(list []string) map[string]bool { - listMap := map[string]bool{} - for _, p := range list { - listMap[p] = true - } - return listMap -} - -// SanitizeUnicode will remove undesirable Unicode characters from a string. -func SanitizeUnicode(s string) string { - return strings.Map(filterBlocklist, s) -} - -// filterBlocklist returns `r` if it is not in the blocklist, otherwise drop (-1). -// Blocklist is taken from https://www.w3.org/TR/unicode-xml/#Charlist -func filterBlocklist(r rune) rune { - const drop = -1 - switch r { - case '\u0340', '\u0341': // clones of grave and acute; deprecated in Unicode - return drop - case '\u17A3', '\u17D3': // obsolete characters for Khmer; deprecated in Unicode - return drop - case '\u2028', '\u2029': // line and paragraph separator - return drop - case '\u202A', '\u202B', '\u202C', '\u202D', '\u202E': // BIDI embedding controls - return drop - case '\u206A', '\u206B': // activate/inhibit symmetric swapping; deprecated in Unicode - return drop - case '\u206C', '\u206D': // activate/inhibit Arabic form shaping; deprecated in Unicode - return drop - case '\u206E', '\u206F': // activate/inhibit national digit shapes; deprecated in Unicode - return drop - case '\uFFF9', '\uFFFA', '\uFFFB': // interlinear annotation characters - return drop - case '\uFEFF': // byte order mark - return drop - case '\uFFFC': // object replacement character - return drop - } - - // Scoping for musical notation - if r >= 0x0001D173 && r <= 0x0001D17A { - return drop - } - - // Language tag code points - if r >= 0x000E0000 && r <= 0x000E007F { - return drop - } - - return r -} - -// UniqueStrings returns a unique subset of the string slice provided. -func UniqueStrings(input []string) []string { - u := make([]string, 0, len(input)) - m := make(map[string]bool) - - for _, val := range input { - if _, ok := m[val]; !ok { - m[val] = true - u = append(u, val) - } - } - - return u -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/version.go b/vendor/github.com/mattermost/mattermost-server/v5/model/version.go deleted file mode 100644 index 28904965..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/version.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "fmt" - "strconv" - "strings" -) - -// This is a list of all the current versions including any patches. -// It should be maintained in chronological order with most current -// release at the front of the list. -var versions = []string{ - "5.39.3", - "5.39.2", - "5.39.1", - "5.39.0", - "5.38.2", - "5.38.1", - "5.38.0", - "5.37.0", - "5.36.0", - "5.35.0", - "5.34.0", - "5.33.0", - "5.32.0", - "5.31.0", - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.0", - "5.25.0", - "5.24.0", - "5.23.0", - "5.22.0", - "5.21.0", - "5.20.0", - "5.19.0", - "5.18.0", - "5.17.0", - "5.16.0", - "5.15.0", - "5.14.0", - "5.13.0", - "5.12.0", - "5.11.0", - "5.10.0", - "5.9.0", - "5.8.0", - "5.7.0", - "5.6.0", - "5.5.0", - "5.4.0", - "5.3.0", - "5.2.0", - "5.1.0", - "5.0.0", - "4.10.0", - "4.9.0", - "4.8.1", - "4.8.0", - "4.7.2", - "4.7.1", - "4.7.0", - "4.6.0", - "4.5.0", - "4.4.0", - "4.3.0", - "4.2.0", - "4.1.0", - "4.0.0", - "3.10.0", - "3.9.0", - "3.8.0", - "3.7.0", - "3.6.0", - "3.5.0", - "3.4.0", - "3.3.0", - "3.2.0", - "3.1.0", - "3.0.0", - "2.2.0", - "2.1.0", - "2.0.0", - "1.4.0", - "1.3.0", - "1.2.1", - "1.2.0", - "1.1.0", - "1.0.0", - "0.7.1", - "0.7.0", - "0.6.0", - "0.5.0", -} - -var CurrentVersion string = versions[0] -var BuildNumber string -var BuildDate string -var BuildHash string -var BuildHashEnterprise string -var BuildEnterpriseReady string -var versionsWithoutHotFixes []string - -func init() { - versionsWithoutHotFixes = make([]string, 0, len(versions)) - seen := make(map[string]string) - - for _, version := range versions { - maj, min, _ := SplitVersion(version) - verStr := fmt.Sprintf("%v.%v.0", maj, min) - - if seen[verStr] == "" { - versionsWithoutHotFixes = append(versionsWithoutHotFixes, verStr) - seen[verStr] = verStr - } - } -} - -func SplitVersion(version string) (int64, int64, int64) { - parts := strings.Split(version, ".") - - major := int64(0) - minor := int64(0) - patch := int64(0) - - if len(parts) > 0 { - major, _ = strconv.ParseInt(parts[0], 10, 64) - } - - if len(parts) > 1 { - minor, _ = strconv.ParseInt(parts[1], 10, 64) - } - - if len(parts) > 2 { - patch, _ = strconv.ParseInt(parts[2], 10, 64) - } - - return major, minor, patch -} - -func GetPreviousVersion(version string) string { - verMajor, verMinor, _ := SplitVersion(version) - verStr := fmt.Sprintf("%v.%v.0", verMajor, verMinor) - - for index, v := range versionsWithoutHotFixes { - if v == verStr && len(versionsWithoutHotFixes) > index+1 { - return versionsWithoutHotFixes[index+1] - } - } - - return "" -} - -func IsCurrentVersion(versionToCheck string) bool { - currentMajor, currentMinor, _ := SplitVersion(CurrentVersion) - toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck) - - if toCheckMajor == currentMajor && toCheckMinor == currentMinor { - return true - } - return false -} - -func IsPreviousVersionsSupported(versionToCheck string) bool { - toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck) - versionToCheckStr := fmt.Sprintf("%v.%v.0", toCheckMajor, toCheckMinor) - - // Current Supported - if versionsWithoutHotFixes[0] == versionToCheckStr { - return true - } - - // Current - 1 Supported - if versionsWithoutHotFixes[1] == versionToCheckStr { - return true - } - - // Current - 2 Supported - if versionsWithoutHotFixes[2] == versionToCheckStr { - return true - } - - // Current - 3 Supported - if versionsWithoutHotFixes[3] == versionToCheckStr { - return true - } - - return false -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_client.go b/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_client.go deleted file mode 100644 index cd89e2d8..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_client.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "encoding/json" - "net/http" - "sync/atomic" - "time" - - "github.com/gorilla/websocket" -) - -const ( - SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB - PING_TIMEOUT_BUFFER_SECONDS = 5 -) - -type msgType int - -const ( - msgTypeJSON msgType = iota + 1 - msgTypePong -) - -type writeMessage struct { - msgType msgType - data interface{} -} - -const avgReadMsgSizeBytes = 1024 - -// WebSocketClient stores the necessary information required to -// communicate with a WebSocket endpoint. -// A client must read from PingTimeoutChannel, EventChannel and ResponseChannel to prevent -// deadlocks from occurring in the program. -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" - ConnectUrl string // The WebSocket URL to connect to like "ws://localhost:8065/api/v3/path/to/websocket" - Conn *websocket.Conn // The WebSocket connection - AuthToken string // The token used to open the WebSocket connection - Sequence int64 // The ever-incrementing sequence attached to each WebSocket action - PingTimeoutChannel chan bool // The channel used to signal ping timeouts - EventChannel chan *WebSocketEvent // The channel used to receive various events pushed from the server. For example: typing, posted - ResponseChannel chan *WebSocketResponse // The channel used to receive responses for requests made to the server - ListenError *AppError // A field that is set if there was an abnormal closure of the WebSocket connection - writeChan chan writeMessage - - pingTimeoutTimer *time.Timer - quitPingWatchdog chan struct{} - - quitWriterChan chan struct{} - resetTimerChan chan struct{} - closed int32 -} - -// NewWebSocketClient constructs a new WebSocket client with convenience -// methods for talking to the server. -func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { - return NewWebSocketClientWithDialer(websocket.DefaultDialer, url, authToken) -} - -// NewWebSocketClientWithDialer constructs a new WebSocket client with convenience -// methods for talking to the server using a custom dialer. -func NewWebSocketClientWithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) { - conn, _, err := dialer.Dial(url+API_URL_SUFFIX+"/websocket", nil) - if err != nil { - return nil, NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) - } - - client := &WebSocketClient{ - Url: url, - ApiUrl: url + API_URL_SUFFIX, - ConnectUrl: url + API_URL_SUFFIX + "/websocket", - Conn: conn, - AuthToken: authToken, - Sequence: 1, - PingTimeoutChannel: make(chan bool, 1), - EventChannel: make(chan *WebSocketEvent, 100), - ResponseChannel: make(chan *WebSocketResponse, 100), - writeChan: make(chan writeMessage), - quitPingWatchdog: make(chan struct{}), - quitWriterChan: make(chan struct{}), - resetTimerChan: make(chan struct{}), - } - - client.configurePingHandling() - go client.writer() - - client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken}) - - return client, nil -} - -// NewWebSocketClient4 constructs a new WebSocket client with convenience -// methods for talking to the server. Uses the v4 endpoint. -func NewWebSocketClient4(url, authToken string) (*WebSocketClient, *AppError) { - return NewWebSocketClient4WithDialer(websocket.DefaultDialer, url, authToken) -} - -// NewWebSocketClient4WithDialer constructs a new WebSocket client with convenience -// methods for talking to the server using a custom dialer. Uses the v4 endpoint. -func NewWebSocketClient4WithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) { - return NewWebSocketClientWithDialer(dialer, url, authToken) -} - -// Connect creates a websocket connection with the given ConnectUrl. -// This is racy and error-prone should not be used. Use any of the New* functions to create a websocket. -func (wsc *WebSocketClient) Connect() *AppError { - return wsc.ConnectWithDialer(websocket.DefaultDialer) -} - -// ConnectWithDialer creates a websocket connection with the given ConnectUrl using the dialer. -// This is racy and error-prone and should not be used. Use any of the New* functions to create a websocket. -func (wsc *WebSocketClient) ConnectWithDialer(dialer *websocket.Dialer) *AppError { - var err error - wsc.Conn, _, err = dialer.Dial(wsc.ConnectUrl, nil) - if err != nil { - return NewAppError("Connect", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) - } - // Super racy and should not be done anyways. - // All of this needs to be redesigned for v6. - wsc.configurePingHandling() - // If it has been closed before, we just restart the writer. - if atomic.CompareAndSwapInt32(&wsc.closed, 1, 0) { - wsc.writeChan = make(chan writeMessage) - wsc.quitWriterChan = make(chan struct{}) - go wsc.writer() - wsc.resetTimerChan = make(chan struct{}) - wsc.quitPingWatchdog = make(chan struct{}) - } - - wsc.EventChannel = make(chan *WebSocketEvent, 100) - wsc.ResponseChannel = make(chan *WebSocketResponse, 100) - - wsc.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": wsc.AuthToken}) - - return nil -} - -// Close closes the websocket client. It is recommended that a closed client should not be -// reused again. Rather a new client should be created anew. -func (wsc *WebSocketClient) Close() { - // CAS to 1 and proceed. Return if already 1. - if !atomic.CompareAndSwapInt32(&wsc.closed, 0, 1) { - return - } - wsc.quitWriterChan <- struct{}{} - close(wsc.writeChan) - // We close the connection, which breaks the reader loop. - // Then we let the defer block in the reader do further cleanup. - wsc.Conn.Close() -} - -// TODO: un-export the Conn so that Write methods go through the writer -func (wsc *WebSocketClient) writer() { - for { - select { - case msg := <-wsc.writeChan: - switch msg.msgType { - case msgTypeJSON: - wsc.Conn.WriteJSON(msg.data) - case msgTypePong: - wsc.Conn.WriteMessage(websocket.PongMessage, []byte{}) - } - case <-wsc.quitWriterChan: - return - } - } -} - -// Listen starts the read loop of the websocket client. -func (wsc *WebSocketClient) Listen() { - // This loop can exit in 2 conditions: - // 1. Either the connection breaks naturally. - // 2. Close was explicitly called, which closes the connection manually. - // - // Due to the way the API is written, there is a requirement that a client may NOT - // call Listen at all and can still call Close and Connect. - // Therefore, we let the cleanup of the reader stuff rely on closing the connection - // and then we do the cleanup in the defer block. - // - // First, we close some channels and then CAS to 1 and proceed to close the writer chan also. - // This is needed because then the defer clause does not double-close the writer when (2) happens. - // But if (1) happens, we set the closed bit, and close the rest of the stuff. - go func() { - defer func() { - close(wsc.EventChannel) - close(wsc.ResponseChannel) - close(wsc.quitPingWatchdog) - close(wsc.resetTimerChan) - // We CAS to 1 and proceed. - if !atomic.CompareAndSwapInt32(&wsc.closed, 0, 1) { - return - } - wsc.quitWriterChan <- struct{}{} - close(wsc.writeChan) - wsc.Conn.Close() // This can most likely be removed. Needs to be checked. - }() - - var buf bytes.Buffer - buf.Grow(avgReadMsgSizeBytes) - - for { - // Reset buffer. - buf.Reset() - _, r, err := wsc.Conn.NextReader() - if err != nil { - if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) { - wsc.ListenError = NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) - } - return - } - // Use pre-allocated buffer. - _, err = buf.ReadFrom(r) - if err != nil { - // This should use a different error ID, but en.json is not imported anyways. - // It's a different bug altogether but we let it be for now. - // See MM-24520. - wsc.ListenError = NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) - return - } - - event := WebSocketEventFromJson(bytes.NewReader(buf.Bytes())) - if event == nil { - continue - } - if event.IsValid() { - wsc.EventChannel <- event - continue - } - - var response WebSocketResponse - if err := json.Unmarshal(buf.Bytes(), &response); err == nil && response.IsValid() { - wsc.ResponseChannel <- &response - continue - } - } - }() -} - -func (wsc *WebSocketClient) SendMessage(action string, data map[string]interface{}) { - req := &WebSocketRequest{} - req.Seq = wsc.Sequence - req.Action = action - req.Data = data - - wsc.Sequence++ - wsc.writeChan <- writeMessage{ - msgType: msgTypeJSON, - data: req, - } -} - -// UserTyping will push a user_typing event out to all connected users -// who are in the specified channel -func (wsc *WebSocketClient) UserTyping(channelId, parentId string) { - data := map[string]interface{}{ - "channel_id": channelId, - "parent_id": parentId, - } - - wsc.SendMessage("user_typing", data) -} - -// GetStatuses will return a map of string statuses using user id as the key -func (wsc *WebSocketClient) GetStatuses() { - wsc.SendMessage("get_statuses", nil) -} - -// GetStatusesByIds will fetch certain user statuses based on ids and return -// a map of string statuses using user id as the key -func (wsc *WebSocketClient) GetStatusesByIds(userIds []string) { - data := map[string]interface{}{ - "user_ids": userIds, - } - wsc.SendMessage("get_statuses_by_ids", data) -} - -func (wsc *WebSocketClient) configurePingHandling() { - wsc.Conn.SetPingHandler(wsc.pingHandler) - wsc.pingTimeoutTimer = time.NewTimer(time.Second * (60 + PING_TIMEOUT_BUFFER_SECONDS)) - go wsc.pingWatchdog() -} - -func (wsc *WebSocketClient) pingHandler(appData string) error { - if atomic.LoadInt32(&wsc.closed) == 1 { - return nil - } - wsc.resetTimerChan <- struct{}{} - wsc.writeChan <- writeMessage{ - msgType: msgTypePong, - } - return nil -} - -// pingWatchdog is used to send values to the PingTimeoutChannel whenever a timeout occurs. -// We use the resetTimerChan from the pingHandler to pass the signal, and then reset the timer -// after draining it. And if the timer naturally expires, we also extend it to prevent it from -// being deadlocked when the resetTimerChan case runs. Because timer.Stop would return false, -// and the code would be forever stuck trying to read from C. -func (wsc *WebSocketClient) pingWatchdog() { - for { - select { - case <-wsc.resetTimerChan: - if !wsc.pingTimeoutTimer.Stop() { - <-wsc.pingTimeoutTimer.C - } - wsc.pingTimeoutTimer.Reset(time.Second * (60 + PING_TIMEOUT_BUFFER_SECONDS)) - - case <-wsc.pingTimeoutTimer.C: - wsc.PingTimeoutChannel <- true - wsc.pingTimeoutTimer.Reset(time.Second * (60 + PING_TIMEOUT_BUFFER_SECONDS)) - case <-wsc.quitPingWatchdog: - return - } - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_message.go b/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_message.go deleted file mode 100644 index 9a845d36..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_message.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "bytes" - "encoding/json" - "fmt" - "io" -) - -const ( - WEBSOCKET_EVENT_TYPING = "typing" - WEBSOCKET_EVENT_POSTED = "posted" - WEBSOCKET_EVENT_POST_EDITED = "post_edited" - WEBSOCKET_EVENT_POST_DELETED = "post_deleted" - WEBSOCKET_EVENT_POST_UNREAD = "post_unread" - WEBSOCKET_EVENT_CHANNEL_CONVERTED = "channel_converted" - WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created" - WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" - WEBSOCKET_EVENT_CHANNEL_RESTORED = "channel_restored" - WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated" - WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED = "channel_member_updated" - WEBSOCKET_EVENT_CHANNEL_SCHEME_UPDATED = "channel_scheme_updated" - WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" - WEBSOCKET_EVENT_GROUP_ADDED = "group_added" - WEBSOCKET_EVENT_NEW_USER = "new_user" - WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team" - WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" - WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" - WEBSOCKET_EVENT_DELETE_TEAM = "delete_team" - WEBSOCKET_EVENT_RESTORE_TEAM = "restore_team" - WEBSOCKET_EVENT_UPDATE_TEAM_SCHEME = "update_team_scheme" - WEBSOCKET_EVENT_USER_ADDED = "user_added" - WEBSOCKET_EVENT_USER_UPDATED = "user_updated" - WEBSOCKET_EVENT_USER_ROLE_UPDATED = "user_role_updated" - WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated" - WEBSOCKET_EVENT_USER_REMOVED = "user_removed" - WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed" - WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed" - WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted" - WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" - WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" - WEBSOCKET_EVENT_HELLO = "hello" - WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" - WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added" - WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed" - WEBSOCKET_EVENT_RESPONSE = "response" - WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added" - WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed" - WEBSOCKET_EVENT_PLUGIN_STATUSES_CHANGED = "plugin_statuses_changed" - WEBSOCKET_EVENT_PLUGIN_ENABLED = "plugin_enabled" - WEBSOCKET_EVENT_PLUGIN_DISABLED = "plugin_disabled" - WEBSOCKET_EVENT_ROLE_UPDATED = "role_updated" - WEBSOCKET_EVENT_LICENSE_CHANGED = "license_changed" - WEBSOCKET_EVENT_CONFIG_CHANGED = "config_changed" - WEBSOCKET_EVENT_OPEN_DIALOG = "open_dialog" - WEBSOCKET_EVENT_GUESTS_DEACTIVATED = "guests_deactivated" - WEBSOCKET_EVENT_USER_ACTIVATION_STATUS_CHANGE = "user_activation_status_change" - WEBSOCKET_EVENT_RECEIVED_GROUP = "received_group" - WEBSOCKET_EVENT_RECEIVED_GROUP_ASSOCIATED_TO_TEAM = "received_group_associated_to_team" - WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_TEAM = "received_group_not_associated_to_team" - WEBSOCKET_EVENT_RECEIVED_GROUP_ASSOCIATED_TO_CHANNEL = "received_group_associated_to_channel" - WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL = "received_group_not_associated_to_channel" - WEBSOCKET_EVENT_SIDEBAR_CATEGORY_CREATED = "sidebar_category_created" - WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED = "sidebar_category_updated" - WEBSOCKET_EVENT_SIDEBAR_CATEGORY_DELETED = "sidebar_category_deleted" - WEBSOCKET_EVENT_SIDEBAR_CATEGORY_ORDER_UPDATED = "sidebar_category_order_updated" - WEBSOCKET_WARN_METRIC_STATUS_RECEIVED = "warn_metric_status_received" - WEBSOCKET_WARN_METRIC_STATUS_REMOVED = "warn_metric_status_removed" - WEBSOCKET_EVENT_CLOUD_PAYMENT_STATUS_UPDATED = "cloud_payment_status_updated" - WEBSOCKET_EVENT_THREAD_UPDATED = "thread_updated" - WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED = "thread_follow_changed" - WEBSOCKET_EVENT_THREAD_READ_CHANGED = "thread_read_changed" - WEBSOCKET_FIRST_ADMIN_VISIT_MARKETPLACE_STATUS_RECEIVED = "first_admin_visit_marketplace_status_received" -) - -type WebSocketMessage interface { - ToJson() string - IsValid() bool - EventType() string -} - -type WebsocketBroadcast struct { - OmitUsers map[string]bool `json:"omit_users"` // broadcast is omitted for users listed here - UserId string `json:"user_id"` // broadcast only occurs for this user - ChannelId string `json:"channel_id"` // broadcast only occurs for users in this channel - TeamId string `json:"team_id"` // broadcast only occurs for users in this team - ContainsSanitizedData bool `json:"-"` - ContainsSensitiveData bool `json:"-"` -} - -type precomputedWebSocketEventJSON struct { - Event json.RawMessage - Data json.RawMessage - Broadcast json.RawMessage -} - -// webSocketEventJSON mirrors WebSocketEvent to make some of its unexported fields serializable -type webSocketEventJSON struct { - Event string `json:"event"` - Data map[string]interface{} `json:"data"` - Broadcast *WebsocketBroadcast `json:"broadcast"` - Sequence int64 `json:"seq"` -} - -// **NOTE**: Direct access to WebSocketEvent fields is deprecated. They will be -// made unexported in next major version release. Provided getter functions should be used instead. -type WebSocketEvent struct { - Event string // Deprecated: use EventType() - Data map[string]interface{} // Deprecated: use GetData() - Broadcast *WebsocketBroadcast // Deprecated: use GetBroadcast() - Sequence int64 // Deprecated: use GetSequence() - precomputedJSON *precomputedWebSocketEventJSON -} - -// PrecomputeJSON precomputes and stores the serialized JSON for all fields other than Sequence. -// This makes ToJson much more efficient when sending the same event to multiple connections. -func (ev *WebSocketEvent) PrecomputeJSON() *WebSocketEvent { - copy := ev.Copy() - event, _ := json.Marshal(copy.Event) - data, _ := json.Marshal(copy.Data) - broadcast, _ := json.Marshal(copy.Broadcast) - copy.precomputedJSON = &precomputedWebSocketEventJSON{ - Event: json.RawMessage(event), - Data: json.RawMessage(data), - Broadcast: json.RawMessage(broadcast), - } - return copy -} - -func (ev *WebSocketEvent) Add(key string, value interface{}) { - ev.Data[key] = value -} - -func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent { - return &WebSocketEvent{Event: event, Data: make(map[string]interface{}), - Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}} -} - -func (ev *WebSocketEvent) Copy() *WebSocketEvent { - copy := &WebSocketEvent{ - Event: ev.Event, - Data: ev.Data, - Broadcast: ev.Broadcast, - Sequence: ev.Sequence, - precomputedJSON: ev.precomputedJSON, - } - return copy -} - -func (ev *WebSocketEvent) GetData() map[string]interface{} { - return ev.Data -} - -func (ev *WebSocketEvent) GetBroadcast() *WebsocketBroadcast { - return ev.Broadcast -} - -func (ev *WebSocketEvent) GetSequence() int64 { - return ev.Sequence -} - -func (ev *WebSocketEvent) SetEvent(event string) *WebSocketEvent { - copy := ev.Copy() - copy.Event = event - return copy -} - -func (ev *WebSocketEvent) SetData(data map[string]interface{}) *WebSocketEvent { - copy := ev.Copy() - copy.Data = data - return copy -} - -func (ev *WebSocketEvent) SetBroadcast(broadcast *WebsocketBroadcast) *WebSocketEvent { - copy := ev.Copy() - copy.Broadcast = broadcast - return copy -} - -func (ev *WebSocketEvent) SetSequence(seq int64) *WebSocketEvent { - copy := ev.Copy() - copy.Sequence = seq - return copy -} - -func (ev *WebSocketEvent) IsValid() bool { - return ev.Event != "" -} - -func (ev *WebSocketEvent) EventType() string { - return ev.Event -} - -func (ev *WebSocketEvent) ToJson() string { - if ev.precomputedJSON != nil { - return fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, ev.precomputedJSON.Event, ev.precomputedJSON.Data, ev.precomputedJSON.Broadcast, ev.Sequence) - } - b, _ := json.Marshal(webSocketEventJSON{ - ev.Event, - ev.Data, - ev.Broadcast, - ev.Sequence, - }) - return string(b) -} - -// Encode encodes the event to the given encoder. -func (ev *WebSocketEvent) Encode(enc *json.Encoder) error { - if ev.precomputedJSON != nil { - return enc.Encode(json.RawMessage( - fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, ev.precomputedJSON.Event, ev.precomputedJSON.Data, ev.precomputedJSON.Broadcast, ev.Sequence), - )) - } - - return enc.Encode(webSocketEventJSON{ - ev.Event, - ev.Data, - ev.Broadcast, - ev.Sequence, - }) -} - -func WebSocketEventFromJson(data io.Reader) *WebSocketEvent { - var ev WebSocketEvent - var o webSocketEventJSON - if err := json.NewDecoder(data).Decode(&o); err != nil { - return nil - } - ev.Event = o.Event - if u, ok := o.Data["user"]; ok { - // We need to convert to and from JSON again - // because the user is in the form of a map[string]interface{}. - buf, err := json.Marshal(u) - if err != nil { - return nil - } - o.Data["user"] = UserFromJson(bytes.NewReader(buf)) - } - ev.Data = o.Data - ev.Broadcast = o.Broadcast - ev.Sequence = o.Sequence - return &ev -} - -// WebSocketResponse represents a response received through the WebSocket -// for a request made to the server. This is available through the ResponseChannel -// channel in WebSocketClient. -type WebSocketResponse struct { - Status string `json:"status"` // The status of the response. For example: OK, FAIL. - SeqReply int64 `json:"seq_reply,omitempty"` // A counter which is incremented for every response sent. - Data map[string]interface{} `json:"data,omitempty"` // The data contained in the response. - Error *AppError `json:"error,omitempty"` // A field that is set if any error has occurred. -} - -func (m *WebSocketResponse) Add(key string, value interface{}) { - m.Data[key] = value -} - -func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse { - return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data} -} - -func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse { - return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err} -} - -func (m *WebSocketResponse) IsValid() bool { - return m.Status != "" -} - -func (m *WebSocketResponse) EventType() string { - return WEBSOCKET_EVENT_RESPONSE -} - -func (m *WebSocketResponse) ToJson() string { - b, _ := json.Marshal(m) - return string(b) -} - -func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse { - var o *WebSocketResponse - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_request.go b/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_request.go deleted file mode 100644 index f18f1285..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/model/websocket_request.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package model - -import ( - "encoding/json" - "io" - - "github.com/mattermost/mattermost-server/v5/shared/i18n" -) - -// WebSocketRequest represents a request made to the server through a websocket. -type WebSocketRequest struct { - // Client-provided fields - Seq int64 `json:"seq"` // A counter which is incremented for every request made. - Action string `json:"action"` // The action to perform for a request. For example: get_statuses, user_typing. - Data map[string]interface{} `json:"data"` // The metadata for an action. - - // Server-provided fields - Session Session `json:"-"` - T i18n.TranslateFunc `json:"-"` - Locale string `json:"-"` -} - -func (o *WebSocketRequest) ToJson() string { - b, _ := json.Marshal(o) - return string(b) -} - -func WebSocketRequestFromJson(data io.Reader) *WebSocketRequest { - var o *WebSocketRequest - json.NewDecoder(data).Decode(&o) - return o -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/default.go b/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/default.go deleted file mode 100644 index 3d835f7d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/default.go +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package timezones - -var DefaultSupportedTimezones = []string{ - "Africa/Abidjan", - "Africa/Accra", - "Africa/Addis_Ababa", - "Africa/Algiers", - "Africa/Asmara", - "Africa/Asmera", - "Africa/Bamako", - "Africa/Bangui", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Blantyre", - "Africa/Brazzaville", - "Africa/Bujumbura", - "Africa/Cairo", - "Africa/Casablanca", - "Africa/Ceuta", - "Africa/Conakry", - "Africa/Dakar", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Douala", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Kigali", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Lome", - "Africa/Luanda", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Malabo", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - "Africa/Mogadishu", - "Africa/Monrovia", - "Africa/Nairobi", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Porto-Novo", - "Africa/Sao_Tome", - "Africa/Timbuktu", - "Africa/Tripoli", - "Africa/Tunis", - "Africa/Windhoek", - "America/Adak", - "America/Anchorage", - "America/Anguilla", - "America/Antigua", - "America/Araguaina", - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/ComodRivadavia", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Aruba", - "America/Asuncion", - "America/Atikokan", - "America/Atka", - "America/Bahia", - "America/Bahia_Banderas", - "America/Barbados", - "America/Belem", - "America/Belize", - "America/Blanc-Sablon", - "America/Boa_Vista", - "America/Bogota", - "America/Boise", - "America/Buenos_Aires", - "America/Cambridge_Bay", - "America/Campo_Grande", - "America/Cancun", - "America/Caracas", - "America/Catamarca", - "America/Cayenne", - "America/Cayman", - "America/Chicago", - "America/Chihuahua", - "America/Coral_Harbour", - "America/Cordoba", - "America/Costa_Rica", - "America/Creston", - "America/Cuiaba", - "America/Curacao", - "America/Danmarkshavn", - "America/Dawson", - "America/Dawson_Creek", - "America/Denver", - "America/Detroit", - "America/Dominica", - "America/Edmonton", - "America/Eirunepe", - "America/El_Salvador", - "America/Ensenada", - "America/Fort_Nelson", - "America/Fort_Wayne", - "America/Fortaleza", - "America/Glace_Bay", - "America/Godthab", - "America/Goose_Bay", - "America/Grand_Turk", - "America/Grenada", - "America/Guadeloupe", - "America/Guatemala", - "America/Guayaquil", - "America/Guyana", - "America/Halifax", - "America/Havana", - "America/Hermosillo", - "America/Indiana/Indianapolis", - "America/Indiana/Knox", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Tell_City", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Indianapolis", - "America/Inuvik", - "America/Iqaluit", - "America/Jamaica", - "America/Jujuy", - "America/Juneau", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Knox_IN", - "America/Kralendijk", - "America/La_Paz", - "America/Lima", - "America/Los_Angeles", - "America/Louisville", - "America/Lower_Princes", - "America/Maceio", - "America/Managua", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Matamoros", - "America/Mazatlan", - "America/Mendoza", - "America/Menominee", - "America/Merida", - "America/Metlakatla", - "America/Mexico_City", - "America/Miquelon", - "America/Moncton", - "America/Monterrey", - "America/Montevideo", - "America/Montreal", - "America/Montserrat", - "America/Nassau", - "America/New_York", - "America/Nipigon", - "America/Nome", - "America/Noronha", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Ojinaga", - "America/Panama", - "America/Pangnirtung", - "America/Paramaribo", - "America/Phoenix", - "America/Port-au-Prince", - "America/Port_of_Spain", - "America/Porto_Acre", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Punta_Arenas", - "America/Rainy_River", - "America/Rankin_Inlet", - "America/Recife", - "America/Regina", - "America/Resolute", - "America/Rio_Branco", - "America/Rosario", - "America/Santa_Isabel", - "America/Santarem", - "America/Santiago", - "America/Santo_Domingo", - "America/Sao_Paulo", - "America/Scoresbysund", - "America/Shiprock", - "America/Sitka", - "America/St_Barthelemy", - "America/St_Johns", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Thule", - "America/Thunder_Bay", - "America/Tijuana", - "America/Toronto", - "America/Tortola", - "America/Vancouver", - "America/Virgin", - "America/Whitehorse", - "America/Winnipeg", - "America/Yakutat", - "America/Yellowknife", - "Antarctica/Casey", - "Antarctica/Davis", - "Antarctica/DumontDUrville", - "Antarctica/Macquarie", - "Antarctica/Mawson", - "Antarctica/McMurdo", - "Antarctica/Palmer", - "Antarctica/Rothera", - "Antarctica/South_Pole", - "Antarctica/Syowa", - "Antarctica/Troll", - "Antarctica/Vostok", - "Arctic/Longyearbyen", - "Asia/Aden", - "Asia/Almaty", - "Asia/Amman", - "Asia/Anadyr", - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Ashkhabad", - "Asia/Atyrau", - "Asia/Baghdad", - "Asia/Bahrain", - "Asia/Baku", - "Asia/Bangkok", - "Asia/Barnaul", - "Asia/Beirut", - "Asia/Bishkek", - "Asia/Brunei", - "Asia/Calcutta", - "Asia/Chita", - "Asia/Choibalsan", - "Asia/Chongqing", - "Asia/Chungking", - "Asia/Colombo", - "Asia/Dacca", - "Asia/Damascus", - "Asia/Dhaka", - "Asia/Dili", - "Asia/Dubai", - "Asia/Dushanbe", - "Asia/Famagusta", - "Asia/Gaza", - "Asia/Harbin", - "Asia/Hebron", - "Asia/Ho_Chi_Minh", - "Asia/Hong_Kong", - "Asia/Hovd", - "Asia/Irkutsk", - "Asia/Istanbul", - "Asia/Jakarta", - "Asia/Jayapura", - "Asia/Jerusalem", - "Asia/Kabul", - "Asia/Kamchatka", - "Asia/Karachi", - "Asia/Kashgar", - "Asia/Kathmandu", - "Asia/Katmandu", - "Asia/Khandyga", - "Asia/Kolkata", - "Asia/Krasnoyarsk", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Asia/Kuwait", - "Asia/Macao", - "Asia/Macau", - "Asia/Magadan", - "Asia/Makassar", - "Asia/Manila", - "Asia/Muscat", - "Asia/Nicosia", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Oral", - "Asia/Phnom_Penh", - "Asia/Pontianak", - "Asia/Pyongyang", - "Asia/Qatar", - "Asia/Qyzylorda", - "Asia/Rangoon", - "Asia/Riyadh", - "Asia/Saigon", - "Asia/Sakhalin", - "Asia/Samarkand", - "Asia/Seoul", - "Asia/Shanghai", - "Asia/Singapore", - "Asia/Srednekolymsk", - "Asia/Taipei", - "Asia/Tashkent", - "Asia/Tbilisi", - "Asia/Tehran", - "Asia/Tel_Aviv", - "Asia/Thimbu", - "Asia/Thimphu", - "Asia/Tokyo", - "Asia/Tomsk", - "Asia/Ujung_Pandang", - "Asia/Ulaanbaatar", - "Asia/Ulan_Bator", - "Asia/Urumqi", - "Asia/Ust-Nera", - "Asia/Vientiane", - "Asia/Vladivostok", - "Asia/Yakutsk", - "Asia/Yangon", - "Asia/Yekaterinburg", - "Asia/Yerevan", - "Atlantic/Azores", - "Atlantic/Bermuda", - "Atlantic/Canary", - "Atlantic/Cape_Verde", - "Atlantic/Faeroe", - "Atlantic/Faroe", - "Atlantic/Jan_Mayen", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/South_Georgia", - "Atlantic/St_Helena", - "Atlantic/Stanley", - "Australia/ACT", - "Australia/Adelaide", - "Australia/Brisbane", - "Australia/Broken_Hill", - "Australia/Canberra", - "Australia/Currie", - "Australia/Darwin", - "Australia/Eucla", - "Australia/Hobart", - "Australia/LHI", - "Australia/Lindeman", - "Australia/Lord_Howe", - "Australia/Melbourne", - "Australia/NSW", - "Australia/North", - "Australia/Perth", - "Australia/Queensland", - "Australia/South", - "Australia/Sydney", - "Australia/Tasmania", - "Australia/Victoria", - "Australia/West", - "Australia/Yancowinna", - "Brazil/Acre", - "Brazil/DeNoronha", - "Brazil/East", - "Brazil/West", - "CET", - "CST6CDT", - "Canada/Atlantic", - "Canada/Central", - "Canada/Eastern", - "Canada/Mountain", - "Canada/Newfoundland", - "Canada/Pacific", - "Canada/Saskatchewan", - "Canada/Yukon", - "Chile/Continental", - "Chile/EasterIsland", - "Cuba", - "EET", - "EST", - "EST5EDT", - "Egypt", - "Eire", - "Etc/GMT", - "Etc/GMT+0", - "Etc/GMT+1", - "Etc/GMT+10", - "Etc/GMT+11", - "Etc/GMT+12", - "Etc/GMT+2", - "Etc/GMT+3", - "Etc/GMT+4", - "Etc/GMT+5", - "Etc/GMT+6", - "Etc/GMT+7", - "Etc/GMT+8", - "Etc/GMT+9", - "Etc/GMT-0", - "Etc/GMT-1", - "Etc/GMT-10", - "Etc/GMT-11", - "Etc/GMT-12", - "Etc/GMT-13", - "Etc/GMT-14", - "Etc/GMT-2", - "Etc/GMT-3", - "Etc/GMT-4", - "Etc/GMT-5", - "Etc/GMT-6", - "Etc/GMT-7", - "Etc/GMT-8", - "Etc/GMT-9", - "Etc/GMT0", - "Etc/Greenwich", - "Etc/UCT", - "Etc/UTC", - "Etc/Universal", - "Etc/Zulu", - "Europe/Amsterdam", - "Europe/Andorra", - "Europe/Astrakhan", - "Europe/Athens", - "Europe/Belfast", - "Europe/Belgrade", - "Europe/Berlin", - "Europe/Bratislava", - "Europe/Brussels", - "Europe/Bucharest", - "Europe/Budapest", - "Europe/Busingen", - "Europe/Chisinau", - "Europe/Copenhagen", - "Europe/Dublin", - "Europe/Gibraltar", - "Europe/Guernsey", - "Europe/Helsinki", - "Europe/Isle_of_Man", - "Europe/Istanbul", - "Europe/Jersey", - "Europe/Kaliningrad", - "Europe/Kiev", - "Europe/Kirov", - "Europe/Lisbon", - "Europe/Ljubljana", - "Europe/London", - "Europe/Luxembourg", - "Europe/Madrid", - "Europe/Malta", - "Europe/Mariehamn", - "Europe/Minsk", - "Europe/Monaco", - "Europe/Moscow", - "Europe/Nicosia", - "Europe/Oslo", - "Europe/Paris", - "Europe/Podgorica", - "Europe/Prague", - "Europe/Riga", - "Europe/Rome", - "Europe/Samara", - "Europe/San_Marino", - "Europe/Sarajevo", - "Europe/Saratov", - "Europe/Simferopol", - "Europe/Skopje", - "Europe/Sofia", - "Europe/Stockholm", - "Europe/Tallinn", - "Europe/Tirane", - "Europe/Tiraspol", - "Europe/Ulyanovsk", - "Europe/Uzhgorod", - "Europe/Vaduz", - "Europe/Vatican", - "Europe/Vienna", - "Europe/Vilnius", - "Europe/Volgograd", - "Europe/Warsaw", - "Europe/Zagreb", - "Europe/Zaporozhye", - "Europe/Zurich", - "GB", - "GB-Eire", - "GMT", - "GMT+0", - "GMT-0", - "GMT0", - "Greenwich", - "HST", - "Hongkong", - "Iceland", - "Indian/Antananarivo", - "Indian/Chagos", - "Indian/Christmas", - "Indian/Cocos", - "Indian/Comoro", - "Indian/Kerguelen", - "Indian/Mahe", - "Indian/Maldives", - "Indian/Mauritius", - "Indian/Mayotte", - "Indian/Reunion", - "Iran", - "Israel", - "Jamaica", - "Japan", - "Kwajalein", - "Libya", - "MET", - "MST", - "MST7MDT", - "Mexico/BajaNorte", - "Mexico/BajaSur", - "Mexico/General", - "NZ", - "NZ-CHAT", - "Navajo", - "PRC", - "PST8PDT", - "Pacific/Apia", - "Pacific/Auckland", - "Pacific/Bougainville", - "Pacific/Chatham", - "Pacific/Chuuk", - "Pacific/Easter", - "Pacific/Efate", - "Pacific/Enderbury", - "Pacific/Fakaofo", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Galapagos", - "Pacific/Gambier", - "Pacific/Guadalcanal", - "Pacific/Guam", - "Pacific/Honolulu", - "Pacific/Johnston", - "Pacific/Kiritimati", - "Pacific/Kosrae", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Marquesas", - "Pacific/Midway", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Norfolk", - "Pacific/Noumea", - "Pacific/Pago_Pago", - "Pacific/Palau", - "Pacific/Pitcairn", - "Pacific/Pohnpei", - "Pacific/Ponape", - "Pacific/Port_Moresby", - "Pacific/Rarotonga", - "Pacific/Saipan", - "Pacific/Samoa", - "Pacific/Tahiti", - "Pacific/Tarawa", - "Pacific/Tongatapu", - "Pacific/Truk", - "Pacific/Wake", - "Pacific/Wallis", - "Pacific/Yap", - "Poland", - "Portugal", - "ROC", - "ROK", - "Singapore", - "Turkey", - "UCT", - "US/Alaska", - "US/Aleutian", - "US/Arizona", - "US/Central", - "US/East-Indiana", - "US/Eastern", - "US/Hawaii", - "US/Indiana-Starke", - "US/Michigan", - "US/Mountain", - "US/Pacific", - "US/Pacific-New", - "US/Samoa", - "UTC", - "Universal", - "W-SU", - "WET", - "Zulu", -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/timezones.go b/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/timezones.go deleted file mode 100644 index e211a4cd..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/services/timezones/timezones.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package timezones - -type Timezones struct { - supportedZones []string -} - -func New() *Timezones { - timezones := Timezones{} - - timezones.supportedZones = DefaultSupportedTimezones - - return &timezones -} - -func (t *Timezones) GetSupported() []string { - return t.supportedZones -} - -func DefaultUserTimezone() map[string]string { - defaultTimezone := make(map[string]string) - defaultTimezone["useAutomaticTimezone"] = "true" - defaultTimezone["automaticTimezone"] = "" - defaultTimezone["manualTimezone"] = "" - - return defaultTimezone -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/filesstore.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/filesstore.go deleted file mode 100644 index ef02895d..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/filesstore.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package filestore - -import ( - "io" - "time" - - "github.com/pkg/errors" -) - -const ( - driverS3 = "amazons3" - driverLocal = "local" -) - -type ReadCloseSeeker interface { - io.ReadCloser - io.Seeker -} - -type FileBackend interface { - TestConnection() error - - Reader(path string) (ReadCloseSeeker, error) - ReadFile(path string) ([]byte, error) - FileExists(path string) (bool, error) - FileSize(path string) (int64, error) - CopyFile(oldPath, newPath string) error - MoveFile(oldPath, newPath string) error - WriteFile(fr io.Reader, path string) (int64, error) - AppendFile(fr io.Reader, path string) (int64, error) - RemoveFile(path string) error - FileModTime(path string) (time.Time, error) - - ListDirectory(path string) ([]string, error) - RemoveDirectory(path string) error -} - -type FileBackendSettings struct { - DriverName string - Directory string - AmazonS3AccessKeyId string - AmazonS3SecretAccessKey string - AmazonS3Bucket string - AmazonS3PathPrefix string - AmazonS3Region string - AmazonS3Endpoint string - AmazonS3SSL bool - AmazonS3SignV2 bool - AmazonS3SSE bool - AmazonS3Trace bool -} - -func (settings *FileBackendSettings) CheckMandatoryS3Fields() error { - if settings.AmazonS3Bucket == "" { - return errors.New("missing s3 bucket settings") - } - - // if S3 endpoint is not set call the set defaults to set that - if settings.AmazonS3Endpoint == "" { - settings.AmazonS3Endpoint = "s3.amazonaws.com" - } - - return nil -} - -func NewFileBackend(settings FileBackendSettings) (FileBackend, error) { - switch settings.DriverName { - case driverS3: - backend, err := NewS3FileBackend(settings) - if err != nil { - return nil, errors.Wrap(err, "unable to connect to the s3 backend") - } - return backend, nil - case driverLocal: - return &LocalFileBackend{ - directory: settings.Directory, - }, nil - } - return nil, errors.New("no valid filestorage driver found") -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/localstore.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/localstore.go deleted file mode 100644 index 6cd8c4ca..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/localstore.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package filestore - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const ( - TestFilePath = "/testfile" -) - -type LocalFileBackend struct { - directory string -} - -// copyFile will copy a file from src path to dst path. -// Overwrites any existing files at dst. -// Permissions are copied from file at src to the new file at dst. -func copyFile(src, dst string) (err error) { - in, err := os.Open(src) - if err != nil { - return - } - defer in.Close() - - if err = os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { - return - } - out, err := os.Create(dst) - if err != nil { - return - } - defer func() { - if e := out.Close(); e != nil { - err = e - } - }() - - _, err = io.Copy(out, in) - if err != nil { - return - } - - err = out.Sync() - if err != nil { - return - } - - stat, err := os.Stat(src) - if err != nil { - return - } - err = os.Chmod(dst, stat.Mode()) - if err != nil { - return - } - - return -} - -func (b *LocalFileBackend) TestConnection() error { - f := bytes.NewReader([]byte("testingwrite")) - if _, err := writeFileLocally(f, filepath.Join(b.directory, TestFilePath)); err != nil { - return errors.Wrap(err, "unable to write to the local filesystem storage") - } - os.Remove(filepath.Join(b.directory, TestFilePath)) - mlog.Debug("Able to write files to local storage.") - return nil -} - -func (b *LocalFileBackend) Reader(path string) (ReadCloseSeeker, error) { - f, err := os.Open(filepath.Join(b.directory, path)) - if err != nil { - return nil, errors.Wrapf(err, "unable to open file %s", path) - } - return f, nil -} - -func (b *LocalFileBackend) ReadFile(path string) ([]byte, error) { - f, err := ioutil.ReadFile(filepath.Join(b.directory, path)) - if err != nil { - return nil, errors.Wrapf(err, "unable to read file %s", path) - } - return f, nil -} - -func (b *LocalFileBackend) FileExists(path string) (bool, error) { - _, err := os.Stat(filepath.Join(b.directory, path)) - - if os.IsNotExist(err) { - return false, nil - } - - if err != nil { - return false, errors.Wrapf(err, "unable to know if file %s exists", path) - } - return true, nil -} - -func (b *LocalFileBackend) FileSize(path string) (int64, error) { - info, err := os.Stat(filepath.Join(b.directory, path)) - if err != nil { - return 0, errors.Wrapf(err, "unable to get file size for %s", path) - } - return info.Size(), nil -} - -func (b *LocalFileBackend) FileModTime(path string) (time.Time, error) { - info, err := os.Stat(filepath.Join(b.directory, path)) - if err != nil { - return time.Time{}, errors.Wrapf(err, "unable to get modification time for file %s", path) - } - return info.ModTime(), nil -} - -func (b *LocalFileBackend) CopyFile(oldPath, newPath string) error { - if err := copyFile(filepath.Join(b.directory, oldPath), filepath.Join(b.directory, newPath)); err != nil { - return errors.Wrapf(err, "unable to copy file from %s to %s", oldPath, newPath) - } - return nil -} - -func (b *LocalFileBackend) MoveFile(oldPath, newPath string) error { - if err := os.MkdirAll(filepath.Dir(filepath.Join(b.directory, newPath)), 0750); err != nil { - return errors.Wrapf(err, "unable to create the new destination directory %s", filepath.Dir(newPath)) - } - - if err := os.Rename(filepath.Join(b.directory, oldPath), filepath.Join(b.directory, newPath)); err != nil { - return errors.Wrapf(err, "unable to move the file to %s to the destination directory", newPath) - } - - return nil -} - -func (b *LocalFileBackend) WriteFile(fr io.Reader, path string) (int64, error) { - return writeFileLocally(fr, filepath.Join(b.directory, path)) -} - -func writeFileLocally(fr io.Reader, path string) (int64, error) { - if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil { - directory, _ := filepath.Abs(filepath.Dir(path)) - return 0, errors.Wrapf(err, "unable to create the directory %s for the file %s", directory, path) - } - fw, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return 0, errors.Wrapf(err, "unable to open the file %s to write the data", path) - } - defer fw.Close() - written, err := io.Copy(fw, fr) - if err != nil { - return written, errors.Wrapf(err, "unable write the data in the file %s", path) - } - return written, nil -} - -func (b *LocalFileBackend) AppendFile(fr io.Reader, path string) (int64, error) { - fp := filepath.Join(b.directory, path) - if _, err := os.Stat(fp); err != nil { - return 0, errors.Wrapf(err, "unable to find the file %s to append the data", path) - } - fw, err := os.OpenFile(fp, os.O_WRONLY|os.O_APPEND, 0600) - if err != nil { - return 0, errors.Wrapf(err, "unable to open the file %s to append the data", path) - } - defer fw.Close() - written, err := io.Copy(fw, fr) - if err != nil { - return written, errors.Wrapf(err, "unable append the data in the file %s", path) - } - return written, nil -} - -func (b *LocalFileBackend) RemoveFile(path string) error { - if err := os.Remove(filepath.Join(b.directory, path)); err != nil { - return errors.Wrapf(err, "unable to remove the file %s", path) - } - return nil -} - -func (b *LocalFileBackend) ListDirectory(path string) ([]string, error) { - var paths []string - fileInfos, err := ioutil.ReadDir(filepath.Join(b.directory, path)) - if err != nil { - if os.IsNotExist(err) { - return paths, nil - } - return nil, errors.Wrapf(err, "unable to list the directory %s", path) - } - for _, fileInfo := range fileInfos { - paths = append(paths, filepath.Join(path, fileInfo.Name())) - } - return paths, nil -} - -func (b *LocalFileBackend) RemoveDirectory(path string) error { - if err := os.RemoveAll(filepath.Join(b.directory, path)); err != nil { - return errors.Wrapf(err, "unable to remove the directory %s", path) - } - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3_overrides.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3_overrides.go deleted file mode 100644 index e7b29b98..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3_overrides.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package filestore - -import ( - "context" - "net/http" - - "github.com/minio/minio-go/v7/pkg/credentials" -) - -// customTransport is used to point the request to a different server. -// This is helpful in situations where a different service is handling AWS S3 requests -// from multiple Mattermost applications, and the Mattermost service itself does not -// have any S3 credentials. -type customTransport struct { - base http.RoundTripper - host string - scheme string - client http.Client -} - -// RoundTrip implements the http.Roundtripper interface. -func (t *customTransport) RoundTrip(req *http.Request) (*http.Response, error) { - // Rountrippers should not modify the original request. - newReq := req.Clone(context.Background()) - *newReq.URL = *req.URL - req.URL.Scheme = t.scheme - req.URL.Host = t.host - return t.client.Do(req) -} - -// customProvider is a dummy credentials provider for the minio client to work -// without actually providing credentials. This is needed with a custom transport -// in cases where the minio client does not actually have credentials with itself, -// rather needs responses from another entity. -// -// It satisfies the credentials.Provider interface. -type customProvider struct { - isSignV2 bool -} - -// Retrieve just returns empty credentials. -func (cp customProvider) Retrieve() (credentials.Value, error) { - sign := credentials.SignatureV4 - if cp.isSignV2 { - sign = credentials.SignatureV2 - } - return credentials.Value{ - SignerType: sign, - }, nil -} - -// IsExpired always returns false. -func (cp customProvider) IsExpired() bool { return false } diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3store.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3store.go deleted file mode 100644 index 5d0bf38e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/filestore/s3store.go +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package filestore - -import ( - "context" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - s3 "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/minio/minio-go/v7/pkg/encrypt" - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -// S3FileBackend contains all necessary information to communicate with -// an AWS S3 compatible API backend. -type S3FileBackend struct { - endpoint string - accessKey string - secretKey string - secure bool - signV2 bool - region string - bucket string - pathPrefix string - encrypt bool - trace bool - client *s3.Client -} - -type S3FileBackendAuthError struct { - DetailedError string -} - -// S3FileBackendNoBucketError is returned when testing a connection and no S3 bucket is found -type S3FileBackendNoBucketError struct{} - -const ( - // This is not exported by minio. See: https://github.com/minio/minio-go/issues/1339 - bucketNotFound = "NoSuchBucket" -) - -var ( - imageExtensions = map[string]bool{".jpg": true, ".jpeg": true, ".gif": true, ".bmp": true, ".png": true, ".tiff": true, "tif": true} - imageMimeTypes = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff", ".tif": "image/tif"} -) - -func isFileExtImage(ext string) bool { - ext = strings.ToLower(ext) - return imageExtensions[ext] -} - -func getImageMimeType(ext string) string { - ext = strings.ToLower(ext) - if imageMimeTypes[ext] == "" { - return "image" - } - return imageMimeTypes[ext] -} - -func (s *S3FileBackendAuthError) Error() string { - return s.DetailedError -} - -func (s *S3FileBackendNoBucketError) Error() string { - return "no such bucket" -} - -// NewS3FileBackend returns an instance of an S3FileBackend. -func NewS3FileBackend(settings FileBackendSettings) (*S3FileBackend, error) { - backend := &S3FileBackend{ - endpoint: settings.AmazonS3Endpoint, - accessKey: settings.AmazonS3AccessKeyId, - secretKey: settings.AmazonS3SecretAccessKey, - secure: settings.AmazonS3SSL, - signV2: settings.AmazonS3SignV2, - region: settings.AmazonS3Region, - bucket: settings.AmazonS3Bucket, - pathPrefix: settings.AmazonS3PathPrefix, - encrypt: settings.AmazonS3SSE, - trace: settings.AmazonS3Trace, - } - cli, err := backend.s3New() - if err != nil { - return nil, err - } - backend.client = cli - return backend, nil -} - -// Similar to s3.New() but allows initialization of signature v2 or signature v4 client. -// If signV2 input is false, function always returns signature v4. -// -// Additionally this function also takes a user defined region, if set -// disables automatic region lookup. -func (b *S3FileBackend) s3New() (*s3.Client, error) { - var creds *credentials.Credentials - - isCloud := os.Getenv("MM_CLOUD_FILESTORE_BIFROST") != "" - if isCloud { - creds = credentials.New(customProvider{isSignV2: b.signV2}) - } else if b.accessKey == "" && b.secretKey == "" { - creds = credentials.NewIAM("") - } else if b.signV2 { - creds = credentials.NewStatic(b.accessKey, b.secretKey, "", credentials.SignatureV2) - } else { - creds = credentials.NewStatic(b.accessKey, b.secretKey, "", credentials.SignatureV4) - } - - opts := s3.Options{ - Creds: creds, - Secure: b.secure, - Region: b.region, - } - - // If this is a cloud installation, we override the default transport. - if isCloud { - tr, err := s3.DefaultTransport(b.secure) - if err != nil { - return nil, err - } - scheme := "http" - if b.secure { - scheme = "https" - } - opts.Transport = &customTransport{ - base: tr, - host: b.endpoint, - scheme: scheme, - } - } - - s3Clnt, err := s3.New(b.endpoint, &opts) - if err != nil { - return nil, err - } - - if b.trace { - s3Clnt.TraceOn(os.Stdout) - } - - return s3Clnt, nil -} - -func (b *S3FileBackend) TestConnection() error { - exists := true - var err error - // If a path prefix is present, we attempt to test the bucket by listing objects under the path - // and just checking the first response. This is because the BucketExists call is only at a bucket level - // and sometimes the user might only be allowed access to the specified path prefix. - if b.pathPrefix != "" { - obj := <-b.client.ListObjects(context.Background(), b.bucket, s3.ListObjectsOptions{Prefix: b.pathPrefix}) - if obj.Err != nil { - typedErr := s3.ToErrorResponse(obj.Err) - if typedErr.Code != bucketNotFound { - return &S3FileBackendAuthError{DetailedError: "unable to list objects in the S3 bucket"} - } - exists = false - } - } else { - exists, err = b.client.BucketExists(context.Background(), b.bucket) - if err != nil { - return &S3FileBackendAuthError{DetailedError: "unable to check if the S3 bucket exists"} - } - } - - if !exists { - return &S3FileBackendNoBucketError{} - } - mlog.Debug("Connection to S3 or minio is good. Bucket exists.") - return nil -} - -func (b *S3FileBackend) MakeBucket() error { - err := b.client.MakeBucket(context.Background(), b.bucket, s3.MakeBucketOptions{Region: b.region}) - if err != nil { - return errors.Wrap(err, "unable to create the s3 bucket") - } - return nil -} - -// Caller must close the first return value -func (b *S3FileBackend) Reader(path string) (ReadCloseSeeker, error) { - path = filepath.Join(b.pathPrefix, path) - minioObject, err := b.client.GetObject(context.Background(), b.bucket, path, s3.GetObjectOptions{}) - if err != nil { - return nil, errors.Wrapf(err, "unable to open file %s", path) - } - - return minioObject, nil -} - -func (b *S3FileBackend) ReadFile(path string) ([]byte, error) { - path = filepath.Join(b.pathPrefix, path) - minioObject, err := b.client.GetObject(context.Background(), b.bucket, path, s3.GetObjectOptions{}) - if err != nil { - return nil, errors.Wrapf(err, "unable to open file %s", path) - } - - defer minioObject.Close() - f, err := ioutil.ReadAll(minioObject) - if err != nil { - return nil, errors.Wrapf(err, "unable to read file %s", path) - } - return f, nil -} - -func (b *S3FileBackend) FileExists(path string) (bool, error) { - path = filepath.Join(b.pathPrefix, path) - - _, err := b.client.StatObject(context.Background(), b.bucket, path, s3.StatObjectOptions{}) - if err == nil { - return true, nil - } - - var s3Err s3.ErrorResponse - if errors.As(err, &s3Err); s3Err.Code == "NoSuchKey" { - return false, nil - } - - return false, errors.Wrapf(err, "unable to know if file %s exists", path) -} - -func (b *S3FileBackend) FileSize(path string) (int64, error) { - path = filepath.Join(b.pathPrefix, path) - - info, err := b.client.StatObject(context.Background(), b.bucket, path, s3.StatObjectOptions{}) - if err != nil { - return 0, errors.Wrapf(err, "unable to get file size for %s", path) - } - - return info.Size, nil -} - -func (b *S3FileBackend) FileModTime(path string) (time.Time, error) { - path = filepath.Join(b.pathPrefix, path) - - info, err := b.client.StatObject(context.Background(), b.bucket, path, s3.StatObjectOptions{}) - if err != nil { - return time.Time{}, errors.Wrapf(err, "unable to get modification time for file %s", path) - } - - return info.LastModified, nil -} - -func (b *S3FileBackend) CopyFile(oldPath, newPath string) error { - oldPath = filepath.Join(b.pathPrefix, oldPath) - newPath = filepath.Join(b.pathPrefix, newPath) - srcOpts := s3.CopySrcOptions{ - Bucket: b.bucket, - Object: oldPath, - Encryption: encrypt.NewSSE(), - } - dstOpts := s3.CopyDestOptions{ - Bucket: b.bucket, - Object: newPath, - Encryption: encrypt.NewSSE(), - } - if _, err := b.client.CopyObject(context.Background(), dstOpts, srcOpts); err != nil { - return errors.Wrapf(err, "unable to copy file from %s to %s", oldPath, newPath) - } - return nil -} - -func (b *S3FileBackend) MoveFile(oldPath, newPath string) error { - oldPath = filepath.Join(b.pathPrefix, oldPath) - newPath = filepath.Join(b.pathPrefix, newPath) - srcOpts := s3.CopySrcOptions{ - Bucket: b.bucket, - Object: oldPath, - Encryption: encrypt.NewSSE(), - } - dstOpts := s3.CopyDestOptions{ - Bucket: b.bucket, - Object: newPath, - Encryption: encrypt.NewSSE(), - } - - if _, err := b.client.CopyObject(context.Background(), dstOpts, srcOpts); err != nil { - return errors.Wrapf(err, "unable to copy the file to %s to the new destionation", newPath) - } - - if err := b.client.RemoveObject(context.Background(), b.bucket, oldPath, s3.RemoveObjectOptions{}); err != nil { - return errors.Wrapf(err, "unable to remove the file old file %s", oldPath) - } - - return nil -} - -func (b *S3FileBackend) WriteFile(fr io.Reader, path string) (int64, error) { - var contentType string - path = filepath.Join(b.pathPrefix, path) - if ext := filepath.Ext(path); isFileExtImage(ext) { - contentType = getImageMimeType(ext) - } else { - contentType = "binary/octet-stream" - } - - options := s3PutOptions(b.encrypt, contentType) - info, err := b.client.PutObject(context.Background(), b.bucket, path, fr, -1, options) - if err != nil { - return info.Size, errors.Wrapf(err, "unable write the data in the file %s", path) - } - - return info.Size, nil -} - -func (b *S3FileBackend) AppendFile(fr io.Reader, path string) (int64, error) { - fp := filepath.Join(b.pathPrefix, path) - if _, err := b.client.StatObject(context.Background(), b.bucket, fp, s3.StatObjectOptions{}); err != nil { - return 0, errors.Wrapf(err, "unable to find the file %s to append the data", path) - } - - var contentType string - if ext := filepath.Ext(fp); isFileExtImage(ext) { - contentType = getImageMimeType(ext) - } else { - contentType = "binary/octet-stream" - } - - options := s3PutOptions(b.encrypt, contentType) - sse := options.ServerSideEncryption - partName := fp + ".part" - info, err := b.client.PutObject(context.Background(), b.bucket, partName, fr, -1, options) - defer b.client.RemoveObject(context.Background(), b.bucket, partName, s3.RemoveObjectOptions{}) - if info.Size > 0 { - src1Opts := s3.CopySrcOptions{ - Bucket: b.bucket, - Object: fp, - } - src2Opts := s3.CopySrcOptions{ - Bucket: b.bucket, - Object: partName, - } - dstOpts := s3.CopyDestOptions{ - Bucket: b.bucket, - Object: fp, - Encryption: sse, - } - _, err = b.client.ComposeObject(context.Background(), dstOpts, src1Opts, src2Opts) - if err != nil { - return 0, errors.Wrapf(err, "unable append the data in the file %s", path) - } - return info.Size, nil - } - - return 0, errors.Wrapf(err, "unable append the data in the file %s", path) -} - -func (b *S3FileBackend) RemoveFile(path string) error { - path = filepath.Join(b.pathPrefix, path) - if err := b.client.RemoveObject(context.Background(), b.bucket, path, s3.RemoveObjectOptions{}); err != nil { - return errors.Wrapf(err, "unable to remove the file %s", path) - } - - return nil -} - -func getPathsFromObjectInfos(in <-chan s3.ObjectInfo) <-chan s3.ObjectInfo { - out := make(chan s3.ObjectInfo, 1) - - go func() { - defer close(out) - - for { - info, done := <-in - - if !done { - break - } - - out <- info - } - }() - - return out -} - -func (b *S3FileBackend) ListDirectory(path string) ([]string, error) { - path = filepath.Join(b.pathPrefix, path) - if !strings.HasSuffix(path, "/") && path != "" { - // s3Clnt returns only the path itself when "/" is not present - // appending "/" to make it consistent across all filestores - path = path + "/" - } - - opts := s3.ListObjectsOptions{ - Prefix: path, - } - var paths []string - for object := range b.client.ListObjects(context.Background(), b.bucket, opts) { - if object.Err != nil { - return nil, errors.Wrapf(object.Err, "unable to list the directory %s", path) - } - // We strip the path prefix that gets applied, - // so that it remains transparent to the application. - object.Key = strings.TrimPrefix(object.Key, b.pathPrefix) - trimmed := strings.Trim(object.Key, "/") - if trimmed != "" { - paths = append(paths, trimmed) - } - } - - return paths, nil -} - -func (b *S3FileBackend) RemoveDirectory(path string) error { - opts := s3.ListObjectsOptions{ - Prefix: filepath.Join(b.pathPrefix, path), - Recursive: true, - } - list := b.client.ListObjects(context.Background(), b.bucket, opts) - objectsCh := b.client.RemoveObjects(context.Background(), b.bucket, getPathsFromObjectInfos(list), s3.RemoveObjectsOptions{}) - for err := range objectsCh { - if err.Err != nil { - return errors.Wrapf(err.Err, "unable to remove the directory %s", path) - } - } - - return nil -} - -func s3PutOptions(encrypted bool, contentType string) s3.PutObjectOptions { - options := s3.PutObjectOptions{} - if encrypted { - options.ServerSideEncryption = encrypt.NewSSE() - } - options.ContentType = contentType - // We set the part size to the minimum allowed value of 5MBs - // to avoid an excessive allocation in minio.PutObject implementation. - options.PartSize = 1024 * 1024 * 5 - - return options -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/i18n/i18n.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/i18n/i18n.go deleted file mode 100644 index a5de30bb..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/i18n/i18n.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package i18n - -import ( - "fmt" - "html/template" - "io/ioutil" - "net/http" - "path/filepath" - "reflect" - "strings" - - "github.com/mattermost/go-i18n/i18n" - - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const defaultLocale = "en" - -// TranslateFunc is the type of the translate functions -type TranslateFunc func(translationID string, args ...interface{}) string - -// T is the translate function using the default server language as fallback language -var T TranslateFunc - -// TDefault is the translate function using english as fallback language -var TDefault TranslateFunc - -var locales map[string]string = make(map[string]string) -var defaultServerLocale string -var defaultClientLocale string - -// TranslationsPreInit loads translations from filesystem if they are not -// loaded already and assigns english while loading server config -func TranslationsPreInit(translationsDir string) error { - if T != nil { - return nil - } - - // Set T even if we fail to load the translations. Lots of shutdown handling code will - // segfault trying to handle the error, and the untranslated IDs are strictly better. - T = tfuncWithFallback(defaultLocale) - TDefault = tfuncWithFallback(defaultLocale) - - return initTranslationsWithDir(translationsDir) -} - -// InitTranslations set the defaults configured in the server and initialize -// the T function using the server default as fallback language -func InitTranslations(serverLocale, clientLocale string) error { - defaultServerLocale = serverLocale - defaultClientLocale = clientLocale - - var err error - T, err = getTranslationsBySystemLocale() - return err -} - -func initTranslationsWithDir(dir string) error { - files, _ := ioutil.ReadDir(dir) - for _, f := range files { - if filepath.Ext(f.Name()) == ".json" { - filename := f.Name() - locales[strings.Split(filename, ".")[0]] = filepath.Join(dir, filename) - - if err := i18n.LoadTranslationFile(filepath.Join(dir, filename)); err != nil { - return err - } - } - } - - return nil -} - -func getTranslationsBySystemLocale() (TranslateFunc, error) { - locale := defaultServerLocale - if _, ok := locales[locale]; !ok { - mlog.Warn("Failed to load system translations for", mlog.String("locale", locale), mlog.String("attempting to fall back to default locale", defaultLocale)) - locale = defaultLocale - } - - if locales[locale] == "" { - return nil, fmt.Errorf("failed to load system translations for '%v'", defaultLocale) - } - - translations := tfuncWithFallback(locale) - if translations == nil { - return nil, fmt.Errorf("failed to load system translations") - } - - mlog.Info("Loaded system translations", mlog.String("for locale", locale), mlog.String("from locale", locales[locale])) - return translations, nil -} - -// GetUserTranslations get the translation function for an specific locale -func GetUserTranslations(locale string) TranslateFunc { - if _, ok := locales[locale]; !ok { - locale = defaultLocale - } - - translations := tfuncWithFallback(locale) - return translations -} - -// GetTranslationsAndLocaleFromRequest return the translation function and the -// locale based on a request headers -func GetTranslationsAndLocaleFromRequest(r *http.Request) (TranslateFunc, string) { - // This is for checking against locales like pt_BR or zn_CN - headerLocaleFull := strings.Split(r.Header.Get("Accept-Language"), ",")[0] - // This is for checking against locales like en, es - headerLocale := strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0] - defaultLocale := defaultClientLocale - if locales[headerLocaleFull] != "" { - translations := tfuncWithFallback(headerLocaleFull) - return translations, headerLocaleFull - } else if locales[headerLocale] != "" { - translations := tfuncWithFallback(headerLocale) - return translations, headerLocale - } else if locales[defaultLocale] != "" { - translations := tfuncWithFallback(defaultLocale) - return translations, headerLocale - } - - translations := tfuncWithFallback(defaultLocale) - return translations, defaultLocale -} - -// GetSupportedLocales return a map of locale code and the file path with the -// translations -func GetSupportedLocales() map[string]string { - return locales -} - -func tfuncWithFallback(pref string) TranslateFunc { - t, _ := i18n.Tfunc(pref) - return func(translationID string, args ...interface{}) string { - if translated := t(translationID, args...); translated != translationID { - return translated - } - - t, _ := i18n.Tfunc(defaultLocale) - return t(translationID, args...) - } -} - -// TranslateAsHTML translates the translationID provided and return a -// template.HTML object -func TranslateAsHTML(t TranslateFunc, translationID string, args map[string]interface{}) template.HTML { - message := t(translationID, escapeForHTML(args)) - message = strings.Replace(message, "[[", "<strong>", -1) - message = strings.Replace(message, "]]", "</strong>", -1) - return template.HTML(message) -} - -func escapeForHTML(arg interface{}) interface{} { - switch typedArg := arg.(type) { - case string: - return template.HTMLEscapeString(typedArg) - case *string: - return template.HTMLEscapeString(*typedArg) - case map[string]interface{}: - safeArg := make(map[string]interface{}, len(typedArg)) - for key, value := range typedArg { - safeArg[key] = escapeForHTML(value) - } - return safeArg - default: - mlog.Warn( - "Unable to escape value for HTML template", - mlog.Any("html_template", arg), - mlog.String("template_type", reflect.ValueOf(arg).Type().String()), - ) - return "" - } -} - -// IdentityTfunc returns a translation function that don't translate, only -// returns the same id -func IdentityTfunc() TranslateFunc { - return func(translationID string, args ...interface{}) string { - return translationID - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/autolink.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/autolink.go deleted file mode 100644 index 2eb05d90..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/autolink.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "regexp" - "strings" - "unicode" - "unicode/utf8" -) - -// Based off of extensions/autolink.c from https://github.com/github/cmark - -var ( - DefaultURLSchemes = []string{"http", "https", "ftp", "mailto", "tel"} - wwwAutoLinkRegex = regexp.MustCompile(`^www\d{0,3}\.`) -) - -// Given a string with a w at the given position, tries to parse and return a range containing a www link. -// if one exists. If the text at the given position isn't a link, returns an empty string. Equivalent to -// www_match from the reference code. -func parseWWWAutolink(data string, position int) (Range, bool) { - // Check that this isn't part of another word - if position > 1 { - prevChar := data[position-1] - - if !isWhitespaceByte(prevChar) && !isAllowedBeforeWWWLink(prevChar) { - return Range{}, false - } - } - - // Check that this starts with www - if len(data)-position < 4 || !wwwAutoLinkRegex.MatchString(data[position:]) { - return Range{}, false - } - - end := checkDomain(data[position:], false) - if end == 0 { - return Range{}, false - } - - end += position - - // Grab all text until the end of the string or the next whitespace character - for end < len(data) && !isWhitespaceByte(data[end]) { - end += 1 - } - - // Trim trailing punctuation - end = trimTrailingCharactersFromLink(data, position, end) - if position == end { - return Range{}, false - } - - return Range{position, end}, true -} - -func isAllowedBeforeWWWLink(c byte) bool { - switch c { - case '*', '_', '~', ')': - return true - } - return false -} - -// Given a string with a : at the given position, tried to parse and return a range containing a URL scheme -// if one exists. If the text around the given position isn't a link, returns an empty string. Equivalent to -// url_match from the reference code. -func parseURLAutolink(data string, position int) (Range, bool) { - // Check that a :// exists. This doesn't match the clients that treat the slashes as optional. - if len(data)-position < 4 || data[position+1] != '/' || data[position+2] != '/' { - return Range{}, false - } - - start := position - 1 - for start > 0 && isAlphanumericByte(data[start-1]) { - start -= 1 - } - - if start < 0 || position >= len(data) { - return Range{}, false - } - - // Ensure that the URL scheme is allowed and that at least one character after the scheme is valid. - scheme := data[start:position] - if !isSchemeAllowed(scheme) || !isValidHostCharacter(data[position+3:]) { - return Range{}, false - } - - end := checkDomain(data[position+3:], true) - if end == 0 { - return Range{}, false - } - - end += position - - // Grab all text until the end of the string or the next whitespace character - for end < len(data) && !isWhitespaceByte(data[end]) { - end += 1 - } - - // Trim trailing punctuation - end = trimTrailingCharactersFromLink(data, start, end) - if start == end { - return Range{}, false - } - - return Range{start, end}, true -} - -func isSchemeAllowed(scheme string) bool { - // Note that this doesn't support the custom URL schemes implemented by the client - for _, allowed := range DefaultURLSchemes { - if strings.EqualFold(allowed, scheme) { - return true - } - } - - return false -} - -// Given a string starting with a URL, returns the number of valid characters that make up the URL's domain. -// Returns 0 if the string doesn't start with a domain name. allowShort determines whether or not the domain -// needs to contain a period to be considered valid. Equivalent to check_domain from the reference code. -func checkDomain(data string, allowShort bool) int { - foundUnderscore := false - foundPeriod := false - - i := 1 - for ; i < len(data)-1; i++ { - if data[i] == '_' { - foundUnderscore = true - break - } else if data[i] == '.' { - foundPeriod = true - } else if !isValidHostCharacter(data[i:]) && data[i] != '-' { - break - } - } - - if foundUnderscore { - return 0 - } - - if allowShort { - // If allowShort is set, accept any string of valid domain characters - return i - } - - // If allowShort isn't set, a valid domain just requires at least a single period. Note that this - // logic isn't entirely necessary because we already know the string starts with "www." when - // this is called from parseWWWAutolink - if foundPeriod { - return i - } - return 0 -} - -// Returns true if the provided link starts with a valid character for a domain name. Equivalent to -// is_valid_hostchar from the reference code. -func isValidHostCharacter(link string) bool { - c, _ := utf8.DecodeRuneInString(link) - if c == utf8.RuneError { - return false - } - - return !unicode.IsSpace(c) && !unicode.IsPunct(c) -} - -// Removes any trailing characters such as punctuation or stray brackets that shouldn't be part of the link. -// Returns a new end position for the link. Equivalent to autolink_delim from the reference code. -func trimTrailingCharactersFromLink(markdown string, start int, end int) int { - runes := []rune(markdown[start:end]) - linkEnd := len(runes) - - // Cut off the link before an open angle bracket if it contains one - for i, c := range runes { - if c == '<' { - linkEnd = i - break - } - } - - for linkEnd > 0 { - c := runes[linkEnd-1] - - if !canEndAutolink(c) { - // Trim trailing quotes, periods, etc - linkEnd = linkEnd - 1 - } else if c == ';' { - // Trim a trailing HTML entity - newEnd := linkEnd - 2 - - for newEnd > 0 && ((runes[newEnd] >= 'a' && runes[newEnd] <= 'z') || (runes[newEnd] >= 'A' && runes[newEnd] <= 'Z')) { - newEnd -= 1 - } - - if newEnd < linkEnd-2 && runes[newEnd] == '&' { - linkEnd = newEnd - } else { - // This isn't actually an HTML entity, so just trim the semicolon - linkEnd = linkEnd - 1 - } - } else if c == ')' { - // Only allow an autolink ending with a bracket if that bracket is part of a matching pair of brackets. - // If there are more closing brackets than opening ones, remove the extra bracket - - numClosing := 0 - numOpening := 0 - - // Examples (input text => output linked portion): - // - // http://www.pokemon.com/Pikachu_(Electric) - // => http://www.pokemon.com/Pikachu_(Electric) - // - // http://www.pokemon.com/Pikachu_((Electric) - // => http://www.pokemon.com/Pikachu_((Electric) - // - // http://www.pokemon.com/Pikachu_(Electric)) - // => http://www.pokemon.com/Pikachu_(Electric) - // - // http://www.pokemon.com/Pikachu_((Electric)) - // => http://www.pokemon.com/Pikachu_((Electric)) - - for i := 0; i < linkEnd; i++ { - if runes[i] == '(' { - numOpening += 1 - } else if runes[i] == ')' { - numClosing += 1 - } - } - - if numClosing <= numOpening { - // There's fewer or equal closing brackets, so we've found the end of the link - break - } - - linkEnd -= 1 - } else { - // There's no special characters at the end of the link, so we're at the end - break - } - } - - return start + len(string(runes[:linkEnd])) -} - -func canEndAutolink(c rune) bool { - switch c { - case '?', '!', '.', ',', ':', '*', '_', '~', '\'', '"': - return false - } - return true -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/block_quote.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/block_quote.go deleted file mode 100644 index 5cf66d10..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/block_quote.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -type BlockQuote struct { - blockBase - markdown string - - Children []Block -} - -func (b *BlockQuote) Continuation(indentation int, r Range) *continuation { - if indentation > 3 { - return nil - } - s := b.markdown[r.Position:r.End] - if s == "" || s[0] != '>' { - return nil - } - remaining := Range{r.Position + 1, r.End} - indentation, indentationBytes := countIndentation(b.markdown, remaining) - if indentation > 0 { - indentation-- - } - return &continuation{ - Indentation: indentation, - Remaining: Range{remaining.Position + indentationBytes, remaining.End}, - } -} - -func (b *BlockQuote) AddChild(openBlocks []Block) []Block { - b.Children = append(b.Children, openBlocks[0]) - return openBlocks -} - -func blockQuoteStart(markdown string, indent int, r Range) []Block { - if indent > 3 { - return nil - } - s := markdown[r.Position:r.End] - if s == "" || s[0] != '>' { - return nil - } - - block := &BlockQuote{ - markdown: markdown, - } - r.Position++ - if len(s) > 1 && s[1] == ' ' { - r.Position++ - } - - indent, bytes := countIndentation(markdown, r) - - ret := []Block{block} - if descendants := blockStartOrParagraph(markdown, indent, Range{r.Position + bytes, r.End}, nil, nil); descendants != nil { - block.Children = append(block.Children, descendants[0]) - ret = append(ret, descendants...) - } - return ret -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/blocks.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/blocks.go deleted file mode 100644 index fe9e272f..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/blocks.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type continuation struct { - Indentation int - Remaining Range -} - -type Block interface { - Continuation(indentation int, r Range) *continuation - AddLine(indentation int, r Range) bool - Close() - AllowsBlockStarts() bool - HasTrailingBlankLine() bool -} - -type blockBase struct{} - -func (*blockBase) AddLine(indentation int, r Range) bool { return false } -func (*blockBase) Close() {} -func (*blockBase) AllowsBlockStarts() bool { return true } -func (*blockBase) HasTrailingBlankLine() bool { return false } - -type ContainerBlock interface { - Block - AddChild(openBlocks []Block) []Block -} - -type Range struct { - Position int - End int -} - -func closeBlocks(blocks []Block, referenceDefinitions []*ReferenceDefinition) []*ReferenceDefinition { - for _, block := range blocks { - block.Close() - if p, ok := block.(*Paragraph); ok && len(p.ReferenceDefinitions) > 0 { - referenceDefinitions = append(referenceDefinitions, p.ReferenceDefinitions...) - } - } - return referenceDefinitions -} - -func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefinition) { - document := &Document{} - var referenceDefinitions []*ReferenceDefinition - - openBlocks := []Block{document} - - for _, line := range lines { - r := line.Range - lastMatchIndex := 0 - - indentation, indentationBytes := countIndentation(markdown, r) - r = Range{r.Position + indentationBytes, r.End} - - for i, block := range openBlocks { - if continuation := block.Continuation(indentation, r); continuation != nil { - indentation = continuation.Indentation - r = continuation.Remaining - additionalIndentation, additionalIndentationBytes := countIndentation(markdown, r) - r = Range{r.Position + additionalIndentationBytes, r.End} - indentation += additionalIndentation - lastMatchIndex = i - } else { - break - } - } - - if openBlocks[lastMatchIndex].AllowsBlockStarts() { - if newBlocks := blockStart(markdown, indentation, r, openBlocks[:lastMatchIndex+1], openBlocks[lastMatchIndex+1:]); newBlocks != nil { - didAdd := false - for i := lastMatchIndex; i >= 0; i-- { - if container, ok := openBlocks[i].(ContainerBlock); ok { - if addedBlocks := container.AddChild(newBlocks); addedBlocks != nil { - referenceDefinitions = closeBlocks(openBlocks[i+1:], referenceDefinitions) - openBlocks = openBlocks[:i+1] - openBlocks = append(openBlocks, addedBlocks...) - didAdd = true - break - } - } - } - if didAdd { - continue - } - } - } - - isBlank := strings.TrimSpace(markdown[r.Position:r.End]) == "" - if paragraph, ok := openBlocks[len(openBlocks)-1].(*Paragraph); ok && !isBlank { - paragraph.Text = append(paragraph.Text, r) - continue - } - - referenceDefinitions = closeBlocks(openBlocks[lastMatchIndex+1:], referenceDefinitions) - openBlocks = openBlocks[:lastMatchIndex+1] - - if openBlocks[lastMatchIndex].AddLine(indentation, r) { - continue - } - - if paragraph := newParagraph(markdown, r); paragraph != nil { - for i := lastMatchIndex; i >= 0; i-- { - if container, ok := openBlocks[i].(ContainerBlock); ok { - if newBlocks := container.AddChild([]Block{paragraph}); newBlocks != nil { - referenceDefinitions = closeBlocks(openBlocks[i+1:], referenceDefinitions) - openBlocks = openBlocks[:i+1] - openBlocks = append(openBlocks, newBlocks...) - break - } - } - } - } - } - - referenceDefinitions = closeBlocks(openBlocks, referenceDefinitions) - - return document, referenceDefinitions -} - -func blockStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { - if r.Position >= r.End { - return nil - } - - if start := blockQuoteStart(markdown, indentation, r); start != nil { - return start - } else if start := listStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { - return start - } else if start := indentedCodeStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { - return start - } else if start := fencedCodeStart(markdown, indentation, r); start != nil { - return start - } - - return nil -} - -func blockStartOrParagraph(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { - if start := blockStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil { - return start - } - if paragraph := newParagraph(markdown, r); paragraph != nil { - return []Block{paragraph} - } - return nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/document.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/document.go deleted file mode 100644 index 306b93da..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/document.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -type Document struct { - blockBase - - Children []Block -} - -func (b *Document) Continuation(indentation int, r Range) *continuation { - return &continuation{ - Indentation: indentation, - Remaining: r, - } -} - -func (b *Document) AddChild(openBlocks []Block) []Block { - b.Children = append(b.Children, openBlocks[0]) - return openBlocks -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/fenced_code.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/fenced_code.go deleted file mode 100644 index c8caad55..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/fenced_code.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type FencedCodeLine struct { - Indentation int - Range Range -} - -type FencedCode struct { - blockBase - markdown string - didSeeClosingFence bool - - Indentation int - OpeningFence Range - RawInfo Range - RawCode []FencedCodeLine -} - -func (b *FencedCode) Code() (result string) { - for _, code := range b.RawCode { - result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End] - } - return -} - -func (b *FencedCode) Info() string { - return Unescape(b.markdown[b.RawInfo.Position:b.RawInfo.End]) -} - -func (b *FencedCode) Continuation(indentation int, r Range) *continuation { - if b.didSeeClosingFence { - return nil - } - return &continuation{ - Indentation: indentation, - Remaining: r, - } -} - -func (b *FencedCode) AddLine(indentation int, r Range) bool { - s := b.markdown[r.Position:r.End] - if indentation <= 3 && strings.HasPrefix(s, b.markdown[b.OpeningFence.Position:b.OpeningFence.End]) { - suffix := strings.TrimSpace(s[b.OpeningFence.End-b.OpeningFence.Position:]) - isClosingFence := true - for _, c := range suffix { - if c != rune(s[0]) { - isClosingFence = false - break - } - } - if isClosingFence { - b.didSeeClosingFence = true - return true - } - } - - if indentation >= b.Indentation { - indentation -= b.Indentation - } else { - indentation = 0 - } - - b.RawCode = append(b.RawCode, FencedCodeLine{ - Indentation: indentation, - Range: r, - }) - return true -} - -func (b *FencedCode) AllowsBlockStarts() bool { - return false -} - -func fencedCodeStart(markdown string, indentation int, r Range) []Block { - s := markdown[r.Position:r.End] - - if !strings.HasPrefix(s, "```") && !strings.HasPrefix(s, "~~~") { - return nil - } - - fenceCharacter := rune(s[0]) - fenceLength := 3 - for _, c := range s[3:] { - if c == fenceCharacter { - fenceLength++ - } else { - break - } - } - - for i := r.Position + fenceLength; i < r.End; i++ { - if markdown[i] == '`' { - return nil - } - } - - return []Block{ - &FencedCode{ - markdown: markdown, - Indentation: indentation, - RawInfo: trimRightSpace(markdown, Range{r.Position + fenceLength, r.End}), - OpeningFence: Range{r.Position, r.Position + fenceLength}, - }, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html.go deleted file mode 100644 index 52583074..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "fmt" - "strings" -) - -var htmlEscaper = strings.NewReplacer( - `&`, "&", - `<`, "<", - `>`, ">", - `"`, """, -) - -// RenderHTML produces HTML with the same behavior as the example renderer used in the CommonMark -// reference materials except for one slight difference: for brevity, no unnecessary whitespace is -// inserted between elements. The output is not defined by the CommonMark spec, and it exists -// primarily as an aid in testing. -func RenderHTML(markdown string) string { - return RenderBlockHTML(Parse(markdown)) -} - -func RenderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition) (result string) { - return renderBlockHTML(block, referenceDefinitions, false) -} - -func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) (result string) { - switch v := block.(type) { - case *Document: - for _, block := range v.Children { - result += RenderBlockHTML(block, referenceDefinitions) - } - case *Paragraph: - if len(v.Text) == 0 { - return - } - if !isTightList { - result += "<p>" - } - for _, inline := range v.ParseInlines(referenceDefinitions) { - result += RenderInlineHTML(inline) - } - if !isTightList { - result += "</p>" - } - case *List: - if v.IsOrdered { - if v.OrderedStart != 1 { - result += fmt.Sprintf(`<ol start="%v">`, v.OrderedStart) - } else { - result += "<ol>" - } - } else { - result += "<ul>" - } - for _, block := range v.Children { - result += renderBlockHTML(block, referenceDefinitions, !v.IsLoose) - } - if v.IsOrdered { - result += "</ol>" - } else { - result += "</ul>" - } - case *ListItem: - result += "<li>" - for _, block := range v.Children { - result += renderBlockHTML(block, referenceDefinitions, isTightList) - } - result += "</li>" - case *BlockQuote: - result += "<blockquote>" - for _, block := range v.Children { - result += RenderBlockHTML(block, referenceDefinitions) - } - result += "</blockquote>" - case *FencedCode: - if info := v.Info(); info != "" { - language := strings.Fields(info)[0] - result += `<pre><code class="language-` + htmlEscaper.Replace(language) + `">` - } else { - result += "<pre><code>" - } - result += htmlEscaper.Replace(v.Code()) + "</code></pre>" - case *IndentedCode: - result += "<pre><code>" + htmlEscaper.Replace(v.Code()) + "</code></pre>" - default: - panic(fmt.Sprintf("missing case for type %T", v)) - } - return -} - -func escapeURL(url string) (result string) { - for i := 0; i < len(url); { - switch b := url[i]; b { - case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '-', '_', '.', '!', '~', '*', '\'', '(', ')', '#': - result += string(b) - i++ - default: - if b == '%' && i+2 < len(url) && isHexByte(url[i+1]) && isHexByte(url[i+2]) { - result += url[i : i+3] - i += 3 - } else if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') { - result += string(b) - i++ - } else { - result += fmt.Sprintf("%%%0X", b) - i++ - } - } - } - return -} - -func RenderInlineHTML(inline Inline) (result string) { - switch v := inline.(type) { - case *Text: - return htmlEscaper.Replace(v.Text) - case *HardLineBreak: - return "<br />" - case *SoftLineBreak: - return "\n" - case *CodeSpan: - return "<code>" + htmlEscaper.Replace(v.Code) + "</code>" - case *InlineImage: - result += `<img src="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `" alt="` + htmlEscaper.Replace(renderImageAltText(v.Children)) + `"` - if title := v.Title(); title != "" { - result += ` title="` + htmlEscaper.Replace(title) + `"` - } - result += ` />` - case *ReferenceImage: - result += `<img src="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `" alt="` + htmlEscaper.Replace(renderImageAltText(v.Children)) + `"` - if title := v.Title(); title != "" { - result += ` title="` + htmlEscaper.Replace(title) + `"` - } - result += ` />` - case *InlineLink: - result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"` - if title := v.Title(); title != "" { - result += ` title="` + htmlEscaper.Replace(title) + `"` - } - result += `>` - for _, inline := range v.Children { - result += RenderInlineHTML(inline) - } - result += "</a>" - case *ReferenceLink: - result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"` - if title := v.Title(); title != "" { - result += ` title="` + htmlEscaper.Replace(title) + `"` - } - result += `>` - for _, inline := range v.Children { - result += RenderInlineHTML(inline) - } - result += "</a>" - case *Autolink: - result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">` - for _, inline := range v.Children { - result += RenderInlineHTML(inline) - } - result += "</a>" - default: - panic(fmt.Sprintf("missing case for type %T", v)) - } - return -} - -func renderImageAltText(children []Inline) (result string) { - for _, inline := range children { - result += renderImageChildAltText(inline) - } - return -} - -func renderImageChildAltText(inline Inline) (result string) { - switch v := inline.(type) { - case *Text: - return v.Text - case *InlineImage: - for _, inline := range v.Children { - result += renderImageChildAltText(inline) - } - case *InlineLink: - for _, inline := range v.Children { - result += renderImageChildAltText(inline) - } - } - return -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html_entities.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html_entities.go deleted file mode 100644 index e94cebb9..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/html_entities.go +++ /dev/null @@ -1,2132 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -var htmlEntities = map[string]string{ - "AElig": "\u00C6", - "AMP": "\u0026", - "Aacute": "\u00C1", - "Abreve": "\u0102", - "Acirc": "\u00C2", - "Acy": "\u0410", - "Afr": "\U0001D504", - "Agrave": "\u00C0", - "Alpha": "\u0391", - "Amacr": "\u0100", - "And": "\u2A53", - "Aogon": "\u0104", - "Aopf": "\U0001D538", - "ApplyFunction": "\u2061", - "Aring": "\u00C5", - "Ascr": "\U0001D49C", - "Assign": "\u2254", - "Atilde": "\u00C3", - "Auml": "\u00C4", - "Backslash": "\u2216", - "Barv": "\u2AE7", - "Barwed": "\u2306", - "Bcy": "\u0411", - "Because": "\u2235", - "Bernoullis": "\u212C", - "Beta": "\u0392", - "Bfr": "\U0001D505", - "Bopf": "\U0001D539", - "Breve": "\u02D8", - "Bscr": "\u212C", - "Bumpeq": "\u224E", - "CHcy": "\u0427", - "COPY": "\u00A9", - "Cacute": "\u0106", - "Cap": "\u22D2", - "CapitalDifferentialD": "\u2145", - "Cayleys": "\u212D", - "Ccaron": "\u010C", - "Ccedil": "\u00C7", - "Ccirc": "\u0108", - "Cconint": "\u2230", - "Cdot": "\u010A", - "Cedilla": "\u00B8", - "CenterDot": "\u00B7", - "Cfr": "\u212D", - "Chi": "\u03A7", - "CircleDot": "\u2299", - "CircleMinus": "\u2296", - "CirclePlus": "\u2295", - "CircleTimes": "\u2297", - "ClockwiseContourIntegral": "\u2232", - "CloseCurlyDoubleQuote": "\u201D", - "CloseCurlyQuote": "\u2019", - "Colon": "\u2237", - "Colone": "\u2A74", - "Congruent": "\u2261", - "Conint": "\u222F", - "ContourIntegral": "\u222E", - "Copf": "\u2102", - "Coproduct": "\u2210", - "CounterClockwiseContourIntegral": "\u2233", - "Cross": "\u2A2F", - "Cscr": "\U0001D49E", - "Cup": "\u22D3", - "CupCap": "\u224D", - "DD": "\u2145", - "DDotrahd": "\u2911", - "DJcy": "\u0402", - "DScy": "\u0405", - "DZcy": "\u040F", - "Dagger": "\u2021", - "Darr": "\u21A1", - "Dashv": "\u2AE4", - "Dcaron": "\u010E", - "Dcy": "\u0414", - "Del": "\u2207", - "Delta": "\u0394", - "Dfr": "\U0001D507", - "DiacriticalAcute": "\u00B4", - "DiacriticalDot": "\u02D9", - "DiacriticalDoubleAcute": "\u02DD", - "DiacriticalGrave": "\u0060", - "DiacriticalTilde": "\u02DC", - "Diamond": "\u22C4", - "DifferentialD": "\u2146", - "Dopf": "\U0001D53B", - "Dot": "\u00A8", - "DotDot": "\u20DC", - "DotEqual": "\u2250", - "DoubleContourIntegral": "\u222F", - "DoubleDot": "\u00A8", - "DoubleDownArrow": "\u21D3", - "DoubleLeftArrow": "\u21D0", - "DoubleLeftRightArrow": "\u21D4", - "DoubleLeftTee": "\u2AE4", - "DoubleLongLeftArrow": "\u27F8", - "DoubleLongLeftRightArrow": "\u27FA", - "DoubleLongRightArrow": "\u27F9", - "DoubleRightArrow": "\u21D2", - "DoubleRightTee": "\u22A8", - "DoubleUpArrow": "\u21D1", - "DoubleUpDownArrow": "\u21D5", - "DoubleVerticalBar": "\u2225", - "DownArrow": "\u2193", - "DownArrowBar": "\u2913", - "DownArrowUpArrow": "\u21F5", - "DownBreve": "\u0311", - "DownLeftRightVector": "\u2950", - "DownLeftTeeVector": "\u295E", - "DownLeftVector": "\u21BD", - "DownLeftVectorBar": "\u2956", - "DownRightTeeVector": "\u295F", - "DownRightVector": "\u21C1", - "DownRightVectorBar": "\u2957", - "DownTee": "\u22A4", - "DownTeeArrow": "\u21A7", - "Downarrow": "\u21D3", - "Dscr": "\U0001D49F", - "Dstrok": "\u0110", - "ENG": "\u014A", - "ETH": "\u00D0", - "Eacute": "\u00C9", - "Ecaron": "\u011A", - "Ecirc": "\u00CA", - "Ecy": "\u042D", - "Edot": "\u0116", - "Efr": "\U0001D508", - "Egrave": "\u00C8", - "Element": "\u2208", - "Emacr": "\u0112", - "EmptySmallSquare": "\u25FB", - "EmptyVerySmallSquare": "\u25AB", - "Eogon": "\u0118", - "Eopf": "\U0001D53C", - "Epsilon": "\u0395", - "Equal": "\u2A75", - "EqualTilde": "\u2242", - "Equilibrium": "\u21CC", - "Escr": "\u2130", - "Esim": "\u2A73", - "Eta": "\u0397", - "Euml": "\u00CB", - "Exists": "\u2203", - "ExponentialE": "\u2147", - "Fcy": "\u0424", - "Ffr": "\U0001D509", - "FilledSmallSquare": "\u25FC", - "FilledVerySmallSquare": "\u25AA", - "Fopf": "\U0001D53D", - "ForAll": "\u2200", - "Fouriertrf": "\u2131", - "Fscr": "\u2131", - "GJcy": "\u0403", - "GT": "\u003E", - "Gamma": "\u0393", - "Gammad": "\u03DC", - "Gbreve": "\u011E", - "Gcedil": "\u0122", - "Gcirc": "\u011C", - "Gcy": "\u0413", - "Gdot": "\u0120", - "Gfr": "\U0001D50A", - "Gg": "\u22D9", - "Gopf": "\U0001D53E", - "GreaterEqual": "\u2265", - "GreaterEqualLess": "\u22DB", - "GreaterFullEqual": "\u2267", - "GreaterGreater": "\u2AA2", - "GreaterLess": "\u2277", - "GreaterSlantEqual": "\u2A7E", - "GreaterTilde": "\u2273", - "Gscr": "\U0001D4A2", - "Gt": "\u226B", - "HARDcy": "\u042A", - "Hacek": "\u02C7", - "Hat": "\u005E", - "Hcirc": "\u0124", - "Hfr": "\u210C", - "HilbertSpace": "\u210B", - "Hopf": "\u210D", - "HorizontalLine": "\u2500", - "Hscr": "\u210B", - "Hstrok": "\u0126", - "HumpDownHump": "\u224E", - "HumpEqual": "\u224F", - "IEcy": "\u0415", - "IJlig": "\u0132", - "IOcy": "\u0401", - "Iacute": "\u00CD", - "Icirc": "\u00CE", - "Icy": "\u0418", - "Idot": "\u0130", - "Ifr": "\u2111", - "Igrave": "\u00CC", - "Im": "\u2111", - "Imacr": "\u012A", - "ImaginaryI": "\u2148", - "Implies": "\u21D2", - "Int": "\u222C", - "Integral": "\u222B", - "Intersection": "\u22C2", - "InvisibleComma": "\u2063", - "InvisibleTimes": "\u2062", - "Iogon": "\u012E", - "Iopf": "\U0001D540", - "Iota": "\u0399", - "Iscr": "\u2110", - "Itilde": "\u0128", - "Iukcy": "\u0406", - "Iuml": "\u00CF", - "Jcirc": "\u0134", - "Jcy": "\u0419", - "Jfr": "\U0001D50D", - "Jopf": "\U0001D541", - "Jscr": "\U0001D4A5", - "Jsercy": "\u0408", - "Jukcy": "\u0404", - "KHcy": "\u0425", - "KJcy": "\u040C", - "Kappa": "\u039A", - "Kcedil": "\u0136", - "Kcy": "\u041A", - "Kfr": "\U0001D50E", - "Kopf": "\U0001D542", - "Kscr": "\U0001D4A6", - "LJcy": "\u0409", - "LT": "\u003C", - "Lacute": "\u0139", - "Lambda": "\u039B", - "Lang": "\u27EA", - "Laplacetrf": "\u2112", - "Larr": "\u219E", - "Lcaron": "\u013D", - "Lcedil": "\u013B", - "Lcy": "\u041B", - "LeftAngleBracket": "\u27E8", - "LeftArrow": "\u2190", - "LeftArrowBar": "\u21E4", - "LeftArrowRightArrow": "\u21C6", - "LeftCeiling": "\u2308", - "LeftDoubleBracket": "\u27E6", - "LeftDownTeeVector": "\u2961", - "LeftDownVector": "\u21C3", - "LeftDownVectorBar": "\u2959", - "LeftFloor": "\u230A", - "LeftRightArrow": "\u2194", - "LeftRightVector": "\u294E", - "LeftTee": "\u22A3", - "LeftTeeArrow": "\u21A4", - "LeftTeeVector": "\u295A", - "LeftTriangle": "\u22B2", - "LeftTriangleBar": "\u29CF", - "LeftTriangleEqual": "\u22B4", - "LeftUpDownVector": "\u2951", - "LeftUpTeeVector": "\u2960", - "LeftUpVector": "\u21BF", - "LeftUpVectorBar": "\u2958", - "LeftVector": "\u21BC", - "LeftVectorBar": "\u2952", - "Leftarrow": "\u21D0", - "Leftrightarrow": "\u21D4", - "LessEqualGreater": "\u22DA", - "LessFullEqual": "\u2266", - "LessGreater": "\u2276", - "LessLess": "\u2AA1", - "LessSlantEqual": "\u2A7D", - "LessTilde": "\u2272", - "Lfr": "\U0001D50F", - "Ll": "\u22D8", - "Lleftarrow": "\u21DA", - "Lmidot": "\u013F", - "LongLeftArrow": "\u27F5", - "LongLeftRightArrow": "\u27F7", - "LongRightArrow": "\u27F6", - "Longleftarrow": "\u27F8", - "Longleftrightarrow": "\u27FA", - "Longrightarrow": "\u27F9", - "Lopf": "\U0001D543", - "LowerLeftArrow": "\u2199", - "LowerRightArrow": "\u2198", - "Lscr": "\u2112", - "Lsh": "\u21B0", - "Lstrok": "\u0141", - "Lt": "\u226A", - "Map": "\u2905", - "Mcy": "\u041C", - "MediumSpace": "\u205F", - "Mellintrf": "\u2133", - "Mfr": "\U0001D510", - "MinusPlus": "\u2213", - "Mopf": "\U0001D544", - "Mscr": "\u2133", - "Mu": "\u039C", - "NJcy": "\u040A", - "Nacute": "\u0143", - "Ncaron": "\u0147", - "Ncedil": "\u0145", - "Ncy": "\u041D", - "NegativeMediumSpace": "\u200B", - "NegativeThickSpace": "\u200B", - "NegativeThinSpace": "\u200B", - "NegativeVeryThinSpace": "\u200B", - "NestedGreaterGreater": "\u226B", - "NestedLessLess": "\u226A", - "NewLine": "\u000A", - "Nfr": "\U0001D511", - "NoBreak": "\u2060", - "NonBreakingSpace": "\u00A0", - "Nopf": "\u2115", - "Not": "\u2AEC", - "NotCongruent": "\u2262", - "NotCupCap": "\u226D", - "NotDoubleVerticalBar": "\u2226", - "NotElement": "\u2209", - "NotEqual": "\u2260", - "NotEqualTilde": "\u2242\u0338", - "NotExists": "\u2204", - "NotGreater": "\u226F", - "NotGreaterEqual": "\u2271", - "NotGreaterFullEqual": "\u2267\u0338", - "NotGreaterGreater": "\u226B\u0338", - "NotGreaterLess": "\u2279", - "NotGreaterSlantEqual": "\u2A7E\u0338", - "NotGreaterTilde": "\u2275", - "NotHumpDownHump": "\u224E\u0338", - "NotHumpEqual": "\u224F\u0338", - "NotLeftTriangle": "\u22EA", - "NotLeftTriangleBar": "\u29CF\u0338", - "NotLeftTriangleEqual": "\u22EC", - "NotLess": "\u226E", - "NotLessEqual": "\u2270", - "NotLessGreater": "\u2278", - "NotLessLess": "\u226A\u0338", - "NotLessSlantEqual": "\u2A7D\u0338", - "NotLessTilde": "\u2274", - "NotNestedGreaterGreater": "\u2AA2\u0338", - "NotNestedLessLess": "\u2AA1\u0338", - "NotPrecedes": "\u2280", - "NotPrecedesEqual": "\u2AAF\u0338", - "NotPrecedesSlantEqual": "\u22E0", - "NotReverseElement": "\u220C", - "NotRightTriangle": "\u22EB", - "NotRightTriangleBar": "\u29D0\u0338", - "NotRightTriangleEqual": "\u22ED", - "NotSquareSubset": "\u228F\u0338", - "NotSquareSubsetEqual": "\u22E2", - "NotSquareSuperset": "\u2290\u0338", - "NotSquareSupersetEqual": "\u22E3", - "NotSubset": "\u2282\u20D2", - "NotSubsetEqual": "\u2288", - "NotSucceeds": "\u2281", - "NotSucceedsEqual": "\u2AB0\u0338", - "NotSucceedsSlantEqual": "\u22E1", - "NotSucceedsTilde": "\u227F\u0338", - "NotSuperset": "\u2283\u20D2", - "NotSupersetEqual": "\u2289", - "NotTilde": "\u2241", - "NotTildeEqual": "\u2244", - "NotTildeFullEqual": "\u2247", - "NotTildeTilde": "\u2249", - "NotVerticalBar": "\u2224", - "Nscr": "\U0001D4A9", - "Ntilde": "\u00D1", - "Nu": "\u039D", - "OElig": "\u0152", - "Oacute": "\u00D3", - "Ocirc": "\u00D4", - "Ocy": "\u041E", - "Odblac": "\u0150", - "Ofr": "\U0001D512", - "Ograve": "\u00D2", - "Omacr": "\u014C", - "Omega": "\u03A9", - "Omicron": "\u039F", - "Oopf": "\U0001D546", - "OpenCurlyDoubleQuote": "\u201C", - "OpenCurlyQuote": "\u2018", - "Or": "\u2A54", - "Oscr": "\U0001D4AA", - "Oslash": "\u00D8", - "Otilde": "\u00D5", - "Otimes": "\u2A37", - "Ouml": "\u00D6", - "OverBar": "\u203E", - "OverBrace": "\u23DE", - "OverBracket": "\u23B4", - "OverParenthesis": "\u23DC", - "PartialD": "\u2202", - "Pcy": "\u041F", - "Pfr": "\U0001D513", - "Phi": "\u03A6", - "Pi": "\u03A0", - "PlusMinus": "\u00B1", - "Poincareplane": "\u210C", - "Popf": "\u2119", - "Pr": "\u2ABB", - "Precedes": "\u227A", - "PrecedesEqual": "\u2AAF", - "PrecedesSlantEqual": "\u227C", - "PrecedesTilde": "\u227E", - "Prime": "\u2033", - "Product": "\u220F", - "Proportion": "\u2237", - "Proportional": "\u221D", - "Pscr": "\U0001D4AB", - "Psi": "\u03A8", - "QUOT": "\u0022", - "Qfr": "\U0001D514", - "Qopf": "\u211A", - "Qscr": "\U0001D4AC", - "RBarr": "\u2910", - "REG": "\u00AE", - "Racute": "\u0154", - "Rang": "\u27EB", - "Rarr": "\u21A0", - "Rarrtl": "\u2916", - "Rcaron": "\u0158", - "Rcedil": "\u0156", - "Rcy": "\u0420", - "Re": "\u211C", - "ReverseElement": "\u220B", - "ReverseEquilibrium": "\u21CB", - "ReverseUpEquilibrium": "\u296F", - "Rfr": "\u211C", - "Rho": "\u03A1", - "RightAngleBracket": "\u27E9", - "RightArrow": "\u2192", - "RightArrowBar": "\u21E5", - "RightArrowLeftArrow": "\u21C4", - "RightCeiling": "\u2309", - "RightDoubleBracket": "\u27E7", - "RightDownTeeVector": "\u295D", - "RightDownVector": "\u21C2", - "RightDownVectorBar": "\u2955", - "RightFloor": "\u230B", - "RightTee": "\u22A2", - "RightTeeArrow": "\u21A6", - "RightTeeVector": "\u295B", - "RightTriangle": "\u22B3", - "RightTriangleBar": "\u29D0", - "RightTriangleEqual": "\u22B5", - "RightUpDownVector": "\u294F", - "RightUpTeeVector": "\u295C", - "RightUpVector": "\u21BE", - "RightUpVectorBar": "\u2954", - "RightVector": "\u21C0", - "RightVectorBar": "\u2953", - "Rightarrow": "\u21D2", - "Ropf": "\u211D", - "RoundImplies": "\u2970", - "Rrightarrow": "\u21DB", - "Rscr": "\u211B", - "Rsh": "\u21B1", - "RuleDelayed": "\u29F4", - "SHCHcy": "\u0429", - "SHcy": "\u0428", - "SOFTcy": "\u042C", - "Sacute": "\u015A", - "Sc": "\u2ABC", - "Scaron": "\u0160", - "Scedil": "\u015E", - "Scirc": "\u015C", - "Scy": "\u0421", - "Sfr": "\U0001D516", - "ShortDownArrow": "\u2193", - "ShortLeftArrow": "\u2190", - "ShortRightArrow": "\u2192", - "ShortUpArrow": "\u2191", - "Sigma": "\u03A3", - "SmallCircle": "\u2218", - "Sopf": "\U0001D54A", - "Sqrt": "\u221A", - "Square": "\u25A1", - "SquareIntersection": "\u2293", - "SquareSubset": "\u228F", - "SquareSubsetEqual": "\u2291", - "SquareSuperset": "\u2290", - "SquareSupersetEqual": "\u2292", - "SquareUnion": "\u2294", - "Sscr": "\U0001D4AE", - "Star": "\u22C6", - "Sub": "\u22D0", - "Subset": "\u22D0", - "SubsetEqual": "\u2286", - "Succeeds": "\u227B", - "SucceedsEqual": "\u2AB0", - "SucceedsSlantEqual": "\u227D", - "SucceedsTilde": "\u227F", - "SuchThat": "\u220B", - "Sum": "\u2211", - "Sup": "\u22D1", - "Superset": "\u2283", - "SupersetEqual": "\u2287", - "Supset": "\u22D1", - "THORN": "\u00DE", - "TRADE": "\u2122", - "TSHcy": "\u040B", - "TScy": "\u0426", - "Tab": "\u0009", - "Tau": "\u03A4", - "Tcaron": "\u0164", - "Tcedil": "\u0162", - "Tcy": "\u0422", - "Tfr": "\U0001D517", - "Therefore": "\u2234", - "Theta": "\u0398", - "ThickSpace": "\u205F\u200A", - "ThinSpace": "\u2009", - "Tilde": "\u223C", - "TildeEqual": "\u2243", - "TildeFullEqual": "\u2245", - "TildeTilde": "\u2248", - "Topf": "\U0001D54B", - "TripleDot": "\u20DB", - "Tscr": "\U0001D4AF", - "Tstrok": "\u0166", - "Uacute": "\u00DA", - "Uarr": "\u219F", - "Uarrocir": "\u2949", - "Ubrcy": "\u040E", - "Ubreve": "\u016C", - "Ucirc": "\u00DB", - "Ucy": "\u0423", - "Udblac": "\u0170", - "Ufr": "\U0001D518", - "Ugrave": "\u00D9", - "Umacr": "\u016A", - "UnderBar": "\u005F", - "UnderBrace": "\u23DF", - "UnderBracket": "\u23B5", - "UnderParenthesis": "\u23DD", - "Union": "\u22C3", - "UnionPlus": "\u228E", - "Uogon": "\u0172", - "Uopf": "\U0001D54C", - "UpArrow": "\u2191", - "UpArrowBar": "\u2912", - "UpArrowDownArrow": "\u21C5", - "UpDownArrow": "\u2195", - "UpEquilibrium": "\u296E", - "UpTee": "\u22A5", - "UpTeeArrow": "\u21A5", - "Uparrow": "\u21D1", - "Updownarrow": "\u21D5", - "UpperLeftArrow": "\u2196", - "UpperRightArrow": "\u2197", - "Upsi": "\u03D2", - "Upsilon": "\u03A5", - "Uring": "\u016E", - "Uscr": "\U0001D4B0", - "Utilde": "\u0168", - "Uuml": "\u00DC", - "VDash": "\u22AB", - "Vbar": "\u2AEB", - "Vcy": "\u0412", - "Vdash": "\u22A9", - "Vdashl": "\u2AE6", - "Vee": "\u22C1", - "Verbar": "\u2016", - "Vert": "\u2016", - "VerticalBar": "\u2223", - "VerticalLine": "\u007C", - "VerticalSeparator": "\u2758", - "VerticalTilde": "\u2240", - "VeryThinSpace": "\u200A", - "Vfr": "\U0001D519", - "Vopf": "\U0001D54D", - "Vscr": "\U0001D4B1", - "Vvdash": "\u22AA", - "Wcirc": "\u0174", - "Wedge": "\u22C0", - "Wfr": "\U0001D51A", - "Wopf": "\U0001D54E", - "Wscr": "\U0001D4B2", - "Xfr": "\U0001D51B", - "Xi": "\u039E", - "Xopf": "\U0001D54F", - "Xscr": "\U0001D4B3", - "YAcy": "\u042F", - "YIcy": "\u0407", - "YUcy": "\u042E", - "Yacute": "\u00DD", - "Ycirc": "\u0176", - "Ycy": "\u042B", - "Yfr": "\U0001D51C", - "Yopf": "\U0001D550", - "Yscr": "\U0001D4B4", - "Yuml": "\u0178", - "ZHcy": "\u0416", - "Zacute": "\u0179", - "Zcaron": "\u017D", - "Zcy": "\u0417", - "Zdot": "\u017B", - "ZeroWidthSpace": "\u200B", - "Zeta": "\u0396", - "Zfr": "\u2128", - "Zopf": "\u2124", - "Zscr": "\U0001D4B5", - "aacute": "\u00E1", - "abreve": "\u0103", - "ac": "\u223E", - "acE": "\u223E\u0333", - "acd": "\u223F", - "acirc": "\u00E2", - "acute": "\u00B4", - "acy": "\u0430", - "aelig": "\u00E6", - "af": "\u2061", - "afr": "\U0001D51E", - "agrave": "\u00E0", - "alefsym": "\u2135", - "aleph": "\u2135", - "alpha": "\u03B1", - "amacr": "\u0101", - "amalg": "\u2A3F", - "amp": "\u0026", - "and": "\u2227", - "andand": "\u2A55", - "andd": "\u2A5C", - "andslope": "\u2A58", - "andv": "\u2A5A", - "ang": "\u2220", - "ange": "\u29A4", - "angle": "\u2220", - "angmsd": "\u2221", - "angmsdaa": "\u29A8", - "angmsdab": "\u29A9", - "angmsdac": "\u29AA", - "angmsdad": "\u29AB", - "angmsdae": "\u29AC", - "angmsdaf": "\u29AD", - "angmsdag": "\u29AE", - "angmsdah": "\u29AF", - "angrt": "\u221F", - "angrtvb": "\u22BE", - "angrtvbd": "\u299D", - "angsph": "\u2222", - "angst": "\u00C5", - "angzarr": "\u237C", - "aogon": "\u0105", - "aopf": "\U0001D552", - "ap": "\u2248", - "apE": "\u2A70", - "apacir": "\u2A6F", - "ape": "\u224A", - "apid": "\u224B", - "apos": "\u0027", - "approx": "\u2248", - "approxeq": "\u224A", - "aring": "\u00E5", - "ascr": "\U0001D4B6", - "ast": "\u002A", - "asymp": "\u2248", - "asympeq": "\u224D", - "atilde": "\u00E3", - "auml": "\u00E4", - "awconint": "\u2233", - "awint": "\u2A11", - "bNot": "\u2AED", - "backcong": "\u224C", - "backepsilon": "\u03F6", - "backprime": "\u2035", - "backsim": "\u223D", - "backsimeq": "\u22CD", - "barvee": "\u22BD", - "barwed": "\u2305", - "barwedge": "\u2305", - "bbrk": "\u23B5", - "bbrktbrk": "\u23B6", - "bcong": "\u224C", - "bcy": "\u0431", - "bdquo": "\u201E", - "becaus": "\u2235", - "because": "\u2235", - "bemptyv": "\u29B0", - "bepsi": "\u03F6", - "bernou": "\u212C", - "beta": "\u03B2", - "beth": "\u2136", - "between": "\u226C", - "bfr": "\U0001D51F", - "bigcap": "\u22C2", - "bigcirc": "\u25EF", - "bigcup": "\u22C3", - "bigodot": "\u2A00", - "bigoplus": "\u2A01", - "bigotimes": "\u2A02", - "bigsqcup": "\u2A06", - "bigstar": "\u2605", - "bigtriangledown": "\u25BD", - "bigtriangleup": "\u25B3", - "biguplus": "\u2A04", - "bigvee": "\u22C1", - "bigwedge": "\u22C0", - "bkarow": "\u290D", - "blacklozenge": "\u29EB", - "blacksquare": "\u25AA", - "blacktriangle": "\u25B4", - "blacktriangledown": "\u25BE", - "blacktriangleleft": "\u25C2", - "blacktriangleright": "\u25B8", - "blank": "\u2423", - "blk12": "\u2592", - "blk14": "\u2591", - "blk34": "\u2593", - "block": "\u2588", - "bne": "\u003D\u20E5", - "bnequiv": "\u2261\u20E5", - "bnot": "\u2310", - "bopf": "\U0001D553", - "bot": "\u22A5", - "bottom": "\u22A5", - "bowtie": "\u22C8", - "boxDL": "\u2557", - "boxDR": "\u2554", - "boxDl": "\u2556", - "boxDr": "\u2553", - "boxH": "\u2550", - "boxHD": "\u2566", - "boxHU": "\u2569", - "boxHd": "\u2564", - "boxHu": "\u2567", - "boxUL": "\u255D", - "boxUR": "\u255A", - "boxUl": "\u255C", - "boxUr": "\u2559", - "boxV": "\u2551", - "boxVH": "\u256C", - "boxVL": "\u2563", - "boxVR": "\u2560", - "boxVh": "\u256B", - "boxVl": "\u2562", - "boxVr": "\u255F", - "boxbox": "\u29C9", - "boxdL": "\u2555", - "boxdR": "\u2552", - "boxdl": "\u2510", - "boxdr": "\u250C", - "boxh": "\u2500", - "boxhD": "\u2565", - "boxhU": "\u2568", - "boxhd": "\u252C", - "boxhu": "\u2534", - "boxminus": "\u229F", - "boxplus": "\u229E", - "boxtimes": "\u22A0", - "boxuL": "\u255B", - "boxuR": "\u2558", - "boxul": "\u2518", - "boxur": "\u2514", - "boxv": "\u2502", - "boxvH": "\u256A", - "boxvL": "\u2561", - "boxvR": "\u255E", - "boxvh": "\u253C", - "boxvl": "\u2524", - "boxvr": "\u251C", - "bprime": "\u2035", - "breve": "\u02D8", - "brvbar": "\u00A6", - "bscr": "\U0001D4B7", - "bsemi": "\u204F", - "bsim": "\u223D", - "bsime": "\u22CD", - "bsol": "\u005C", - "bsolb": "\u29C5", - "bsolhsub": "\u27C8", - "bull": "\u2022", - "bullet": "\u2022", - "bump": "\u224E", - "bumpE": "\u2AAE", - "bumpe": "\u224F", - "bumpeq": "\u224F", - "cacute": "\u0107", - "cap": "\u2229", - "capand": "\u2A44", - "capbrcup": "\u2A49", - "capcap": "\u2A4B", - "capcup": "\u2A47", - "capdot": "\u2A40", - "caps": "\u2229\uFE00", - "caret": "\u2041", - "caron": "\u02C7", - "ccaps": "\u2A4D", - "ccaron": "\u010D", - "ccedil": "\u00E7", - "ccirc": "\u0109", - "ccups": "\u2A4C", - "ccupssm": "\u2A50", - "cdot": "\u010B", - "cedil": "\u00B8", - "cemptyv": "\u29B2", - "cent": "\u00A2", - "centerdot": "\u00B7", - "cfr": "\U0001D520", - "chcy": "\u0447", - "check": "\u2713", - "checkmark": "\u2713", - "chi": "\u03C7", - "cir": "\u25CB", - "cirE": "\u29C3", - "circ": "\u02C6", - "circeq": "\u2257", - "circlearrowleft": "\u21BA", - "circlearrowright": "\u21BB", - "circledR": "\u00AE", - "circledS": "\u24C8", - "circledast": "\u229B", - "circledcirc": "\u229A", - "circleddash": "\u229D", - "cire": "\u2257", - "cirfnint": "\u2A10", - "cirmid": "\u2AEF", - "cirscir": "\u29C2", - "clubs": "\u2663", - "clubsuit": "\u2663", - "colon": "\u003A", - "colone": "\u2254", - "coloneq": "\u2254", - "comma": "\u002C", - "commat": "\u0040", - "comp": "\u2201", - "compfn": "\u2218", - "complement": "\u2201", - "complexes": "\u2102", - "cong": "\u2245", - "congdot": "\u2A6D", - "conint": "\u222E", - "copf": "\U0001D554", - "coprod": "\u2210", - "copy": "\u00A9", - "copysr": "\u2117", - "crarr": "\u21B5", - "cross": "\u2717", - "cscr": "\U0001D4B8", - "csub": "\u2ACF", - "csube": "\u2AD1", - "csup": "\u2AD0", - "csupe": "\u2AD2", - "ctdot": "\u22EF", - "cudarrl": "\u2938", - "cudarrr": "\u2935", - "cuepr": "\u22DE", - "cuesc": "\u22DF", - "cularr": "\u21B6", - "cularrp": "\u293D", - "cup": "\u222A", - "cupbrcap": "\u2A48", - "cupcap": "\u2A46", - "cupcup": "\u2A4A", - "cupdot": "\u228D", - "cupor": "\u2A45", - "cups": "\u222A\uFE00", - "curarr": "\u21B7", - "curarrm": "\u293C", - "curlyeqprec": "\u22DE", - "curlyeqsucc": "\u22DF", - "curlyvee": "\u22CE", - "curlywedge": "\u22CF", - "curren": "\u00A4", - "curvearrowleft": "\u21B6", - "curvearrowright": "\u21B7", - "cuvee": "\u22CE", - "cuwed": "\u22CF", - "cwconint": "\u2232", - "cwint": "\u2231", - "cylcty": "\u232D", - "dArr": "\u21D3", - "dHar": "\u2965", - "dagger": "\u2020", - "daleth": "\u2138", - "darr": "\u2193", - "dash": "\u2010", - "dashv": "\u22A3", - "dbkarow": "\u290F", - "dblac": "\u02DD", - "dcaron": "\u010F", - "dcy": "\u0434", - "dd": "\u2146", - "ddagger": "\u2021", - "ddarr": "\u21CA", - "ddotseq": "\u2A77", - "deg": "\u00B0", - "delta": "\u03B4", - "demptyv": "\u29B1", - "dfisht": "\u297F", - "dfr": "\U0001D521", - "dharl": "\u21C3", - "dharr": "\u21C2", - "diam": "\u22C4", - "diamond": "\u22C4", - "diamondsuit": "\u2666", - "diams": "\u2666", - "die": "\u00A8", - "digamma": "\u03DD", - "disin": "\u22F2", - "div": "\u00F7", - "divide": "\u00F7", - "divideontimes": "\u22C7", - "divonx": "\u22C7", - "djcy": "\u0452", - "dlcorn": "\u231E", - "dlcrop": "\u230D", - "dollar": "\u0024", - "dopf": "\U0001D555", - "dot": "\u02D9", - "doteq": "\u2250", - "doteqdot": "\u2251", - "dotminus": "\u2238", - "dotplus": "\u2214", - "dotsquare": "\u22A1", - "doublebarwedge": "\u2306", - "downarrow": "\u2193", - "downdownarrows": "\u21CA", - "downharpoonleft": "\u21C3", - "downharpoonright": "\u21C2", - "drbkarow": "\u2910", - "drcorn": "\u231F", - "drcrop": "\u230C", - "dscr": "\U0001D4B9", - "dscy": "\u0455", - "dsol": "\u29F6", - "dstrok": "\u0111", - "dtdot": "\u22F1", - "dtri": "\u25BF", - "dtrif": "\u25BE", - "duarr": "\u21F5", - "duhar": "\u296F", - "dwangle": "\u29A6", - "dzcy": "\u045F", - "dzigrarr": "\u27FF", - "eDDot": "\u2A77", - "eDot": "\u2251", - "eacute": "\u00E9", - "easter": "\u2A6E", - "ecaron": "\u011B", - "ecir": "\u2256", - "ecirc": "\u00EA", - "ecolon": "\u2255", - "ecy": "\u044D", - "edot": "\u0117", - "ee": "\u2147", - "efDot": "\u2252", - "efr": "\U0001D522", - "eg": "\u2A9A", - "egrave": "\u00E8", - "egs": "\u2A96", - "egsdot": "\u2A98", - "el": "\u2A99", - "elinters": "\u23E7", - "ell": "\u2113", - "els": "\u2A95", - "elsdot": "\u2A97", - "emacr": "\u0113", - "empty": "\u2205", - "emptyset": "\u2205", - "emptyv": "\u2205", - "emsp": "\u2003", - "emsp13": "\u2004", - "emsp14": "\u2005", - "eng": "\u014B", - "ensp": "\u2002", - "eogon": "\u0119", - "eopf": "\U0001D556", - "epar": "\u22D5", - "eparsl": "\u29E3", - "eplus": "\u2A71", - "epsi": "\u03B5", - "epsilon": "\u03B5", - "epsiv": "\u03F5", - "eqcirc": "\u2256", - "eqcolon": "\u2255", - "eqsim": "\u2242", - "eqslantgtr": "\u2A96", - "eqslantless": "\u2A95", - "equals": "\u003D", - "equest": "\u225F", - "equiv": "\u2261", - "equivDD": "\u2A78", - "eqvparsl": "\u29E5", - "erDot": "\u2253", - "erarr": "\u2971", - "escr": "\u212F", - "esdot": "\u2250", - "esim": "\u2242", - "eta": "\u03B7", - "eth": "\u00F0", - "euml": "\u00EB", - "euro": "\u20AC", - "excl": "\u0021", - "exist": "\u2203", - "expectation": "\u2130", - "exponentiale": "\u2147", - "fallingdotseq": "\u2252", - "fcy": "\u0444", - "female": "\u2640", - "ffilig": "\uFB03", - "fflig": "\uFB00", - "ffllig": "\uFB04", - "ffr": "\U0001D523", - "filig": "\uFB01", - "fjlig": "\u0066\u006A", - "flat": "\u266D", - "fllig": "\uFB02", - "fltns": "\u25B1", - "fnof": "\u0192", - "fopf": "\U0001D557", - "forall": "\u2200", - "fork": "\u22D4", - "forkv": "\u2AD9", - "fpartint": "\u2A0D", - "frac12": "\u00BD", - "frac13": "\u2153", - "frac14": "\u00BC", - "frac15": "\u2155", - "frac16": "\u2159", - "frac18": "\u215B", - "frac23": "\u2154", - "frac25": "\u2156", - "frac34": "\u00BE", - "frac35": "\u2157", - "frac38": "\u215C", - "frac45": "\u2158", - "frac56": "\u215A", - "frac58": "\u215D", - "frac78": "\u215E", - "frasl": "\u2044", - "frown": "\u2322", - "fscr": "\U0001D4BB", - "gE": "\u2267", - "gEl": "\u2A8C", - "gacute": "\u01F5", - "gamma": "\u03B3", - "gammad": "\u03DD", - "gap": "\u2A86", - "gbreve": "\u011F", - "gcirc": "\u011D", - "gcy": "\u0433", - "gdot": "\u0121", - "ge": "\u2265", - "gel": "\u22DB", - "geq": "\u2265", - "geqq": "\u2267", - "geqslant": "\u2A7E", - "ges": "\u2A7E", - "gescc": "\u2AA9", - "gesdot": "\u2A80", - "gesdoto": "\u2A82", - "gesdotol": "\u2A84", - "gesl": "\u22DB\uFE00", - "gesles": "\u2A94", - "gfr": "\U0001D524", - "gg": "\u226B", - "ggg": "\u22D9", - "gimel": "\u2137", - "gjcy": "\u0453", - "gl": "\u2277", - "glE": "\u2A92", - "gla": "\u2AA5", - "glj": "\u2AA4", - "gnE": "\u2269", - "gnap": "\u2A8A", - "gnapprox": "\u2A8A", - "gne": "\u2A88", - "gneq": "\u2A88", - "gneqq": "\u2269", - "gnsim": "\u22E7", - "gopf": "\U0001D558", - "grave": "\u0060", - "gscr": "\u210A", - "gsim": "\u2273", - "gsime": "\u2A8E", - "gsiml": "\u2A90", - "gt": "\u003E", - "gtcc": "\u2AA7", - "gtcir": "\u2A7A", - "gtdot": "\u22D7", - "gtlPar": "\u2995", - "gtquest": "\u2A7C", - "gtrapprox": "\u2A86", - "gtrarr": "\u2978", - "gtrdot": "\u22D7", - "gtreqless": "\u22DB", - "gtreqqless": "\u2A8C", - "gtrless": "\u2277", - "gtrsim": "\u2273", - "gvertneqq": "\u2269\uFE00", - "gvnE": "\u2269\uFE00", - "hArr": "\u21D4", - "hairsp": "\u200A", - "half": "\u00BD", - "hamilt": "\u210B", - "hardcy": "\u044A", - "harr": "\u2194", - "harrcir": "\u2948", - "harrw": "\u21AD", - "hbar": "\u210F", - "hcirc": "\u0125", - "hearts": "\u2665", - "heartsuit": "\u2665", - "hellip": "\u2026", - "hercon": "\u22B9", - "hfr": "\U0001D525", - "hksearow": "\u2925", - "hkswarow": "\u2926", - "hoarr": "\u21FF", - "homtht": "\u223B", - "hookleftarrow": "\u21A9", - "hookrightarrow": "\u21AA", - "hopf": "\U0001D559", - "horbar": "\u2015", - "hscr": "\U0001D4BD", - "hslash": "\u210F", - "hstrok": "\u0127", - "hybull": "\u2043", - "hyphen": "\u2010", - "iacute": "\u00ED", - "ic": "\u2063", - "icirc": "\u00EE", - "icy": "\u0438", - "iecy": "\u0435", - "iexcl": "\u00A1", - "iff": "\u21D4", - "ifr": "\U0001D526", - "igrave": "\u00EC", - "ii": "\u2148", - "iiiint": "\u2A0C", - "iiint": "\u222D", - "iinfin": "\u29DC", - "iiota": "\u2129", - "ijlig": "\u0133", - "imacr": "\u012B", - "image": "\u2111", - "imagline": "\u2110", - "imagpart": "\u2111", - "imath": "\u0131", - "imof": "\u22B7", - "imped": "\u01B5", - "in": "\u2208", - "incare": "\u2105", - "infin": "\u221E", - "infintie": "\u29DD", - "inodot": "\u0131", - "int": "\u222B", - "intcal": "\u22BA", - "integers": "\u2124", - "intercal": "\u22BA", - "intlarhk": "\u2A17", - "intprod": "\u2A3C", - "iocy": "\u0451", - "iogon": "\u012F", - "iopf": "\U0001D55A", - "iota": "\u03B9", - "iprod": "\u2A3C", - "iquest": "\u00BF", - "iscr": "\U0001D4BE", - "isin": "\u2208", - "isinE": "\u22F9", - "isindot": "\u22F5", - "isins": "\u22F4", - "isinsv": "\u22F3", - "isinv": "\u2208", - "it": "\u2062", - "itilde": "\u0129", - "iukcy": "\u0456", - "iuml": "\u00EF", - "jcirc": "\u0135", - "jcy": "\u0439", - "jfr": "\U0001D527", - "jmath": "\u0237", - "jopf": "\U0001D55B", - "jscr": "\U0001D4BF", - "jsercy": "\u0458", - "jukcy": "\u0454", - "kappa": "\u03BA", - "kappav": "\u03F0", - "kcedil": "\u0137", - "kcy": "\u043A", - "kfr": "\U0001D528", - "kgreen": "\u0138", - "khcy": "\u0445", - "kjcy": "\u045C", - "kopf": "\U0001D55C", - "kscr": "\U0001D4C0", - "lAarr": "\u21DA", - "lArr": "\u21D0", - "lAtail": "\u291B", - "lBarr": "\u290E", - "lE": "\u2266", - "lEg": "\u2A8B", - "lHar": "\u2962", - "lacute": "\u013A", - "laemptyv": "\u29B4", - "lagran": "\u2112", - "lambda": "\u03BB", - "lang": "\u27E8", - "langd": "\u2991", - "langle": "\u27E8", - "lap": "\u2A85", - "laquo": "\u00AB", - "larr": "\u2190", - "larrb": "\u21E4", - "larrbfs": "\u291F", - "larrfs": "\u291D", - "larrhk": "\u21A9", - "larrlp": "\u21AB", - "larrpl": "\u2939", - "larrsim": "\u2973", - "larrtl": "\u21A2", - "lat": "\u2AAB", - "latail": "\u2919", - "late": "\u2AAD", - "lates": "\u2AAD\uFE00", - "lbarr": "\u290C", - "lbbrk": "\u2772", - "lbrace": "\u007B", - "lbrack": "\u005B", - "lbrke": "\u298B", - "lbrksld": "\u298F", - "lbrkslu": "\u298D", - "lcaron": "\u013E", - "lcedil": "\u013C", - "lceil": "\u2308", - "lcub": "\u007B", - "lcy": "\u043B", - "ldca": "\u2936", - "ldquo": "\u201C", - "ldquor": "\u201E", - "ldrdhar": "\u2967", - "ldrushar": "\u294B", - "ldsh": "\u21B2", - "le": "\u2264", - "leftarrow": "\u2190", - "leftarrowtail": "\u21A2", - "leftharpoondown": "\u21BD", - "leftharpoonup": "\u21BC", - "leftleftarrows": "\u21C7", - "leftrightarrow": "\u2194", - "leftrightarrows": "\u21C6", - "leftrightharpoons": "\u21CB", - "leftrightsquigarrow": "\u21AD", - "leftthreetimes": "\u22CB", - "leg": "\u22DA", - "leq": "\u2264", - "leqq": "\u2266", - "leqslant": "\u2A7D", - "les": "\u2A7D", - "lescc": "\u2AA8", - "lesdot": "\u2A7F", - "lesdoto": "\u2A81", - "lesdotor": "\u2A83", - "lesg": "\u22DA\uFE00", - "lesges": "\u2A93", - "lessapprox": "\u2A85", - "lessdot": "\u22D6", - "lesseqgtr": "\u22DA", - "lesseqqgtr": "\u2A8B", - "lessgtr": "\u2276", - "lesssim": "\u2272", - "lfisht": "\u297C", - "lfloor": "\u230A", - "lfr": "\U0001D529", - "lg": "\u2276", - "lgE": "\u2A91", - "lhard": "\u21BD", - "lharu": "\u21BC", - "lharul": "\u296A", - "lhblk": "\u2584", - "ljcy": "\u0459", - "ll": "\u226A", - "llarr": "\u21C7", - "llcorner": "\u231E", - "llhard": "\u296B", - "lltri": "\u25FA", - "lmidot": "\u0140", - "lmoust": "\u23B0", - "lmoustache": "\u23B0", - "lnE": "\u2268", - "lnap": "\u2A89", - "lnapprox": "\u2A89", - "lne": "\u2A87", - "lneq": "\u2A87", - "lneqq": "\u2268", - "lnsim": "\u22E6", - "loang": "\u27EC", - "loarr": "\u21FD", - "lobrk": "\u27E6", - "longleftarrow": "\u27F5", - "longleftrightarrow": "\u27F7", - "longmapsto": "\u27FC", - "longrightarrow": "\u27F6", - "looparrowleft": "\u21AB", - "looparrowright": "\u21AC", - "lopar": "\u2985", - "lopf": "\U0001D55D", - "loplus": "\u2A2D", - "lotimes": "\u2A34", - "lowast": "\u2217", - "lowbar": "\u005F", - "loz": "\u25CA", - "lozenge": "\u25CA", - "lozf": "\u29EB", - "lpar": "\u0028", - "lparlt": "\u2993", - "lrarr": "\u21C6", - "lrcorner": "\u231F", - "lrhar": "\u21CB", - "lrhard": "\u296D", - "lrm": "\u200E", - "lrtri": "\u22BF", - "lsaquo": "\u2039", - "lscr": "\U0001D4C1", - "lsh": "\u21B0", - "lsim": "\u2272", - "lsime": "\u2A8D", - "lsimg": "\u2A8F", - "lsqb": "\u005B", - "lsquo": "\u2018", - "lsquor": "\u201A", - "lstrok": "\u0142", - "lt": "\u003C", - "ltcc": "\u2AA6", - "ltcir": "\u2A79", - "ltdot": "\u22D6", - "lthree": "\u22CB", - "ltimes": "\u22C9", - "ltlarr": "\u2976", - "ltquest": "\u2A7B", - "ltrPar": "\u2996", - "ltri": "\u25C3", - "ltrie": "\u22B4", - "ltrif": "\u25C2", - "lurdshar": "\u294A", - "luruhar": "\u2966", - "lvertneqq": "\u2268\uFE00", - "lvnE": "\u2268\uFE00", - "mDDot": "\u223A", - "macr": "\u00AF", - "male": "\u2642", - "malt": "\u2720", - "maltese": "\u2720", - "map": "\u21A6", - "mapsto": "\u21A6", - "mapstodown": "\u21A7", - "mapstoleft": "\u21A4", - "mapstoup": "\u21A5", - "marker": "\u25AE", - "mcomma": "\u2A29", - "mcy": "\u043C", - "mdash": "\u2014", - "measuredangle": "\u2221", - "mfr": "\U0001D52A", - "mho": "\u2127", - "micro": "\u00B5", - "mid": "\u2223", - "midast": "\u002A", - "midcir": "\u2AF0", - "middot": "\u00B7", - "minus": "\u2212", - "minusb": "\u229F", - "minusd": "\u2238", - "minusdu": "\u2A2A", - "mlcp": "\u2ADB", - "mldr": "\u2026", - "mnplus": "\u2213", - "models": "\u22A7", - "mopf": "\U0001D55E", - "mp": "\u2213", - "mscr": "\U0001D4C2", - "mstpos": "\u223E", - "mu": "\u03BC", - "multimap": "\u22B8", - "mumap": "\u22B8", - "nGg": "\u22D9\u0338", - "nGt": "\u226B\u20D2", - "nGtv": "\u226B\u0338", - "nLeftarrow": "\u21CD", - "nLeftrightarrow": "\u21CE", - "nLl": "\u22D8\u0338", - "nLt": "\u226A\u20D2", - "nLtv": "\u226A\u0338", - "nRightarrow": "\u21CF", - "nVDash": "\u22AF", - "nVdash": "\u22AE", - "nabla": "\u2207", - "nacute": "\u0144", - "nang": "\u2220\u20D2", - "nap": "\u2249", - "napE": "\u2A70\u0338", - "napid": "\u224B\u0338", - "napos": "\u0149", - "napprox": "\u2249", - "natur": "\u266E", - "natural": "\u266E", - "naturals": "\u2115", - "nbsp": "\u00A0", - "nbump": "\u224E\u0338", - "nbumpe": "\u224F\u0338", - "ncap": "\u2A43", - "ncaron": "\u0148", - "ncedil": "\u0146", - "ncong": "\u2247", - "ncongdot": "\u2A6D\u0338", - "ncup": "\u2A42", - "ncy": "\u043D", - "ndash": "\u2013", - "ne": "\u2260", - "neArr": "\u21D7", - "nearhk": "\u2924", - "nearr": "\u2197", - "nearrow": "\u2197", - "nedot": "\u2250\u0338", - "nequiv": "\u2262", - "nesear": "\u2928", - "nesim": "\u2242\u0338", - "nexist": "\u2204", - "nexists": "\u2204", - "nfr": "\U0001D52B", - "ngE": "\u2267\u0338", - "nge": "\u2271", - "ngeq": "\u2271", - "ngeqq": "\u2267\u0338", - "ngeqslant": "\u2A7E\u0338", - "nges": "\u2A7E\u0338", - "ngsim": "\u2275", - "ngt": "\u226F", - "ngtr": "\u226F", - "nhArr": "\u21CE", - "nharr": "\u21AE", - "nhpar": "\u2AF2", - "ni": "\u220B", - "nis": "\u22FC", - "nisd": "\u22FA", - "niv": "\u220B", - "njcy": "\u045A", - "nlArr": "\u21CD", - "nlE": "\u2266\u0338", - "nlarr": "\u219A", - "nldr": "\u2025", - "nle": "\u2270", - "nleftarrow": "\u219A", - "nleftrightarrow": "\u21AE", - "nleq": "\u2270", - "nleqq": "\u2266\u0338", - "nleqslant": "\u2A7D\u0338", - "nles": "\u2A7D\u0338", - "nless": "\u226E", - "nlsim": "\u2274", - "nlt": "\u226E", - "nltri": "\u22EA", - "nltrie": "\u22EC", - "nmid": "\u2224", - "nopf": "\U0001D55F", - "not": "\u00AC", - "notin": "\u2209", - "notinE": "\u22F9\u0338", - "notindot": "\u22F5\u0338", - "notinva": "\u2209", - "notinvb": "\u22F7", - "notinvc": "\u22F6", - "notni": "\u220C", - "notniva": "\u220C", - "notnivb": "\u22FE", - "notnivc": "\u22FD", - "npar": "\u2226", - "nparallel": "\u2226", - "nparsl": "\u2AFD\u20E5", - "npart": "\u2202\u0338", - "npolint": "\u2A14", - "npr": "\u2280", - "nprcue": "\u22E0", - "npre": "\u2AAF\u0338", - "nprec": "\u2280", - "npreceq": "\u2AAF\u0338", - "nrArr": "\u21CF", - "nrarr": "\u219B", - "nrarrc": "\u2933\u0338", - "nrarrw": "\u219D\u0338", - "nrightarrow": "\u219B", - "nrtri": "\u22EB", - "nrtrie": "\u22ED", - "nsc": "\u2281", - "nsccue": "\u22E1", - "nsce": "\u2AB0\u0338", - "nscr": "\U0001D4C3", - "nshortmid": "\u2224", - "nshortparallel": "\u2226", - "nsim": "\u2241", - "nsime": "\u2244", - "nsimeq": "\u2244", - "nsmid": "\u2224", - "nspar": "\u2226", - "nsqsube": "\u22E2", - "nsqsupe": "\u22E3", - "nsub": "\u2284", - "nsubE": "\u2AC5\u0338", - "nsube": "\u2288", - "nsubset": "\u2282\u20D2", - "nsubseteq": "\u2288", - "nsubseteqq": "\u2AC5\u0338", - "nsucc": "\u2281", - "nsucceq": "\u2AB0\u0338", - "nsup": "\u2285", - "nsupE": "\u2AC6\u0338", - "nsupe": "\u2289", - "nsupset": "\u2283\u20D2", - "nsupseteq": "\u2289", - "nsupseteqq": "\u2AC6\u0338", - "ntgl": "\u2279", - "ntilde": "\u00F1", - "ntlg": "\u2278", - "ntriangleleft": "\u22EA", - "ntrianglelefteq": "\u22EC", - "ntriangleright": "\u22EB", - "ntrianglerighteq": "\u22ED", - "nu": "\u03BD", - "num": "\u0023", - "numero": "\u2116", - "numsp": "\u2007", - "nvDash": "\u22AD", - "nvHarr": "\u2904", - "nvap": "\u224D\u20D2", - "nvdash": "\u22AC", - "nvge": "\u2265\u20D2", - "nvgt": "\u003E\u20D2", - "nvinfin": "\u29DE", - "nvlArr": "\u2902", - "nvle": "\u2264\u20D2", - "nvlt": "\u003C\u20D2", - "nvltrie": "\u22B4\u20D2", - "nvrArr": "\u2903", - "nvrtrie": "\u22B5\u20D2", - "nvsim": "\u223C\u20D2", - "nwArr": "\u21D6", - "nwarhk": "\u2923", - "nwarr": "\u2196", - "nwarrow": "\u2196", - "nwnear": "\u2927", - "oS": "\u24C8", - "oacute": "\u00F3", - "oast": "\u229B", - "ocir": "\u229A", - "ocirc": "\u00F4", - "ocy": "\u043E", - "odash": "\u229D", - "odblac": "\u0151", - "odiv": "\u2A38", - "odot": "\u2299", - "odsold": "\u29BC", - "oelig": "\u0153", - "ofcir": "\u29BF", - "ofr": "\U0001D52C", - "ogon": "\u02DB", - "ograve": "\u00F2", - "ogt": "\u29C1", - "ohbar": "\u29B5", - "ohm": "\u03A9", - "oint": "\u222E", - "olarr": "\u21BA", - "olcir": "\u29BE", - "olcross": "\u29BB", - "oline": "\u203E", - "olt": "\u29C0", - "omacr": "\u014D", - "omega": "\u03C9", - "omicron": "\u03BF", - "omid": "\u29B6", - "ominus": "\u2296", - "oopf": "\U0001D560", - "opar": "\u29B7", - "operp": "\u29B9", - "oplus": "\u2295", - "or": "\u2228", - "orarr": "\u21BB", - "ord": "\u2A5D", - "order": "\u2134", - "orderof": "\u2134", - "ordf": "\u00AA", - "ordm": "\u00BA", - "origof": "\u22B6", - "oror": "\u2A56", - "orslope": "\u2A57", - "orv": "\u2A5B", - "oscr": "\u2134", - "oslash": "\u00F8", - "osol": "\u2298", - "otilde": "\u00F5", - "otimes": "\u2297", - "otimesas": "\u2A36", - "ouml": "\u00F6", - "ovbar": "\u233D", - "par": "\u2225", - "para": "\u00B6", - "parallel": "\u2225", - "parsim": "\u2AF3", - "parsl": "\u2AFD", - "part": "\u2202", - "pcy": "\u043F", - "percnt": "\u0025", - "period": "\u002E", - "permil": "\u2030", - "perp": "\u22A5", - "pertenk": "\u2031", - "pfr": "\U0001D52D", - "phi": "\u03C6", - "phiv": "\u03D5", - "phmmat": "\u2133", - "phone": "\u260E", - "pi": "\u03C0", - "pitchfork": "\u22D4", - "piv": "\u03D6", - "planck": "\u210F", - "planckh": "\u210E", - "plankv": "\u210F", - "plus": "\u002B", - "plusacir": "\u2A23", - "plusb": "\u229E", - "pluscir": "\u2A22", - "plusdo": "\u2214", - "plusdu": "\u2A25", - "pluse": "\u2A72", - "plusmn": "\u00B1", - "plussim": "\u2A26", - "plustwo": "\u2A27", - "pm": "\u00B1", - "pointint": "\u2A15", - "popf": "\U0001D561", - "pound": "\u00A3", - "pr": "\u227A", - "prE": "\u2AB3", - "prap": "\u2AB7", - "prcue": "\u227C", - "pre": "\u2AAF", - "prec": "\u227A", - "precapprox": "\u2AB7", - "preccurlyeq": "\u227C", - "preceq": "\u2AAF", - "precnapprox": "\u2AB9", - "precneqq": "\u2AB5", - "precnsim": "\u22E8", - "precsim": "\u227E", - "prime": "\u2032", - "primes": "\u2119", - "prnE": "\u2AB5", - "prnap": "\u2AB9", - "prnsim": "\u22E8", - "prod": "\u220F", - "profalar": "\u232E", - "profline": "\u2312", - "profsurf": "\u2313", - "prop": "\u221D", - "propto": "\u221D", - "prsim": "\u227E", - "prurel": "\u22B0", - "pscr": "\U0001D4C5", - "psi": "\u03C8", - "puncsp": "\u2008", - "qfr": "\U0001D52E", - "qint": "\u2A0C", - "qopf": "\U0001D562", - "qprime": "\u2057", - "qscr": "\U0001D4C6", - "quaternions": "\u210D", - "quatint": "\u2A16", - "quest": "\u003F", - "questeq": "\u225F", - "quot": "\u0022", - "rAarr": "\u21DB", - "rArr": "\u21D2", - "rAtail": "\u291C", - "rBarr": "\u290F", - "rHar": "\u2964", - "race": "\u223D\u0331", - "racute": "\u0155", - "radic": "\u221A", - "raemptyv": "\u29B3", - "rang": "\u27E9", - "rangd": "\u2992", - "range": "\u29A5", - "rangle": "\u27E9", - "raquo": "\u00BB", - "rarr": "\u2192", - "rarrap": "\u2975", - "rarrb": "\u21E5", - "rarrbfs": "\u2920", - "rarrc": "\u2933", - "rarrfs": "\u291E", - "rarrhk": "\u21AA", - "rarrlp": "\u21AC", - "rarrpl": "\u2945", - "rarrsim": "\u2974", - "rarrtl": "\u21A3", - "rarrw": "\u219D", - "ratail": "\u291A", - "ratio": "\u2236", - "rationals": "\u211A", - "rbarr": "\u290D", - "rbbrk": "\u2773", - "rbrace": "\u007D", - "rbrack": "\u005D", - "rbrke": "\u298C", - "rbrksld": "\u298E", - "rbrkslu": "\u2990", - "rcaron": "\u0159", - "rcedil": "\u0157", - "rceil": "\u2309", - "rcub": "\u007D", - "rcy": "\u0440", - "rdca": "\u2937", - "rdldhar": "\u2969", - "rdquo": "\u201D", - "rdquor": "\u201D", - "rdsh": "\u21B3", - "real": "\u211C", - "realine": "\u211B", - "realpart": "\u211C", - "reals": "\u211D", - "rect": "\u25AD", - "reg": "\u00AE", - "rfisht": "\u297D", - "rfloor": "\u230B", - "rfr": "\U0001D52F", - "rhard": "\u21C1", - "rharu": "\u21C0", - "rharul": "\u296C", - "rho": "\u03C1", - "rhov": "\u03F1", - "rightarrow": "\u2192", - "rightarrowtail": "\u21A3", - "rightharpoondown": "\u21C1", - "rightharpoonup": "\u21C0", - "rightleftarrows": "\u21C4", - "rightleftharpoons": "\u21CC", - "rightrightarrows": "\u21C9", - "rightsquigarrow": "\u219D", - "rightthreetimes": "\u22CC", - "ring": "\u02DA", - "risingdotseq": "\u2253", - "rlarr": "\u21C4", - "rlhar": "\u21CC", - "rlm": "\u200F", - "rmoust": "\u23B1", - "rmoustache": "\u23B1", - "rnmid": "\u2AEE", - "roang": "\u27ED", - "roarr": "\u21FE", - "robrk": "\u27E7", - "ropar": "\u2986", - "ropf": "\U0001D563", - "roplus": "\u2A2E", - "rotimes": "\u2A35", - "rpar": "\u0029", - "rpargt": "\u2994", - "rppolint": "\u2A12", - "rrarr": "\u21C9", - "rsaquo": "\u203A", - "rscr": "\U0001D4C7", - "rsh": "\u21B1", - "rsqb": "\u005D", - "rsquo": "\u2019", - "rsquor": "\u2019", - "rthree": "\u22CC", - "rtimes": "\u22CA", - "rtri": "\u25B9", - "rtrie": "\u22B5", - "rtrif": "\u25B8", - "rtriltri": "\u29CE", - "ruluhar": "\u2968", - "rx": "\u211E", - "sacute": "\u015B", - "sbquo": "\u201A", - "sc": "\u227B", - "scE": "\u2AB4", - "scap": "\u2AB8", - "scaron": "\u0161", - "sccue": "\u227D", - "sce": "\u2AB0", - "scedil": "\u015F", - "scirc": "\u015D", - "scnE": "\u2AB6", - "scnap": "\u2ABA", - "scnsim": "\u22E9", - "scpolint": "\u2A13", - "scsim": "\u227F", - "scy": "\u0441", - "sdot": "\u22C5", - "sdotb": "\u22A1", - "sdote": "\u2A66", - "seArr": "\u21D8", - "searhk": "\u2925", - "searr": "\u2198", - "searrow": "\u2198", - "sect": "\u00A7", - "semi": "\u003B", - "seswar": "\u2929", - "setminus": "\u2216", - "setmn": "\u2216", - "sext": "\u2736", - "sfr": "\U0001D530", - "sfrown": "\u2322", - "sharp": "\u266F", - "shchcy": "\u0449", - "shcy": "\u0448", - "shortmid": "\u2223", - "shortparallel": "\u2225", - "shy": "\u00AD", - "sigma": "\u03C3", - "sigmaf": "\u03C2", - "sigmav": "\u03C2", - "sim": "\u223C", - "simdot": "\u2A6A", - "sime": "\u2243", - "simeq": "\u2243", - "simg": "\u2A9E", - "simgE": "\u2AA0", - "siml": "\u2A9D", - "simlE": "\u2A9F", - "simne": "\u2246", - "simplus": "\u2A24", - "simrarr": "\u2972", - "slarr": "\u2190", - "smallsetminus": "\u2216", - "smashp": "\u2A33", - "smeparsl": "\u29E4", - "smid": "\u2223", - "smile": "\u2323", - "smt": "\u2AAA", - "smte": "\u2AAC", - "smtes": "\u2AAC\uFE00", - "softcy": "\u044C", - "sol": "\u002F", - "solb": "\u29C4", - "solbar": "\u233F", - "sopf": "\U0001D564", - "spades": "\u2660", - "spadesuit": "\u2660", - "spar": "\u2225", - "sqcap": "\u2293", - "sqcaps": "\u2293\uFE00", - "sqcup": "\u2294", - "sqcups": "\u2294\uFE00", - "sqsub": "\u228F", - "sqsube": "\u2291", - "sqsubset": "\u228F", - "sqsubseteq": "\u2291", - "sqsup": "\u2290", - "sqsupe": "\u2292", - "sqsupset": "\u2290", - "sqsupseteq": "\u2292", - "squ": "\u25A1", - "square": "\u25A1", - "squarf": "\u25AA", - "squf": "\u25AA", - "srarr": "\u2192", - "sscr": "\U0001D4C8", - "ssetmn": "\u2216", - "ssmile": "\u2323", - "sstarf": "\u22C6", - "star": "\u2606", - "starf": "\u2605", - "straightepsilon": "\u03F5", - "straightphi": "\u03D5", - "strns": "\u00AF", - "sub": "\u2282", - "subE": "\u2AC5", - "subdot": "\u2ABD", - "sube": "\u2286", - "subedot": "\u2AC3", - "submult": "\u2AC1", - "subnE": "\u2ACB", - "subne": "\u228A", - "subplus": "\u2ABF", - "subrarr": "\u2979", - "subset": "\u2282", - "subseteq": "\u2286", - "subseteqq": "\u2AC5", - "subsetneq": "\u228A", - "subsetneqq": "\u2ACB", - "subsim": "\u2AC7", - "subsub": "\u2AD5", - "subsup": "\u2AD3", - "succ": "\u227B", - "succapprox": "\u2AB8", - "succcurlyeq": "\u227D", - "succeq": "\u2AB0", - "succnapprox": "\u2ABA", - "succneqq": "\u2AB6", - "succnsim": "\u22E9", - "succsim": "\u227F", - "sum": "\u2211", - "sung": "\u266A", - "sup": "\u2283", - "sup1": "\u00B9", - "sup2": "\u00B2", - "sup3": "\u00B3", - "supE": "\u2AC6", - "supdot": "\u2ABE", - "supdsub": "\u2AD8", - "supe": "\u2287", - "supedot": "\u2AC4", - "suphsol": "\u27C9", - "suphsub": "\u2AD7", - "suplarr": "\u297B", - "supmult": "\u2AC2", - "supnE": "\u2ACC", - "supne": "\u228B", - "supplus": "\u2AC0", - "supset": "\u2283", - "supseteq": "\u2287", - "supseteqq": "\u2AC6", - "supsetneq": "\u228B", - "supsetneqq": "\u2ACC", - "supsim": "\u2AC8", - "supsub": "\u2AD4", - "supsup": "\u2AD6", - "swArr": "\u21D9", - "swarhk": "\u2926", - "swarr": "\u2199", - "swarrow": "\u2199", - "swnwar": "\u292A", - "szlig": "\u00DF", - "target": "\u2316", - "tau": "\u03C4", - "tbrk": "\u23B4", - "tcaron": "\u0165", - "tcedil": "\u0163", - "tcy": "\u0442", - "tdot": "\u20DB", - "telrec": "\u2315", - "tfr": "\U0001D531", - "there4": "\u2234", - "therefore": "\u2234", - "theta": "\u03B8", - "thetasym": "\u03D1", - "thetav": "\u03D1", - "thickapprox": "\u2248", - "thicksim": "\u223C", - "thinsp": "\u2009", - "thkap": "\u2248", - "thksim": "\u223C", - "thorn": "\u00FE", - "tilde": "\u02DC", - "times": "\u00D7", - "timesb": "\u22A0", - "timesbar": "\u2A31", - "timesd": "\u2A30", - "tint": "\u222D", - "toea": "\u2928", - "top": "\u22A4", - "topbot": "\u2336", - "topcir": "\u2AF1", - "topf": "\U0001D565", - "topfork": "\u2ADA", - "tosa": "\u2929", - "tprime": "\u2034", - "trade": "\u2122", - "triangle": "\u25B5", - "triangledown": "\u25BF", - "triangleleft": "\u25C3", - "trianglelefteq": "\u22B4", - "triangleq": "\u225C", - "triangleright": "\u25B9", - "trianglerighteq": "\u22B5", - "tridot": "\u25EC", - "trie": "\u225C", - "triminus": "\u2A3A", - "triplus": "\u2A39", - "trisb": "\u29CD", - "tritime": "\u2A3B", - "trpezium": "\u23E2", - "tscr": "\U0001D4C9", - "tscy": "\u0446", - "tshcy": "\u045B", - "tstrok": "\u0167", - "twixt": "\u226C", - "twoheadleftarrow": "\u219E", - "twoheadrightarrow": "\u21A0", - "uArr": "\u21D1", - "uHar": "\u2963", - "uacute": "\u00FA", - "uarr": "\u2191", - "ubrcy": "\u045E", - "ubreve": "\u016D", - "ucirc": "\u00FB", - "ucy": "\u0443", - "udarr": "\u21C5", - "udblac": "\u0171", - "udhar": "\u296E", - "ufisht": "\u297E", - "ufr": "\U0001D532", - "ugrave": "\u00F9", - "uharl": "\u21BF", - "uharr": "\u21BE", - "uhblk": "\u2580", - "ulcorn": "\u231C", - "ulcorner": "\u231C", - "ulcrop": "\u230F", - "ultri": "\u25F8", - "umacr": "\u016B", - "uml": "\u00A8", - "uogon": "\u0173", - "uopf": "\U0001D566", - "uparrow": "\u2191", - "updownarrow": "\u2195", - "upharpoonleft": "\u21BF", - "upharpoonright": "\u21BE", - "uplus": "\u228E", - "upsi": "\u03C5", - "upsih": "\u03D2", - "upsilon": "\u03C5", - "upuparrows": "\u21C8", - "urcorn": "\u231D", - "urcorner": "\u231D", - "urcrop": "\u230E", - "uring": "\u016F", - "urtri": "\u25F9", - "uscr": "\U0001D4CA", - "utdot": "\u22F0", - "utilde": "\u0169", - "utri": "\u25B5", - "utrif": "\u25B4", - "uuarr": "\u21C8", - "uuml": "\u00FC", - "uwangle": "\u29A7", - "vArr": "\u21D5", - "vBar": "\u2AE8", - "vBarv": "\u2AE9", - "vDash": "\u22A8", - "vangrt": "\u299C", - "varepsilon": "\u03F5", - "varkappa": "\u03F0", - "varnothing": "\u2205", - "varphi": "\u03D5", - "varpi": "\u03D6", - "varpropto": "\u221D", - "varr": "\u2195", - "varrho": "\u03F1", - "varsigma": "\u03C2", - "varsubsetneq": "\u228A\uFE00", - "varsubsetneqq": "\u2ACB\uFE00", - "varsupsetneq": "\u228B\uFE00", - "varsupsetneqq": "\u2ACC\uFE00", - "vartheta": "\u03D1", - "vartriangleleft": "\u22B2", - "vartriangleright": "\u22B3", - "vcy": "\u0432", - "vdash": "\u22A2", - "vee": "\u2228", - "veebar": "\u22BB", - "veeeq": "\u225A", - "vellip": "\u22EE", - "verbar": "\u007C", - "vert": "\u007C", - "vfr": "\U0001D533", - "vltri": "\u22B2", - "vnsub": "\u2282\u20D2", - "vnsup": "\u2283\u20D2", - "vopf": "\U0001D567", - "vprop": "\u221D", - "vrtri": "\u22B3", - "vscr": "\U0001D4CB", - "vsubnE": "\u2ACB\uFE00", - "vsubne": "\u228A\uFE00", - "vsupnE": "\u2ACC\uFE00", - "vsupne": "\u228B\uFE00", - "vzigzag": "\u299A", - "wcirc": "\u0175", - "wedbar": "\u2A5F", - "wedge": "\u2227", - "wedgeq": "\u2259", - "weierp": "\u2118", - "wfr": "\U0001D534", - "wopf": "\U0001D568", - "wp": "\u2118", - "wr": "\u2240", - "wreath": "\u2240", - "wscr": "\U0001D4CC", - "xcap": "\u22C2", - "xcirc": "\u25EF", - "xcup": "\u22C3", - "xdtri": "\u25BD", - "xfr": "\U0001D535", - "xhArr": "\u27FA", - "xharr": "\u27F7", - "xi": "\u03BE", - "xlArr": "\u27F8", - "xlarr": "\u27F5", - "xmap": "\u27FC", - "xnis": "\u22FB", - "xodot": "\u2A00", - "xopf": "\U0001D569", - "xoplus": "\u2A01", - "xotime": "\u2A02", - "xrArr": "\u27F9", - "xrarr": "\u27F6", - "xscr": "\U0001D4CD", - "xsqcup": "\u2A06", - "xuplus": "\u2A04", - "xutri": "\u25B3", - "xvee": "\u22C1", - "xwedge": "\u22C0", - "yacute": "\u00FD", - "yacy": "\u044F", - "ycirc": "\u0177", - "ycy": "\u044B", - "yen": "\u00A5", - "yfr": "\U0001D536", - "yicy": "\u0457", - "yopf": "\U0001D56A", - "yscr": "\U0001D4CE", - "yucy": "\u044E", - "yuml": "\u00FF", - "zacute": "\u017A", - "zcaron": "\u017E", - "zcy": "\u0437", - "zdot": "\u017C", - "zeetrf": "\u2128", - "zeta": "\u03B6", - "zfr": "\U0001D537", - "zhcy": "\u0436", - "zigrarr": "\u21DD", - "zopf": "\U0001D56B", - "zscr": "\U0001D4CF", - "zwj": "\u200D", - "zwnj": "\u200C", -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/indented_code.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/indented_code.go deleted file mode 100644 index a89ee6c7..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/indented_code.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type IndentedCodeLine struct { - Indentation int - Range Range -} - -type IndentedCode struct { - blockBase - markdown string - - RawCode []IndentedCodeLine -} - -func (b *IndentedCode) Code() (result string) { - for _, code := range b.RawCode { - result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End] - } - return -} - -func (b *IndentedCode) Continuation(indentation int, r Range) *continuation { - if indentation >= 4 { - return &continuation{ - Indentation: indentation - 4, - Remaining: r, - } - } - s := b.markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - return &continuation{ - Remaining: r, - } - } - return nil -} - -func (b *IndentedCode) AddLine(indentation int, r Range) bool { - b.RawCode = append(b.RawCode, IndentedCodeLine{ - Indentation: indentation, - Range: r, - }) - return true -} - -func (b *IndentedCode) Close() { - for { - last := b.RawCode[len(b.RawCode)-1] - s := b.markdown[last.Range.Position:last.Range.End] - if strings.TrimRight(s, "\r\n") == "" { - b.RawCode = b.RawCode[:len(b.RawCode)-1] - } else { - break - } - } -} - -func (b *IndentedCode) AllowsBlockStarts() bool { - return false -} - -func indentedCodeStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { - if len(unmatchedBlocks) > 0 { - if _, ok := unmatchedBlocks[len(unmatchedBlocks)-1].(*Paragraph); ok { - return nil - } - } else if len(matchedBlocks) > 0 { - if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok { - return nil - } - } - - if indentation < 4 { - return nil - } - - s := markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - return nil - } - - return []Block{ - &IndentedCode{ - markdown: markdown, - RawCode: []IndentedCodeLine{{ - Indentation: indentation - 4, - Range: r, - }}, - }, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inlines.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inlines.go deleted file mode 100644 index 43dee3bd..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inlines.go +++ /dev/null @@ -1,663 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "container/list" - "strings" - "unicode" - "unicode/utf8" -) - -type Inline interface { - IsInline() bool -} - -type inlineBase struct{} - -func (inlineBase) IsInline() bool { return true } - -type Text struct { - inlineBase - - Text string - Range Range -} - -type CodeSpan struct { - inlineBase - - Code string -} - -type HardLineBreak struct { - inlineBase -} - -type SoftLineBreak struct { - inlineBase -} - -type InlineLinkOrImage struct { - inlineBase - - Children []Inline - - RawDestination Range - - markdown string - rawTitle string -} - -func (i *InlineLinkOrImage) Destination() string { - return Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End]) -} - -func (i *InlineLinkOrImage) Title() string { - return Unescape(i.rawTitle) -} - -type InlineLink struct { - InlineLinkOrImage -} - -type InlineImage struct { - InlineLinkOrImage -} - -type ReferenceLinkOrImage struct { - inlineBase - *ReferenceDefinition - - Children []Inline -} - -type ReferenceLink struct { - ReferenceLinkOrImage -} - -type ReferenceImage struct { - ReferenceLinkOrImage -} - -type Autolink struct { - inlineBase - - Children []Inline - - RawDestination Range - - markdown string -} - -func (i *Autolink) Destination() string { - destination := Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End]) - - if strings.HasPrefix(destination, "www") { - destination = "http://" + destination - } - - return destination -} - -type delimiterType int - -const ( - linkOpeningDelimiter delimiterType = iota - imageOpeningDelimiter -) - -type delimiter struct { - Type delimiterType - IsInactive bool - TextNode int - Range Range -} - -type inlineParser struct { - markdown string - ranges []Range - referenceDefinitions []*ReferenceDefinition - - raw string - position int - inlines []Inline - delimiterStack *list.List -} - -func newInlineParser(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) *inlineParser { - return &inlineParser{ - markdown: markdown, - ranges: ranges, - referenceDefinitions: referenceDefinitions, - delimiterStack: list.New(), - } -} - -func (p *inlineParser) parseBackticks() { - count := 1 - for i := p.position + 1; i < len(p.raw) && p.raw[i] == '`'; i++ { - count++ - } - opening := p.raw[p.position : p.position+count] - search := p.position + count - for search < len(p.raw) { - end := strings.Index(p.raw[search:], opening) - if end == -1 { - break - } - if search+end+count < len(p.raw) && p.raw[search+end+count] == '`' { - search += end + count - for search < len(p.raw) && p.raw[search] == '`' { - search++ - } - continue - } - code := strings.Join(strings.Fields(p.raw[p.position+count:search+end]), " ") - p.position = search + end + count - p.inlines = append(p.inlines, &CodeSpan{ - Code: code, - }) - return - } - p.position += len(opening) - absPos := relativeToAbsolutePosition(p.ranges, p.position-len(opening)) - p.inlines = append(p.inlines, &Text{ - Text: opening, - Range: Range{absPos, absPos + len(opening)}, - }) -} - -func (p *inlineParser) parseLineEnding() { - if p.position >= 1 && p.raw[p.position-1] == '\t' { - p.inlines = append(p.inlines, &HardLineBreak{}) - } else if p.position >= 2 && p.raw[p.position-1] == ' ' && (p.raw[p.position-2] == '\t' || p.raw[p.position-1] == ' ') { - p.inlines = append(p.inlines, &HardLineBreak{}) - } else { - p.inlines = append(p.inlines, &SoftLineBreak{}) - } - p.position++ - if p.position < len(p.raw) && p.raw[p.position] == '\n' { - p.position++ - } -} - -func (p *inlineParser) parseEscapeCharacter() { - if p.position+1 < len(p.raw) && isEscapableByte(p.raw[p.position+1]) { - absPos := relativeToAbsolutePosition(p.ranges, p.position+1) - p.inlines = append(p.inlines, &Text{ - Text: string(p.raw[p.position+1]), - Range: Range{absPos, absPos + len(string(p.raw[p.position+1]))}, - }) - p.position += 2 - } else { - absPos := relativeToAbsolutePosition(p.ranges, p.position) - p.inlines = append(p.inlines, &Text{ - Text: `\`, - Range: Range{absPos, absPos + 1}, - }) - p.position++ - } -} - -func (p *inlineParser) parseText() { - if next := strings.IndexAny(p.raw[p.position:], "\r\n\\`&![]wW:"); next == -1 { - absPos := relativeToAbsolutePosition(p.ranges, p.position) - p.inlines = append(p.inlines, &Text{ - Text: strings.TrimRightFunc(p.raw[p.position:], isWhitespace), - Range: Range{absPos, absPos + len(p.raw[p.position:])}, - }) - p.position = len(p.raw) - } else { - absPos := relativeToAbsolutePosition(p.ranges, p.position) - if p.raw[p.position+next] == '\r' || p.raw[p.position+next] == '\n' { - s := strings.TrimRightFunc(p.raw[p.position:p.position+next], isWhitespace) - p.inlines = append(p.inlines, &Text{ - Text: s, - Range: Range{absPos, absPos + len(s)}, - }) - } else { - if next == 0 { - // Always read at least one character since 'w', 'W', and ':' may not actually match another - // type of node - next = 1 - } - - p.inlines = append(p.inlines, &Text{ - Text: p.raw[p.position : p.position+next], - Range: Range{absPos, absPos + next}, - }) - } - p.position += next - } -} - -func (p *inlineParser) parseLinkOrImageDelimiter() { - absPos := relativeToAbsolutePosition(p.ranges, p.position) - if p.raw[p.position] == '[' { - p.inlines = append(p.inlines, &Text{ - Text: "[", - Range: Range{absPos, absPos + 1}, - }) - p.delimiterStack.PushBack(&delimiter{ - Type: linkOpeningDelimiter, - TextNode: len(p.inlines) - 1, - Range: Range{p.position, p.position + 1}, - }) - p.position++ - } else if p.raw[p.position] == '!' && p.position+1 < len(p.raw) && p.raw[p.position+1] == '[' { - p.inlines = append(p.inlines, &Text{ - Text: "![", - Range: Range{absPos, absPos + 2}, - }) - p.delimiterStack.PushBack(&delimiter{ - Type: imageOpeningDelimiter, - TextNode: len(p.inlines) - 1, - Range: Range{p.position, p.position + 2}, - }) - p.position += 2 - } else { - p.inlines = append(p.inlines, &Text{ - Text: "!", - Range: Range{absPos, absPos + 1}, - }) - p.position++ - } -} - -func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int, isImage bool) (destination, title Range, end int, ok bool) { - if position >= len(p.raw) || p.raw[position] != '(' { - return - } - position++ - - destinationStart := nextNonWhitespace(p.raw, position) - if destinationStart >= len(p.raw) { - return - } else if p.raw[destinationStart] == ')' { - return Range{destinationStart, destinationStart}, Range{destinationStart, destinationStart}, destinationStart + 1, true - } - - destination, end, ok = parseLinkDestination(p.raw, destinationStart) - if !ok { - return - } - position = end - - if isImage && position < len(p.raw) && isWhitespaceByte(p.raw[position]) { - dimensionsStart := nextNonWhitespace(p.raw, position) - if dimensionsStart >= len(p.raw) { - return - } - - if p.raw[dimensionsStart] == '=' { - // Read optional image dimensions even if we don't use them - _, end, ok = parseImageDimensions(p.raw, dimensionsStart) - if !ok { - return - } - - position = end - } - } - - if position < len(p.raw) && isWhitespaceByte(p.raw[position]) { - titleStart := nextNonWhitespace(p.raw, position) - if titleStart >= len(p.raw) { - return - } else if p.raw[titleStart] == ')' { - return destination, Range{titleStart, titleStart}, titleStart + 1, true - } - - if p.raw[titleStart] == '"' || p.raw[titleStart] == '\'' || p.raw[titleStart] == '(' { - title, end, ok = parseLinkTitle(p.raw, titleStart) - if !ok { - return - } - position = end - } - } - - closingPosition := nextNonWhitespace(p.raw, position) - if closingPosition >= len(p.raw) || p.raw[closingPosition] != ')' { - return Range{}, Range{}, 0, false - } - - return destination, title, closingPosition + 1, true -} - -func (p *inlineParser) referenceDefinition(label string) *ReferenceDefinition { - clean := strings.Join(strings.Fields(label), " ") - for _, d := range p.referenceDefinitions { - if strings.EqualFold(clean, strings.Join(strings.Fields(d.Label()), " ")) { - return d - } - } - return nil -} - -func (p *inlineParser) lookForLinkOrImage() { - for element := p.delimiterStack.Back(); element != nil; element = element.Prev() { - d := element.Value.(*delimiter) - if d.Type != imageOpeningDelimiter && d.Type != linkOpeningDelimiter { - continue - } - if d.IsInactive { - p.delimiterStack.Remove(element) - break - } - - isImage := d.Type == imageOpeningDelimiter - - var inline Inline - - if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position+1, isImage); ok { - destinationMarkdownPosition := relativeToAbsolutePosition(p.ranges, destination.Position) - linkOrImage := InlineLinkOrImage{ - Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...), - RawDestination: Range{destinationMarkdownPosition, destinationMarkdownPosition + destination.End - destination.Position}, - markdown: p.markdown, - rawTitle: p.raw[title.Position:title.End], - } - if d.Type == imageOpeningDelimiter { - inline = &InlineImage{linkOrImage} - } else { - inline = &InlineLink{linkOrImage} - } - p.position = next - } else { - referenceLabel := "" - label, next, hasLinkLabel := parseLinkLabel(p.raw, p.position+1) - if hasLinkLabel && label.End > label.Position { - referenceLabel = p.raw[label.Position:label.End] - } else { - referenceLabel = p.raw[d.Range.End:p.position] - if !hasLinkLabel { - next = p.position + 1 - } - } - if referenceLabel != "" { - if reference := p.referenceDefinition(referenceLabel); reference != nil { - linkOrImage := ReferenceLinkOrImage{ - ReferenceDefinition: reference, - Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...), - } - if d.Type == imageOpeningDelimiter { - inline = &ReferenceImage{linkOrImage} - } else { - inline = &ReferenceLink{linkOrImage} - } - p.position = next - } - } - } - - if inline != nil { - if d.Type == imageOpeningDelimiter { - p.inlines = append(p.inlines[:d.TextNode], inline) - } else { - p.inlines = append(p.inlines[:d.TextNode], inline) - for inlineElement := element.Prev(); inlineElement != nil; inlineElement = inlineElement.Prev() { - if d := inlineElement.Value.(*delimiter); d.Type == linkOpeningDelimiter { - d.IsInactive = true - } - } - } - p.delimiterStack.Remove(element) - return - } - p.delimiterStack.Remove(element) - break - } - absPos := relativeToAbsolutePosition(p.ranges, p.position) - p.inlines = append(p.inlines, &Text{ - Text: "]", - Range: Range{absPos, absPos + 1}, - }) - p.position++ -} - -func CharacterReference(ref string) string { - if ref == "" { - return "" - } - if ref[0] == '#' { - if len(ref) < 2 { - return "" - } - n := 0 - if ref[1] == 'X' || ref[1] == 'x' { - if len(ref) < 3 { - return "" - } - for i := 2; i < len(ref); i++ { - if i > 9 { - return "" - } - d := ref[i] - switch { - case d >= '0' && d <= '9': - n = n*16 + int(d-'0') - case d >= 'a' && d <= 'f': - n = n*16 + 10 + int(d-'a') - case d >= 'A' && d <= 'F': - n = n*16 + 10 + int(d-'A') - default: - return "" - } - } - } else { - for i := 1; i < len(ref); i++ { - if i > 8 || ref[i] < '0' || ref[i] > '9' { - return "" - } - n = n*10 + int(ref[i]-'0') - } - } - c := rune(n) - if c == '\u0000' || !utf8.ValidRune(c) { - return string(unicode.ReplacementChar) - } - return string(c) - } - if entity, ok := htmlEntities[ref]; ok { - return entity - } - return "" -} - -func (p *inlineParser) parseCharacterReference() { - absPos := relativeToAbsolutePosition(p.ranges, p.position) - p.position++ - if semicolon := strings.IndexByte(p.raw[p.position:], ';'); semicolon == -1 { - p.inlines = append(p.inlines, &Text{ - Text: "&", - Range: Range{absPos, absPos + 1}, - }) - } else if s := CharacterReference(p.raw[p.position : p.position+semicolon]); s != "" { - p.position += semicolon + 1 - p.inlines = append(p.inlines, &Text{ - Text: s, - Range: Range{absPos, absPos + len(s)}, - }) - } else { - p.inlines = append(p.inlines, &Text{ - Text: "&", - Range: Range{absPos, absPos + 1}, - }) - } -} - -func (p *inlineParser) parseAutolink(c rune) bool { - for element := p.delimiterStack.Back(); element != nil; element = element.Prev() { - d := element.Value.(*delimiter) - if !d.IsInactive { - return false - } - } - - var link Range - if c == ':' { - var ok bool - link, ok = parseURLAutolink(p.raw, p.position) - - if !ok { - return false - } - - // Since the current position is at the colon, we have to rewind the parsing slightly so that - // we don't duplicate the URL scheme - rewind := strings.Index(p.raw[link.Position:link.End], ":") - if rewind != -1 { - lastInline := p.inlines[len(p.inlines)-1] - lastText, ok := lastInline.(*Text) - - if !ok { - // This should never occur since parseURLAutolink will only return a non-empty value - // when the previous text ends in a valid URL protocol which would mean that the previous - // node is a Text node - return false - } - - p.inlines = p.inlines[0 : len(p.inlines)-1] - p.inlines = append(p.inlines, &Text{ - Text: lastText.Text[:len(lastText.Text)-rewind], - Range: Range{lastText.Range.Position, lastText.Range.End - rewind}, - }) - p.position -= rewind - } - } else if c == 'w' || c == 'W' { - var ok bool - link, ok = parseWWWAutolink(p.raw, p.position) - - if !ok { - return false - } - } - - linkMarkdownPosition := relativeToAbsolutePosition(p.ranges, link.Position) - linkRange := Range{linkMarkdownPosition, linkMarkdownPosition + link.End - link.Position} - - p.inlines = append(p.inlines, &Autolink{ - Children: []Inline{ - &Text{ - Text: p.raw[link.Position:link.End], - Range: linkRange, - }, - }, - RawDestination: linkRange, - markdown: p.markdown, - }) - p.position += (link.End - link.Position) - - return true -} - -func (p *inlineParser) Parse() []Inline { - for _, r := range p.ranges { - p.raw += p.markdown[r.Position:r.End] - } - - for p.position < len(p.raw) { - c, _ := utf8.DecodeRuneInString(p.raw[p.position:]) - - switch c { - case '\r', '\n': - p.parseLineEnding() - case '\\': - p.parseEscapeCharacter() - case '`': - p.parseBackticks() - case '&': - p.parseCharacterReference() - case '!', '[': - p.parseLinkOrImageDelimiter() - case ']': - p.lookForLinkOrImage() - case 'w', 'W', ':': - matched := p.parseAutolink(c) - - if !matched { - p.parseText() - } - default: - p.parseText() - } - } - - return p.inlines -} - -func ParseInlines(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) (inlines []Inline) { - return newInlineParser(markdown, ranges, referenceDefinitions).Parse() -} - -func MergeInlineText(inlines []Inline) []Inline { - ret := inlines[:0] - for i, v := range inlines { - // always add first node - if i == 0 { - ret = append(ret, v) - continue - } - // not a text node? nothing to merge - text, ok := v.(*Text) - if !ok { - ret = append(ret, v) - continue - } - // previous node is not a text node? nothing to merge - prevText, ok := ret[len(ret)-1].(*Text) - if !ok { - ret = append(ret, v) - continue - } - // previous node is not right before this one - if prevText.Range.End != text.Range.Position { - ret = append(ret, v) - continue - } - // we have two consecutive text nodes - ret[len(ret)-1] = &Text{ - Text: prevText.Text + text.Text, - Range: Range{prevText.Range.Position, text.Range.End}, - } - } - return ret -} - -func Unescape(markdown string) string { - ret := "" - - position := 0 - for position < len(markdown) { - c, cSize := utf8.DecodeRuneInString(markdown[position:]) - - switch c { - case '\\': - if position+1 < len(markdown) && isEscapableByte(markdown[position+1]) { - ret += string(markdown[position+1]) - position += 2 - } else { - ret += `\` - position++ - } - case '&': - position++ - if semicolon := strings.IndexByte(markdown[position:], ';'); semicolon == -1 { - ret += "&" - } else if s := CharacterReference(markdown[position : position+semicolon]); s != "" { - position += semicolon + 1 - ret += s - } else { - ret += "&" - } - default: - ret += string(c) - position += cSize - } - } - - return ret -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inspect.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inspect.go deleted file mode 100644 index 3c7f2d1c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/inspect.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -// Inspect traverses the markdown tree in depth-first order. If f returns true, Inspect invokes f -// recursively for each child of the block or inline, followed by a call of f(nil). -func Inspect(markdown string, f func(interface{}) bool) { - document, referenceDefinitions := Parse(markdown) - InspectBlock(document, func(block Block) bool { - if !f(block) { - return false - } - switch v := block.(type) { - case *Paragraph: - for _, inline := range MergeInlineText(v.ParseInlines(referenceDefinitions)) { - InspectInline(inline, func(inline Inline) bool { - return f(inline) - }) - } - } - return true - }) -} - -// InspectBlock traverses the blocks in depth-first order, starting with block. If f returns true, -// InspectBlock invokes f recursively for each child of the block, followed by a call of f(nil). -func InspectBlock(block Block, f func(Block) bool) { - if !f(block) { - return - } - switch v := block.(type) { - case *Document: - for _, child := range v.Children { - InspectBlock(child, f) - } - case *List: - for _, child := range v.Children { - InspectBlock(child, f) - } - case *ListItem: - for _, child := range v.Children { - InspectBlock(child, f) - } - case *BlockQuote: - for _, child := range v.Children { - InspectBlock(child, f) - } - } - f(nil) -} - -// InspectInline traverses the blocks in depth-first order, starting with block. If f returns true, -// InspectInline invokes f recursively for each child of the block, followed by a call of f(nil). -func InspectInline(inline Inline, f func(Inline) bool) { - if !f(inline) { - return - } - switch v := inline.(type) { - case *InlineImage: - for _, child := range v.Children { - InspectInline(child, f) - } - case *InlineLink: - for _, child := range v.Children { - InspectInline(child, f) - } - case *ReferenceImage: - for _, child := range v.Children { - InspectInline(child, f) - } - case *ReferenceLink: - for _, child := range v.Children { - InspectInline(child, f) - } - } - f(nil) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/lines.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/lines.go deleted file mode 100644 index f59e5afe..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/lines.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type Line struct { - Range -} - -func ParseLines(markdown string) []Line { - lineStartPosition := 0 - isAfterCarriageReturn := false - lines := make([]Line, 0, strings.Count(markdown, "\n")) - for position, r := range markdown { - if r == '\n' { - lines = append(lines, Line{Range{lineStartPosition, position + 1}}) - lineStartPosition = position + 1 - } else if isAfterCarriageReturn { - lines = append(lines, Line{Range{lineStartPosition, position}}) - lineStartPosition = position - } - isAfterCarriageReturn = r == '\r' - } - if lineStartPosition < len(markdown) { - lines = append(lines, Line{Range{lineStartPosition, len(markdown)}}) - } - return lines -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/links.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/links.go deleted file mode 100644 index 6aa56f25..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/links.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "unicode/utf8" -) - -func parseLinkDestination(markdown string, position int) (raw Range, next int, ok bool) { - if position >= len(markdown) { - return - } - - if markdown[position] == '<' { - isEscaped := false - - for offset, c := range []byte(markdown[position+1:]) { - if isEscaped { - isEscaped = false - if isEscapableByte(c) { - continue - } - } - - if c == '\\' { - isEscaped = true - } else if c == '<' { - break - } else if c == '>' { - return Range{position + 1, position + 1 + offset}, position + 1 + offset + 1, true - } else if isWhitespaceByte(c) { - break - } - } - } - - openCount := 0 - isEscaped := false - for offset, c := range []byte(markdown[position:]) { - if isEscaped { - isEscaped = false - if isEscapableByte(c) { - continue - } - } - - switch c { - case '\\': - isEscaped = true - case '(': - openCount++ - case ')': - if openCount < 1 { - return Range{position, position + offset}, position + offset, true - } - openCount-- - default: - if isWhitespaceByte(c) { - return Range{position, position + offset}, position + offset, true - } - } - } - return Range{position, len(markdown)}, len(markdown), true -} - -func parseLinkTitle(markdown string, position int) (raw Range, next int, ok bool) { - if position >= len(markdown) { - return - } - - originalPosition := position - - var closer byte - switch markdown[position] { - case '"', '\'': - closer = markdown[position] - case '(': - closer = ')' - default: - return - } - position++ - - for position < len(markdown) { - switch markdown[position] { - case '\\': - position++ - if position < len(markdown) && isEscapableByte(markdown[position]) { - position++ - } - case closer: - return Range{originalPosition + 1, position}, position + 1, true - default: - position++ - } - } - - return -} - -func parseLinkLabel(markdown string, position int) (raw Range, next int, ok bool) { - if position >= len(markdown) || markdown[position] != '[' { - return - } - - originalPosition := position - position++ - - for position < len(markdown) { - switch markdown[position] { - case '\\': - position++ - if position < len(markdown) && isEscapableByte(markdown[position]) { - position++ - } - case '[': - return - case ']': - if position-originalPosition >= 1000 && utf8.RuneCountInString(markdown[originalPosition:position]) >= 1000 { - return - } - return Range{originalPosition + 1, position}, position + 1, true - default: - position++ - } - } - - return -} - -// As a non-standard feature, we allow image links to specify dimensions of the image by adding "=WIDTHxHEIGHT" -// after the image destination but before the image title like ![alt](http://example.com/image.png =100x200 "title"). -// Both width and height are optional, but at least one of them must be specified. -func parseImageDimensions(markdown string, position int) (raw Range, next int, ok bool) { - if position >= len(markdown) { - return - } - - originalPosition := position - - // Read = - position += 1 - if position >= len(markdown) { - return - } - - // Read width - hasWidth := false - for position < len(markdown)-1 && isNumericByte(markdown[position]) { - hasWidth = true - position += 1 - } - - // Look for early end of dimensions - if isWhitespaceByte(markdown[position]) || markdown[position] == ')' { - return Range{originalPosition, position - 1}, position, true - } - - // Read the x - if (markdown[position] != 'x' && markdown[position] != 'X') || position == len(markdown)-1 { - return - } - position += 1 - - // Read height - hasHeight := false - for position < len(markdown)-1 && isNumericByte(markdown[position]) { - hasHeight = true - position += 1 - } - - // Make sure the there's no trailing characters - if !isWhitespaceByte(markdown[position]) && markdown[position] != ')' { - return - } - - if !hasWidth && !hasHeight { - // At least one of width or height is required - return - } - - return Range{originalPosition, position - 1}, position, true -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/list.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/list.go deleted file mode 100644 index 39039295..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/list.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type ListItem struct { - blockBase - markdown string - hasTrailingBlankLine bool - hasBlankLineBetweenChildren bool - - Indentation int - Children []Block -} - -func (b *ListItem) Continuation(indentation int, r Range) *continuation { - s := b.markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - if b.Children == nil { - return nil - } - return &continuation{ - Remaining: r, - } - } - if indentation < b.Indentation { - return nil - } - return &continuation{ - Indentation: indentation - b.Indentation, - Remaining: r, - } -} - -func (b *ListItem) AddChild(openBlocks []Block) []Block { - b.Children = append(b.Children, openBlocks[0]) - if b.hasTrailingBlankLine { - b.hasBlankLineBetweenChildren = true - } - b.hasTrailingBlankLine = false - return openBlocks -} - -func (b *ListItem) AddLine(indentation int, r Range) bool { - isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == "" - if isBlank { - b.hasTrailingBlankLine = true - } - return false -} - -func (b *ListItem) HasTrailingBlankLine() bool { - return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine()) -} - -func (b *ListItem) isLoose() bool { - if b.hasBlankLineBetweenChildren { - return true - } - for i, child := range b.Children { - if i < len(b.Children)-1 && child.HasTrailingBlankLine() { - return true - } - } - return false -} - -type List struct { - blockBase - markdown string - hasTrailingBlankLine bool - hasBlankLineBetweenChildren bool - - IsLoose bool - IsOrdered bool - OrderedStart int - BulletOrDelimiter byte - Children []*ListItem -} - -func (b *List) Continuation(indentation int, r Range) *continuation { - s := b.markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - return &continuation{ - Remaining: r, - } - } - return &continuation{ - Indentation: indentation, - Remaining: r, - } -} - -func (b *List) AddChild(openBlocks []Block) []Block { - if item, ok := openBlocks[0].(*ListItem); ok { - b.Children = append(b.Children, item) - if b.hasTrailingBlankLine { - b.hasBlankLineBetweenChildren = true - } - b.hasTrailingBlankLine = false - return openBlocks - } else if list, ok := openBlocks[0].(*List); ok { - if len(list.Children) == 1 && list.IsOrdered == b.IsOrdered && list.BulletOrDelimiter == b.BulletOrDelimiter { - return b.AddChild(openBlocks[1:]) - } - } - return nil -} - -func (b *List) AddLine(indentation int, r Range) bool { - isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == "" - if isBlank { - b.hasTrailingBlankLine = true - } - return false -} - -func (b *List) HasTrailingBlankLine() bool { - return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine()) -} - -func (b *List) isLoose() bool { - if b.hasBlankLineBetweenChildren { - return true - } - for i, child := range b.Children { - if child.isLoose() || (i < len(b.Children)-1 && child.HasTrailingBlankLine()) { - return true - } - } - return false -} - -func (b *List) Close() { - b.IsLoose = b.isLoose() -} - -func parseListMarker(markdown string, r Range) (success, isOrdered bool, orderedStart int, bulletOrDelimiter byte, markerWidth int, remaining Range) { - digits := 0 - n := 0 - for i := r.Position; i < r.End && markdown[i] >= '0' && markdown[i] <= '9'; i++ { - digits++ - n = n*10 + int(markdown[i]-'0') - } - if digits > 0 { - if digits > 9 || r.Position+digits >= r.End { - return - } - next := markdown[r.Position+digits] - if next != '.' && next != ')' { - return - } - return true, true, n, next, digits + 1, Range{r.Position + digits + 1, r.End} - } - if r.Position >= r.End { - return - } - next := markdown[r.Position] - if next != '-' && next != '+' && next != '*' { - return - } - return true, false, 0, next, 1, Range{r.Position + 1, r.End} -} - -func listStart(markdown string, indent int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block { - afterList := false - if len(matchedBlocks) > 0 { - _, afterList = matchedBlocks[len(matchedBlocks)-1].(*List) - } - if !afterList && indent > 3 { - return nil - } - - success, isOrdered, orderedStart, bulletOrDelimiter, markerWidth, remaining := parseListMarker(markdown, r) - if !success { - return nil - } - - isBlank := strings.TrimSpace(markdown[remaining.Position:remaining.End]) == "" - if len(matchedBlocks) > 0 && len(unmatchedBlocks) == 0 { - if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok { - if isBlank || (isOrdered && orderedStart != 1) { - return nil - } - } - } - - indentAfterMarker, indentBytesAfterMarker := countIndentation(markdown, remaining) - if !isBlank && indentAfterMarker < 1 { - return nil - } - - remaining = Range{remaining.Position + indentBytesAfterMarker, remaining.End} - consumedIndentAfterMarker := indentAfterMarker - if isBlank || indentAfterMarker >= 5 { - consumedIndentAfterMarker = 1 - } - - listItem := &ListItem{ - markdown: markdown, - Indentation: indent + markerWidth + consumedIndentAfterMarker, - } - list := &List{ - markdown: markdown, - IsOrdered: isOrdered, - OrderedStart: orderedStart, - BulletOrDelimiter: bulletOrDelimiter, - Children: []*ListItem{listItem}, - } - ret := []Block{list, listItem} - if descendants := blockStartOrParagraph(markdown, indentAfterMarker-consumedIndentAfterMarker, remaining, nil, nil); descendants != nil { - listItem.Children = append(listItem.Children, descendants[0]) - ret = append(ret, descendants...) - } - return ret -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/markdown.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/markdown.go deleted file mode 100644 index 5ccdad8c..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/markdown.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -// This package implements a parser for the subset of the CommonMark spec necessary for us to do -// server-side processing. It is not a full implementation and lacks many features. But it is -// complete enough to efficiently and accurately allow us to do what we need to like rewrite image -// URLs for proxying. -package markdown - -import ( - "strings" -) - -func isEscapable(c rune) bool { - return c > ' ' && (c < '0' || (c > '9' && (c < 'A' || (c > 'Z' && (c < 'a' || (c > 'z' && c <= '~')))))) -} - -func isEscapableByte(c byte) bool { - return isEscapable(rune(c)) -} - -func isWhitespace(c rune) bool { - switch c { - case ' ', '\t', '\n', '\u000b', '\u000c', '\r': - return true - } - return false -} - -func isWhitespaceByte(c byte) bool { - return isWhitespace(rune(c)) -} - -func isNumeric(c rune) bool { - return c >= '0' && c <= '9' -} - -func isNumericByte(c byte) bool { - return isNumeric(rune(c)) -} - -func isHex(c rune) bool { - return isNumeric(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') -} - -func isHexByte(c byte) bool { - return isHex(rune(c)) -} - -func isAlphanumeric(c rune) bool { - return isNumeric(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') -} - -func isAlphanumericByte(c byte) bool { - return isAlphanumeric(rune(c)) -} - -func nextNonWhitespace(markdown string, position int) int { - for offset, c := range []byte(markdown[position:]) { - if !isWhitespaceByte(c) { - return position + offset - } - } - return len(markdown) -} - -func nextLine(markdown string, position int) (linePosition int, skippedNonWhitespace bool) { - for i := position; i < len(markdown); i++ { - c := markdown[i] - if c == '\r' { - if i+1 < len(markdown) && markdown[i+1] == '\n' { - return i + 2, skippedNonWhitespace - } - return i + 1, skippedNonWhitespace - } else if c == '\n' { - return i + 1, skippedNonWhitespace - } else if !isWhitespaceByte(c) { - skippedNonWhitespace = true - } - } - return len(markdown), skippedNonWhitespace -} - -func countIndentation(markdown string, r Range) (spaces, bytes int) { - for i := r.Position; i < r.End; i++ { - if markdown[i] == ' ' { - spaces++ - bytes++ - } else if markdown[i] == '\t' { - spaces += 4 - bytes++ - } else { - break - } - } - return -} - -func trimLeftSpace(markdown string, r Range) Range { - s := markdown[r.Position:r.End] - trimmed := strings.TrimLeftFunc(s, isWhitespace) - return Range{r.Position, r.End - (len(s) - len(trimmed))} -} - -func trimRightSpace(markdown string, r Range) Range { - s := markdown[r.Position:r.End] - trimmed := strings.TrimRightFunc(s, isWhitespace) - return Range{r.Position, r.End - (len(s) - len(trimmed))} -} - -func relativeToAbsolutePosition(ranges []Range, position int) int { - rem := position - for _, r := range ranges { - l := r.End - r.Position - if rem < l { - return r.Position + rem - } - rem -= l - } - if len(ranges) == 0 { - return 0 - } - return ranges[len(ranges)-1].End -} - -func trimBytesFromRanges(ranges []Range, bytes int) (result []Range) { - rem := bytes - for _, r := range ranges { - if rem == 0 { - result = append(result, r) - continue - } - l := r.End - r.Position - if rem < l { - result = append(result, Range{r.Position + rem, r.End}) - rem = 0 - continue - } - rem -= l - } - return -} - -func Parse(markdown string) (*Document, []*ReferenceDefinition) { - lines := ParseLines(markdown) - return ParseBlocks(markdown, lines) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/paragraph.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/paragraph.go deleted file mode 100644 index aef01b5e..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/paragraph.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -import ( - "strings" -) - -type Paragraph struct { - blockBase - markdown string - - Text []Range - ReferenceDefinitions []*ReferenceDefinition -} - -func (b *Paragraph) ParseInlines(referenceDefinitions []*ReferenceDefinition) []Inline { - return ParseInlines(b.markdown, b.Text, referenceDefinitions) -} - -func (b *Paragraph) Continuation(indentation int, r Range) *continuation { - s := b.markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - return nil - } - return &continuation{ - Indentation: indentation, - Remaining: r, - } -} - -func (b *Paragraph) Close() { - for { - for i := 0; i < len(b.Text); i++ { - b.Text[i] = trimLeftSpace(b.markdown, b.Text[i]) - if b.Text[i].Position < b.Text[i].End { - break - } - } - - if len(b.Text) == 0 || b.Text[0].Position < b.Text[0].End && b.markdown[b.Text[0].Position] != '[' { - break - } - - definition, remaining := parseReferenceDefinition(b.markdown, b.Text) - if definition == nil { - break - } - b.ReferenceDefinitions = append(b.ReferenceDefinitions, definition) - b.Text = remaining - } - - for i := len(b.Text) - 1; i >= 0; i-- { - b.Text[i] = trimRightSpace(b.markdown, b.Text[i]) - if b.Text[i].Position < b.Text[i].End { - break - } - } -} - -func newParagraph(markdown string, r Range) *Paragraph { - s := markdown[r.Position:r.End] - if strings.TrimSpace(s) == "" { - return nil - } - return &Paragraph{ - markdown: markdown, - Text: []Range{r}, - } -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/reference_definition.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/reference_definition.go deleted file mode 100644 index 69e8ed94..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/markdown/reference_definition.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package markdown - -type ReferenceDefinition struct { - RawDestination Range - - markdown string - rawLabel string - rawTitle string -} - -func (d *ReferenceDefinition) Destination() string { - return Unescape(d.markdown[d.RawDestination.Position:d.RawDestination.End]) -} - -func (d *ReferenceDefinition) Label() string { - return d.rawLabel -} - -func (d *ReferenceDefinition) Title() string { - return Unescape(d.rawTitle) -} - -func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) { - raw := "" - for _, r := range ranges { - raw += markdown[r.Position:r.End] - } - - label, next, ok := parseLinkLabel(raw, 0) - if !ok { - return nil, nil - } - position := next - - if position >= len(raw) || raw[position] != ':' { - return nil, nil - } - position++ - - destination, next, ok := parseLinkDestination(raw, nextNonWhitespace(raw, position)) - if !ok { - return nil, nil - } - position = next - - absoluteDestination := relativeToAbsolutePosition(ranges, destination.Position) - ret := &ReferenceDefinition{ - RawDestination: Range{absoluteDestination, absoluteDestination + destination.End - destination.Position}, - markdown: markdown, - rawLabel: raw[label.Position:label.End], - } - - if position < len(raw) && isWhitespaceByte(raw[position]) { - title, next, ok := parseLinkTitle(raw, nextNonWhitespace(raw, position)) - if !ok { - if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace { - return ret, trimBytesFromRanges(ranges, nextLine) - } - return nil, nil - } - if nextLine, skippedNonWhitespace := nextLine(raw, next); !skippedNonWhitespace { - ret.rawTitle = raw[title.Position:title.End] - return ret, trimBytesFromRanges(ranges, nextLine) - } - } - - if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace { - return ret, trimBytesFromRanges(ranges, nextLine) - } - - return nil, nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/default.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/default.go deleted file mode 100644 index e7faa8c4..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/default.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" - - "github.com/mattermost/logr" -) - -// defaultLog manually encodes the log to STDERR, providing a basic, default logging implementation -// before mlog is fully configured. -func defaultLog(level, msg string, fields ...Field) { - log := struct { - Level string `json:"level"` - Message string `json:"msg"` - Fields []Field `json:"fields,omitempty"` - }{ - level, - msg, - fields, - } - - if b, err := json.Marshal(log); err != nil { - fmt.Fprintf(os.Stderr, `{"level":"error","msg":"failed to encode log message"}%s`, "\n") - } else { - fmt.Fprintf(os.Stderr, "%s\n", b) - } -} - -func defaultIsLevelEnabled(level LogLevel) bool { - return true -} - -func defaultDebugLog(msg string, fields ...Field) { - defaultLog("debug", msg, fields...) -} - -func defaultInfoLog(msg string, fields ...Field) { - defaultLog("info", msg, fields...) -} - -func defaultWarnLog(msg string, fields ...Field) { - defaultLog("warn", msg, fields...) -} - -func defaultErrorLog(msg string, fields ...Field) { - defaultLog("error", msg, fields...) -} - -func defaultCriticalLog(msg string, fields ...Field) { - // We map critical to error in zap, so be consistent. - defaultLog("error", msg, fields...) -} - -func defaultCustomLog(lvl LogLevel, msg string, fields ...Field) { - // custom log levels are only output once log targets are configured. -} - -func defaultCustomMultiLog(lvl []LogLevel, msg string, fields ...Field) { - // custom log levels are only output once log targets are configured. -} - -func defaultFlush(ctx context.Context) error { - return nil -} - -func defaultAdvancedConfig(cfg LogTargetCfg) error { - // mlog.ConfigAdvancedConfig should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot config advanced logging on default logger") -} - -func defaultAdvancedShutdown(ctx context.Context) error { - return nil -} - -func defaultAddTarget(targets ...logr.Target) error { - // mlog.AddTarget should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot AddTarget on default logger") -} - -func defaultRemoveTargets(ctx context.Context, f func(TargetInfo) bool) error { - // mlog.RemoveTargets should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot RemoveTargets on default logger") -} - -func defaultEnableMetrics(collector logr.MetricsCollector) error { - // mlog.EnableMetrics should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot EnableMetrics on default logger") -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/errors.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/errors.go deleted file mode 100644 index 93762fda..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/errors.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "github.com/mattermost/logr" -) - -// onLoggerError is called when the logging system encounters an error, -// such as a target not able to write records. The targets will keep trying -// however the error will be logged with a dedicated level that can be output -// to a safe/always available target for monitoring or alerting. -func onLoggerError(err error) { - Log(LvlLogError, "advanced logging error", Err(err)) -} - -// onQueueFull is called when the main logger queue is full, indicating the -// volume and frequency of log record creation is too high for the queue size -// and/or the target latencies. -func onQueueFull(rec *logr.LogRec, maxQueueSize int) bool { - Log(LvlLogError, "main queue full, dropping record", Any("rec", rec)) - return true // drop record -} - -// onTargetQueueFull is called when the main logger queue is full, indicating the -// volume and frequency of log record creation is too high for the target's queue size -// and/or the target latency. -func onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool { - Log(LvlLogError, "target queue full, dropping record", String("target", ""), Any("rec", rec)) - return true // drop record -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/global.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/global.go deleted file mode 100644 index aba06646..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/global.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "log" - "sync/atomic" - - "github.com/mattermost/logr" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -var globalLogger *Logger - -func InitGlobalLogger(logger *Logger) { - // Clean up previous instance. - if globalLogger != nil && globalLogger.logrLogger != nil { - globalLogger.logrLogger.Logr().Shutdown() - } - glob := *logger - glob.zap = glob.zap.WithOptions(zap.AddCallerSkip(1)) - globalLogger = &glob - IsLevelEnabled = globalLogger.IsLevelEnabled - Debug = globalLogger.Debug - Info = globalLogger.Info - Warn = globalLogger.Warn - Error = globalLogger.Error - Critical = globalLogger.Critical - Log = globalLogger.Log - LogM = globalLogger.LogM - Flush = globalLogger.Flush - ConfigAdvancedLogging = globalLogger.ConfigAdvancedLogging - ShutdownAdvancedLogging = globalLogger.ShutdownAdvancedLogging - AddTarget = globalLogger.AddTarget - RemoveTargets = globalLogger.RemoveTargets - EnableMetrics = globalLogger.EnableMetrics -} - -// logWriterFunc provides access to mlog via io.Writer, so the standard logger -// can be redirected to use mlog and whatever targets are defined. -type logWriterFunc func([]byte) (int, error) - -func (lw logWriterFunc) Write(p []byte) (int, error) { - return lw(p) -} - -func RedirectStdLog(logger *Logger) { - if atomic.LoadInt32(&disableZap) == 0 { - zap.RedirectStdLogAt(logger.zap.With(zap.String("source", "stdlog")).WithOptions(zap.AddCallerSkip(-2)), zapcore.ErrorLevel) - return - } - - writer := func(p []byte) (int, error) { - Log(LvlStdLog, string(p)) - return len(p), nil - } - log.SetOutput(logWriterFunc(writer)) -} - -type IsLevelEnabledFunc func(LogLevel) bool -type LogFunc func(string, ...Field) -type LogFuncCustom func(LogLevel, string, ...Field) -type LogFuncCustomMulti func([]LogLevel, string, ...Field) -type FlushFunc func(context.Context) error -type ConfigFunc func(cfg LogTargetCfg) error -type ShutdownFunc func(context.Context) error -type AddTargetFunc func(...logr.Target) error -type RemoveTargetsFunc func(context.Context, func(TargetInfo) bool) error -type EnableMetricsFunc func(logr.MetricsCollector) error - -// DON'T USE THIS Modify the level on the app logger -func GloballyDisableDebugLogForTest() { - globalLogger.consoleLevel.SetLevel(zapcore.ErrorLevel) -} - -// DON'T USE THIS Modify the level on the app logger -func GloballyEnableDebugLogForTest() { - globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel) -} - -var IsLevelEnabled IsLevelEnabledFunc = defaultIsLevelEnabled -var Debug LogFunc = defaultDebugLog -var Info LogFunc = defaultInfoLog -var Warn LogFunc = defaultWarnLog -var Error LogFunc = defaultErrorLog -var Critical LogFunc = defaultCriticalLog -var Log LogFuncCustom = defaultCustomLog -var LogM LogFuncCustomMulti = defaultCustomMultiLog -var Flush FlushFunc = defaultFlush - -var ConfigAdvancedLogging ConfigFunc = defaultAdvancedConfig -var ShutdownAdvancedLogging ShutdownFunc = defaultAdvancedShutdown -var AddTarget AddTargetFunc = defaultAddTarget -var RemoveTargets RemoveTargetsFunc = defaultRemoveTargets -var EnableMetrics EnableMetricsFunc = defaultEnableMetrics diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/levels.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/levels.go deleted file mode 100644 index 24d29e0b..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/levels.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -// Standard levels -var ( - LvlPanic = LogLevel{ID: 0, Name: "panic", Stacktrace: true} - LvlFatal = LogLevel{ID: 1, Name: "fatal", Stacktrace: true} - LvlError = LogLevel{ID: 2, Name: "error"} - LvlWarn = LogLevel{ID: 3, Name: "warn"} - LvlInfo = LogLevel{ID: 4, Name: "info"} - LvlDebug = LogLevel{ID: 5, Name: "debug"} - LvlTrace = LogLevel{ID: 6, Name: "trace"} - // used by redirected standard logger - LvlStdLog = LogLevel{ID: 10, Name: "stdlog"} - // used only by the logger - LvlLogError = LogLevel{ID: 11, Name: "logerror", Stacktrace: true} -) - -// Register custom (discrete) levels here. -// !!!!! ID's must not exceed 32,768 !!!!!! -var ( - // used by the audit system - LvlAuditAPI = LogLevel{ID: 100, Name: "audit-api"} - LvlAuditContent = LogLevel{ID: 101, Name: "audit-content"} - LvlAuditPerms = LogLevel{ID: 102, Name: "audit-permissions"} - LvlAuditCLI = LogLevel{ID: 103, Name: "audit-cli"} - - // used by the TCP log target - LvlTcpLogTarget = LogLevel{ID: 120, Name: "TcpLogTarget"} - - // used by Remote Cluster Service - LvlRemoteClusterServiceDebug = LogLevel{ID: 130, Name: "RemoteClusterServiceDebug"} - LvlRemoteClusterServiceError = LogLevel{ID: 131, Name: "RemoteClusterServiceError"} - LvlRemoteClusterServiceWarn = LogLevel{ID: 132, Name: "RemoteClusterServiceWarn"} - - // used by Shared Channel Sync Service - LvlSharedChannelServiceDebug = LogLevel{ID: 200, Name: "SharedChannelServiceDebug"} - LvlSharedChannelServiceError = LogLevel{ID: 201, Name: "SharedChannelServiceError"} - LvlSharedChannelServiceWarn = LogLevel{ID: 202, Name: "SharedChannelServiceWarn"} - LvlSharedChannelServiceMessagesInbound = LogLevel{ID: 203, Name: "SharedChannelServiceMsgInbound"} - LvlSharedChannelServiceMessagesOutbound = LogLevel{ID: 204, Name: "SharedChannelServiceMsgOutbound"} - - // add more here ... -) - -// Combinations for LogM (log multi) -var ( - MLvlAuditAll = []LogLevel{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI} -) diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/log.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/log.go deleted file mode 100644 index d50fc123..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/log.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "fmt" - "io" - "log" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/mattermost/logr" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -const ( - // Very verbose messages for debugging specific issues - LevelDebug = "debug" - // Default log level, informational - LevelInfo = "info" - // Warnings are messages about possible issues - LevelWarn = "warn" - // Errors are messages about things we know are problems - LevelError = "error" - - // DefaultFlushTimeout is the default amount of time mlog.Flush will wait - // before timing out. - DefaultFlushTimeout = time.Second * 5 -) - -var ( - // disableZap is set when Zap should be disabled and Logr used instead. - // This is needed for unit testing as Zap has no shutdown capabilities - // and holds file handles until process exit. Currently unit test create - // many server instances, and thus many Zap log files. - // This flag will be removed when Zap is permanently replaced. - disableZap int32 -) - -// Type and function aliases from zap to limit the libraries scope into MM code -type Field = zapcore.Field - -var Int64 = zap.Int64 -var Int32 = zap.Int32 -var Int = zap.Int -var Uint32 = zap.Uint32 -var String = zap.String -var Any = zap.Any -var Err = zap.Error -var NamedErr = zap.NamedError -var Bool = zap.Bool -var Duration = zap.Duration - -type LoggerIFace interface { - IsLevelEnabled(LogLevel) bool - Debug(string, ...Field) - Info(string, ...Field) - Warn(string, ...Field) - Error(string, ...Field) - Critical(string, ...Field) - Log(LogLevel, string, ...Field) - LogM([]LogLevel, string, ...Field) -} - -type TargetInfo logr.TargetInfo - -type LoggerConfiguration struct { - EnableConsole bool - ConsoleJson bool - EnableColor bool - ConsoleLevel string - EnableFile bool - FileJson bool - FileLevel string - FileLocation string -} - -type Logger struct { - zap *zap.Logger - consoleLevel zap.AtomicLevel - fileLevel zap.AtomicLevel - logrLogger *logr.Logger - mutex *sync.RWMutex -} - -func getZapLevel(level string) zapcore.Level { - switch level { - case LevelInfo: - return zapcore.InfoLevel - case LevelWarn: - return zapcore.WarnLevel - case LevelDebug: - return zapcore.DebugLevel - case LevelError: - return zapcore.ErrorLevel - default: - return zapcore.InfoLevel - } -} - -func makeEncoder(json, color bool) zapcore.Encoder { - encoderConfig := zap.NewProductionEncoderConfig() - if json { - return zapcore.NewJSONEncoder(encoderConfig) - } - - if color { - encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - } - encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - return zapcore.NewConsoleEncoder(encoderConfig) -} - -func NewLogger(config *LoggerConfiguration) *Logger { - cores := []zapcore.Core{} - logger := &Logger{ - consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)), - fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)), - logrLogger: newLogr(), - mutex: &sync.RWMutex{}, - } - - if config.EnableConsole { - writer := zapcore.Lock(os.Stderr) - core := zapcore.NewCore(makeEncoder(config.ConsoleJson, config.EnableColor), writer, logger.consoleLevel) - cores = append(cores, core) - } - - if config.EnableFile { - if atomic.LoadInt32(&disableZap) != 0 { - t := &LogTarget{ - Type: "file", - Format: "json", - Levels: mlogLevelToLogrLevels(config.FileLevel), - MaxQueueSize: DefaultMaxTargetQueue, - Options: []byte(fmt.Sprintf(`{"Filename":"%s", "MaxSizeMB":%d, "Compress":%t}`, - config.FileLocation, 100, true)), - } - if !config.FileJson { - t.Format = "plain" - } - if tgt, err := NewLogrTarget("mlogFile", t); err == nil { - logger.logrLogger.Logr().AddTarget(tgt) - } else { - Error("error creating mlogFile", Err(err)) - } - } else { - writer := zapcore.AddSync(&lumberjack.Logger{ - Filename: config.FileLocation, - MaxSize: 100, - Compress: true, - }) - - core := zapcore.NewCore(makeEncoder(config.FileJson, false), writer, logger.fileLevel) - cores = append(cores, core) - } - } - - combinedCore := zapcore.NewTee(cores...) - - logger.zap = zap.New(combinedCore, - zap.AddCaller(), - ) - return logger -} - -func (l *Logger) ChangeLevels(config *LoggerConfiguration) { - l.consoleLevel.SetLevel(getZapLevel(config.ConsoleLevel)) - l.fileLevel.SetLevel(getZapLevel(config.FileLevel)) -} - -func (l *Logger) SetConsoleLevel(level string) { - l.consoleLevel.SetLevel(getZapLevel(level)) -} - -func (l *Logger) With(fields ...Field) *Logger { - newLogger := *l - newLogger.zap = newLogger.zap.With(fields...) - if newLogger.getLogger() != nil { - ll := newLogger.getLogger().WithFields(zapToLogr(fields)) - newLogger.logrLogger = &ll - } - return &newLogger -} - -func (l *Logger) StdLog(fields ...Field) *log.Logger { - return zap.NewStdLog(l.With(fields...).zap.WithOptions(getStdLogOption())) -} - -// StdLogAt returns *log.Logger which writes to supplied zap logger at required level. -func (l *Logger) StdLogAt(level string, fields ...Field) (*log.Logger, error) { - return zap.NewStdLogAt(l.With(fields...).zap.WithOptions(getStdLogOption()), getZapLevel(level)) -} - -// StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger -// anything written will be interpreted as log entries accordingly -func (l *Logger) StdLogWriter() io.Writer { - newLogger := *l - newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(4), getStdLogOption()) - f := newLogger.Info - return &loggerWriter{f} -} - -func (l *Logger) WithCallerSkip(skip int) *Logger { - newLogger := *l - newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(skip)) - return &newLogger -} - -// Made for the plugin interface, wraps mlog in a simpler interface -// at the cost of performance -func (l *Logger) Sugar() *SugarLogger { - return &SugarLogger{ - wrappedLogger: l, - zapSugar: l.zap.Sugar(), - } -} - -func (l *Logger) IsLevelEnabled(level LogLevel) bool { - return isLevelEnabled(l.getLogger(), logr.Level(level)) -} - -func (l *Logger) Debug(message string, fields ...Field) { - l.zap.Debug(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Debug) { - l.getLogger().WithFields(zapToLogr(fields)).Debug(message) - } -} - -func (l *Logger) Info(message string, fields ...Field) { - l.zap.Info(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Info) { - l.getLogger().WithFields(zapToLogr(fields)).Info(message) - } -} - -func (l *Logger) Warn(message string, fields ...Field) { - l.zap.Warn(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Warn) { - l.getLogger().WithFields(zapToLogr(fields)).Warn(message) - } -} - -func (l *Logger) Error(message string, fields ...Field) { - l.zap.Error(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Error) { - l.getLogger().WithFields(zapToLogr(fields)).Error(message) - } -} - -func (l *Logger) Critical(message string, fields ...Field) { - l.zap.Error(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Error) { - l.getLogger().WithFields(zapToLogr(fields)).Error(message) - } -} - -func (l *Logger) Log(level LogLevel, message string, fields ...Field) { - l.getLogger().WithFields(zapToLogr(fields)).Log(logr.Level(level), message) -} - -func (l *Logger) LogM(levels []LogLevel, message string, fields ...Field) { - var logger *logr.Logger - for _, lvl := range levels { - if isLevelEnabled(l.getLogger(), logr.Level(lvl)) { - // don't create logger with fields unless at least one level is active. - if logger == nil { - l := l.getLogger().WithFields(zapToLogr(fields)) - logger = &l - } - logger.Log(logr.Level(lvl), message) - } - } -} - -func (l *Logger) Flush(cxt context.Context) error { - return l.getLogger().Logr().FlushWithTimeout(cxt) -} - -// ShutdownAdvancedLogging stops the logger from accepting new log records and tries to -// flush queues within the context timeout. Once complete all targets are shutdown -// and any resources released. -func (l *Logger) ShutdownAdvancedLogging(cxt context.Context) error { - err := l.getLogger().Logr().ShutdownWithTimeout(cxt) - l.setLogger(newLogr()) - return err -} - -// ConfigAdvancedLoggingConfig (re)configures advanced logging based on the -// specified log targets. This is the easiest way to get the advanced logger -// configured via a config source such as file. -func (l *Logger) ConfigAdvancedLogging(targets LogTargetCfg) error { - if err := l.ShutdownAdvancedLogging(context.Background()); err != nil { - Error("error shutting down previous logger", Err(err)) - } - - err := logrAddTargets(l.getLogger(), targets) - return err -} - -// AddTarget adds one or more logr.Target to the advanced logger. This is the preferred method -// to add custom targets or provide configuration that cannot be expressed via a -// config source. -func (l *Logger) AddTarget(targets ...logr.Target) error { - return l.getLogger().Logr().AddTarget(targets...) -} - -// RemoveTargets selectively removes targets that were previously added to this logger instance -// using the passed in filter function. The filter function should return true to remove the target -// and false to keep it. -func (l *Logger) RemoveTargets(ctx context.Context, f func(ti TargetInfo) bool) error { - // Use locally defined TargetInfo type so we don't spread Logr dependencies. - fc := func(tic logr.TargetInfo) bool { - return f(TargetInfo(tic)) - } - return l.getLogger().Logr().RemoveTargets(ctx, fc) -} - -// EnableMetrics enables metrics collection by supplying a MetricsCollector. -// The MetricsCollector provides counters and gauges that are updated by log targets. -func (l *Logger) EnableMetrics(collector logr.MetricsCollector) error { - return l.getLogger().Logr().SetMetricsCollector(collector) -} - -// getLogger is a concurrent safe getter of the logr logger -func (l *Logger) getLogger() *logr.Logger { - defer l.mutex.RUnlock() - l.mutex.RLock() - return l.logrLogger -} - -// setLogger is a concurrent safe setter of the logr logger -func (l *Logger) setLogger(logger *logr.Logger) { - defer l.mutex.Unlock() - l.mutex.Lock() - l.logrLogger = logger -} - -// DisableZap is called to disable Zap, and Logr will be used instead. Any Logger -// instances created after this call will only use Logr. -// -// This is needed for unit testing as Zap has no shutdown capabilities -// and holds file handles until process exit. Currently unit tests create -// many server instances, and thus many Zap log file handles. -// -// This method will be removed when Zap is permanently replaced. -func DisableZap() { - atomic.StoreInt32(&disableZap, 1) -} - -// EnableZap re-enables Zap such that any Logger instances created after this -// call will allow Zap targets. -func EnableZap() { - atomic.StoreInt32(&disableZap, 0) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/logr.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/logr.go deleted file mode 100644 index c44fafa0..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/logr.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "encoding/json" - "fmt" - "io" - "os" - - "github.com/hashicorp/go-multierror" - "github.com/mattermost/logr" - logrFmt "github.com/mattermost/logr/format" - "github.com/mattermost/logr/target" - "go.uber.org/zap/zapcore" -) - -const ( - DefaultMaxTargetQueue = 1000 - DefaultSysLogPort = 514 -) - -type LogLevel struct { - ID logr.LevelID - Name string - Stacktrace bool -} - -type LogTarget struct { - Type string // one of "console", "file", "tcp", "syslog", "none". - Format string // one of "json", "plain" - Levels []LogLevel - Options json.RawMessage - MaxQueueSize int -} - -type LogTargetCfg map[string]*LogTarget -type LogrCleanup func() error - -func newLogr() *logr.Logger { - lgr := &logr.Logr{} - lgr.OnExit = func(int) {} - lgr.OnPanic = func(interface{}) {} - lgr.OnLoggerError = onLoggerError - lgr.OnQueueFull = onQueueFull - lgr.OnTargetQueueFull = onTargetQueueFull - - logger := lgr.NewLogger() - return &logger -} - -func logrAddTargets(logger *logr.Logger, targets LogTargetCfg) error { - lgr := logger.Logr() - var errs error - for name, t := range targets { - target, err := NewLogrTarget(name, t) - if err != nil { - errs = multierror.Append(err) - continue - } - if target != nil { - target.SetName(name) - lgr.AddTarget(target) - } - } - return errs -} - -// NewLogrTarget creates a `logr.Target` based on a target config. -// Can be used when parsing custom config files, or when programmatically adding -// built-in targets. Use `mlog.AddTarget` to add custom targets. -func NewLogrTarget(name string, t *LogTarget) (logr.Target, error) { - formatter, err := newFormatter(name, t.Format) - if err != nil { - return nil, err - } - filter := newFilter(t.Levels) - - if t.MaxQueueSize == 0 { - t.MaxQueueSize = DefaultMaxTargetQueue - } - - switch t.Type { - case "console": - return newConsoleTarget(name, t, filter, formatter) - case "file": - return newFileTarget(name, t, filter, formatter) - case "syslog": - return newSyslogTarget(name, t, filter, formatter) - case "tcp": - return newTCPTarget(name, t, filter, formatter) - case "none": - return nil, nil - } - return nil, fmt.Errorf("invalid type '%s' for target %s", t.Type, name) -} - -func newFilter(levels []LogLevel) logr.Filter { - filter := &logr.CustomFilter{} - for _, lvl := range levels { - filter.Add(logr.Level(lvl)) - } - return filter -} - -func newFormatter(name string, format string) (logr.Formatter, error) { - switch format { - case "json", "": - return &logrFmt.JSON{}, nil - case "plain": - return &logrFmt.Plain{Delim: " | "}, nil - default: - return nil, fmt.Errorf("invalid format '%s' for target %s", format, name) - } -} - -func newConsoleTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - type consoleOptions struct { - Out string `json:"Out"` - } - options := &consoleOptions{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - var w io.Writer - switch options.Out { - case "stdout", "": - w = os.Stdout - case "stderr": - w = os.Stderr - default: - return nil, fmt.Errorf("invalid out '%s' for target %s", options.Out, name) - } - - newTarget := target.NewWriterTarget(filter, formatter, w, t.MaxQueueSize) - return newTarget, nil -} - -func newFileTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - type fileOptions struct { - Filename string `json:"Filename"` - MaxSize int `json:"MaxSizeMB"` - MaxAge int `json:"MaxAgeDays"` - MaxBackups int `json:"MaxBackups"` - Compress bool `json:"Compress"` - } - options := &fileOptions{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - return newFileTargetWithOpts(name, t, target.FileOptions(*options), filter, formatter) -} - -func newFileTargetWithOpts(name string, t *LogTarget, opts target.FileOptions, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - if opts.Filename == "" { - return nil, fmt.Errorf("missing 'Filename' option for target %s", name) - } - if err := checkFileWritable(opts.Filename); err != nil { - return nil, fmt.Errorf("error writing to 'Filename' for target %s: %w", name, err) - } - - newTarget := target.NewFileTarget(filter, formatter, opts, t.MaxQueueSize) - return newTarget, nil -} - -func newSyslogTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - options := &SyslogParams{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - if options.IP == "" { - return nil, fmt.Errorf("missing 'IP' option for target %s", name) - } - if options.Port == 0 { - options.Port = DefaultSysLogPort - } - return NewSyslogTarget(filter, formatter, options, t.MaxQueueSize) -} - -func newTCPTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - options := &TcpParams{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - if options.IP == "" { - return nil, fmt.Errorf("missing 'IP' option for target %s", name) - } - if options.Port == 0 { - return nil, fmt.Errorf("missing 'Port' option for target %s", name) - } - return NewTcpTarget(filter, formatter, options, t.MaxQueueSize) -} - -func checkFileWritable(filename string) error { - // try opening/creating the file for writing - file, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - return err - } - file.Close() - return nil -} - -func isLevelEnabled(logger *logr.Logger, level logr.Level) bool { - if logger == nil || logger.Logr() == nil { - return false - } - - status := logger.Logr().IsLevelEnabled(level) - return status.Enabled -} - -// zapToLogr converts Zap fields to Logr fields. -// This will not be needed once Logr is used for all logging. -func zapToLogr(zapFields []Field) logr.Fields { - encoder := zapcore.NewMapObjectEncoder() - for _, zapField := range zapFields { - zapField.AddTo(encoder) - } - return logr.Fields(encoder.Fields) -} - -// mlogLevelToLogrLevel converts a mlog logger level to -// an array of discrete Logr levels. -func mlogLevelToLogrLevels(level string) []LogLevel { - levels := make([]LogLevel, 0) - levels = append(levels, LvlError, LvlPanic, LvlFatal, LvlStdLog) - - switch level { - case LevelDebug: - levels = append(levels, LvlDebug) - fallthrough - case LevelInfo: - levels = append(levels, LvlInfo) - fallthrough - case LevelWarn: - levels = append(levels, LvlWarn) - } - return levels -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/stdlog.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/stdlog.go deleted file mode 100644 index fd702abf..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/stdlog.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "bytes" - "strings" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// Implementation of zapcore.Core to interpret log messages from a standard logger -// and translate the levels to zapcore levels. -type stdLogLevelInterpreterCore struct { - wrappedCore zapcore.Core -} - -func stdLogInterpretZapEntry(entry zapcore.Entry) zapcore.Entry { - message := entry.Message - if strings.Index(message, "[DEBUG]") == 0 { - entry.Level = zapcore.DebugLevel - entry.Message = message[7:] - } else if strings.Index(message, "[DEBG]") == 0 { - entry.Level = zapcore.DebugLevel - entry.Message = message[6:] - } else if strings.Index(message, "[WARN]") == 0 { - entry.Level = zapcore.WarnLevel - entry.Message = message[6:] - } else if strings.Index(message, "[ERROR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[7:] - } else if strings.Index(message, "[EROR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[6:] - } else if strings.Index(message, "[ERR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[5:] - } else if strings.Index(message, "[INFO]") == 0 { - entry.Level = zapcore.InfoLevel - entry.Message = message[6:] - } - return entry -} - -func (s *stdLogLevelInterpreterCore) Enabled(lvl zapcore.Level) bool { - return s.wrappedCore.Enabled(lvl) -} - -func (s *stdLogLevelInterpreterCore) With(fields []zapcore.Field) zapcore.Core { - return s.wrappedCore.With(fields) -} - -func (s *stdLogLevelInterpreterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry { - entry = stdLogInterpretZapEntry(entry) - return s.wrappedCore.Check(entry, checkedEntry) -} - -func (s *stdLogLevelInterpreterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { - entry = stdLogInterpretZapEntry(entry) - return s.wrappedCore.Write(entry, fields) -} - -func (s *stdLogLevelInterpreterCore) Sync() error { - return s.wrappedCore.Sync() -} - -func getStdLogOption() zap.Option { - return zap.WrapCore( - func(core zapcore.Core) zapcore.Core { - return &stdLogLevelInterpreterCore{core} - }, - ) -} - -type loggerWriter struct { - logFunc func(msg string, fields ...Field) -} - -func (l *loggerWriter) Write(p []byte) (int, error) { - trimmed := string(bytes.TrimSpace(p)) - for _, line := range strings.Split(trimmed, "\n") { - l.logFunc(line) - } - return len(p), nil -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/sugar.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/sugar.go deleted file mode 100644 index 2368b085..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/sugar.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "go.uber.org/zap" -) - -// Made for the plugin interface, use the regular logger for other uses -type SugarLogger struct { - wrappedLogger *Logger - zapSugar *zap.SugaredLogger -} - -func (l *SugarLogger) Debug(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Debugw(msg, keyValuePairs...) -} - -func (l *SugarLogger) Info(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Infow(msg, keyValuePairs...) -} - -func (l *SugarLogger) Error(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Errorw(msg, keyValuePairs...) -} - -func (l *SugarLogger) Warn(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Warnw(msg, keyValuePairs...) -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/syslog.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/syslog.go deleted file mode 100644 index 8766a964..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/syslog.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - - "github.com/mattermost/logr" - "github.com/wiggin77/merror" - syslog "github.com/wiggin77/srslog" -) - -// Syslog outputs log records to local or remote syslog. -type Syslog struct { - logr.Basic - w *syslog.Writer -} - -// SyslogParams provides parameters for dialing a syslog daemon. -type SyslogParams struct { - IP string `json:"IP"` - Port int `json:"Port"` - Tag string `json:"Tag"` - TLS bool `json:"TLS"` - Cert string `json:"Cert"` - Insecure bool `json:"Insecure"` -} - -// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS. -func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) { - network := "tcp" - var config *tls.Config - - if params.TLS { - network = "tcp+tls" - config = &tls.Config{InsecureSkipVerify: params.Insecure} - if params.Cert != "" { - pool, err := getCertPool(params.Cert) - if err != nil { - return nil, err - } - config.RootCAs = pool - } - } - raddr := fmt.Sprintf("%s:%d", params.IP, params.Port) - - writer, err := syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, params.Tag, config) - if err != nil { - return nil, err - } - - s := &Syslog{w: writer} - s.Basic.Start(s, s, filter, formatter, maxQueue) - - return s, nil -} - -// Shutdown stops processing log records after making best effort to flush queue. -func (s *Syslog) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := s.Basic.Shutdown(ctx) - errs.Append(err) - - err = s.w.Close() - errs.Append(err) - - return errs.ErrorOrNil() -} - -// getCertPool returns a x509.CertPool containing the cert(s) -// from `cert`, which can be a path to a .pem or .crt file, -// or a base64 encoded cert. -func getCertPool(cert string) (*x509.CertPool, error) { - if cert == "" { - return nil, errors.New("no cert provided") - } - - // first treat as a file and try to read. - serverCert, err := ioutil.ReadFile(cert) - if err != nil { - // maybe it's a base64 encoded cert - serverCert, err = base64.StdEncoding.DecodeString(cert) - if err != nil { - return nil, errors.New("cert cannot be read") - } - } - - pool := x509.NewCertPool() - if ok := pool.AppendCertsFromPEM(serverCert); ok { - return pool, nil - } - return nil, errors.New("cannot parse cert") -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to syslog. -func (s *Syslog) Write(rec *logr.LogRec) error { - _, stacktrace := s.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := s.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - txt := buf.String() - - switch rec.Level() { - case logr.Panic, logr.Fatal: - err = s.w.Crit(txt) - case logr.Error: - err = s.w.Err(txt) - case logr.Warn: - err = s.w.Warning(txt) - case logr.Debug, logr.Trace: - err = s.w.Debug(txt) - default: - // logr.Info plus all custom levels. - err = s.w.Info(txt) - } - - if err != nil { - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("syslog write fail: %w", err)) - // syslog writer will try to reconnect. - } - return err -} - -// String returns a string representation of this target. -func (s *Syslog) String() string { - return "SyslogTarget" -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/tcp.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/tcp.go deleted file mode 100644 index d65b43ee..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/tcp.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "net" - _ "net/http/pprof" - "sync" - "time" - - "github.com/hashicorp/go-multierror" - "github.com/mattermost/logr" -) - -const ( - DialTimeoutSecs = 30 - WriteTimeoutSecs = 30 - RetryBackoffMillis int64 = 100 - MaxRetryBackoffMillis int64 = 30 * 1000 // 30 seconds -) - -// Tcp outputs log records to raw socket server. -type Tcp struct { - logr.Basic - - params *TcpParams - addy string - - mutex sync.Mutex - conn net.Conn - monitor chan struct{} - shutdown chan struct{} -} - -// TcpParams provides parameters for dialing a socket server. -type TcpParams struct { - IP string `json:"IP"` - Port int `json:"Port"` - TLS bool `json:"TLS"` - Cert string `json:"Cert"` - Insecure bool `json:"Insecure"` -} - -// NewTcpTarget creates a target capable of outputting log records to a raw socket, with or without TLS. -func NewTcpTarget(filter logr.Filter, formatter logr.Formatter, params *TcpParams, maxQueue int) (*Tcp, error) { - tcp := &Tcp{ - params: params, - addy: fmt.Sprintf("%s:%d", params.IP, params.Port), - monitor: make(chan struct{}), - shutdown: make(chan struct{}), - } - tcp.Basic.Start(tcp, tcp, filter, formatter, maxQueue) - - return tcp, nil -} - -// getConn provides a net.Conn. If a connection already exists, it is returned immediately, -// otherwise this method blocks until a new connection is created, timeout or shutdown. -func (tcp *Tcp) getConn() (net.Conn, error) { - tcp.mutex.Lock() - defer tcp.mutex.Unlock() - - Log(LvlTcpLogTarget, "getConn enter", String("addy", tcp.addy)) - defer Log(LvlTcpLogTarget, "getConn exit", String("addy", tcp.addy)) - - if tcp.conn != nil { - Log(LvlTcpLogTarget, "reusing existing conn", String("addy", tcp.addy)) // use "With" once Zap is removed - return tcp.conn, nil - } - - type result struct { - conn net.Conn - err error - } - - connChan := make(chan result) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*DialTimeoutSecs) - defer cancel() - - go func(ctx context.Context, ch chan result) { - Log(LvlTcpLogTarget, "dailing", String("addy", tcp.addy)) - conn, err := tcp.dial(ctx) - if err == nil { - tcp.conn = conn - tcp.monitor = make(chan struct{}) - go monitor(tcp.conn, tcp.monitor, Log) - } - ch <- result{conn: conn, err: err} - }(ctx, connChan) - - select { - case <-tcp.shutdown: - return nil, errors.New("shutdown") - case res := <-connChan: - return res.conn, res.err - } -} - -// dial connects to a TCP socket, and optionally performs a TLS handshake. -// A non-nil context must be provided which can cancel the dial. -func (tcp *Tcp) dial(ctx context.Context) (net.Conn, error) { - var dialer net.Dialer - dialer.Timeout = time.Second * DialTimeoutSecs - conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.params.IP, tcp.params.Port)) - if err != nil { - return nil, err - } - - if !tcp.params.TLS { - return conn, nil - } - - Log(LvlTcpLogTarget, "TLS handshake", String("addy", tcp.addy)) - - tlsconfig := &tls.Config{ - ServerName: tcp.params.IP, - InsecureSkipVerify: tcp.params.Insecure, - } - if tcp.params.Cert != "" { - pool, err := getCertPool(tcp.params.Cert) - if err != nil { - return nil, err - } - tlsconfig.RootCAs = pool - } - - tlsConn := tls.Client(conn, tlsconfig) - if err := tlsConn.Handshake(); err != nil { - return nil, err - } - return tlsConn, nil -} - -func (tcp *Tcp) close() error { - tcp.mutex.Lock() - defer tcp.mutex.Unlock() - - var err error - if tcp.conn != nil { - Log(LvlTcpLogTarget, "closing connection", String("addy", tcp.addy)) - close(tcp.monitor) - err = tcp.conn.Close() - tcp.conn = nil - } - return err -} - -// Shutdown stops processing log records after making best effort to flush queue. -func (tcp *Tcp) Shutdown(ctx context.Context) error { - errs := &multierror.Error{} - - Log(LvlTcpLogTarget, "shutting down", String("addy", tcp.addy)) - - if err := tcp.Basic.Shutdown(ctx); err != nil { - errs = multierror.Append(errs, err) - } - - if err := tcp.close(); err != nil { - errs = multierror.Append(errs, err) - } - - close(tcp.shutdown) - return errs.ErrorOrNil() -} - -// Write converts the log record to bytes, via the Formatter, and outputs to the socket. -// Called by dedicated target goroutine and will block until success or shutdown. -func (tcp *Tcp) Write(rec *logr.LogRec) error { - _, stacktrace := tcp.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := tcp.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - - try := 1 - backoff := RetryBackoffMillis - for { - select { - case <-tcp.shutdown: - return err - default: - } - - conn, err := tcp.getConn() - if err != nil { - Log(LvlTcpLogTarget, "failed getting connection", String("addy", tcp.addy), Err(err)) - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err)) - backoff = tcp.sleep(backoff) - continue - } - - conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs)) - _, err = buf.WriteTo(conn) - if err == nil { - return nil - } - - Log(LvlTcpLogTarget, "write error", String("addy", tcp.addy), Err(err)) - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("log target %s write error: %w", tcp.String(), err)) - - _ = tcp.close() - - backoff = tcp.sleep(backoff) - try++ - Log(LvlTcpLogTarget, "retrying write", String("addy", tcp.addy), Int("try", try)) - } -} - -// monitor continuously tries to read from the connection to detect socket close. -// This is needed because TCP target uses a write only socket and Linux systems -// take a long time to detect a loss of connectivity on a socket when only writing; -// the writes simply fail without an error returned. -func monitor(conn net.Conn, done <-chan struct{}, logFunc LogFuncCustom) { - addy := conn.RemoteAddr().String() - defer logFunc(LvlTcpLogTarget, "monitor exiting", String("addy", addy)) - - buf := make([]byte, 1) - for { - logFunc(LvlTcpLogTarget, "monitor loop", String("addy", addy)) - - select { - case <-done: - return - case <-time.After(1 * time.Second): - } - - err := conn.SetReadDeadline(time.Now().Add(time.Second * 30)) - if err != nil { - continue - } - - _, err = conn.Read(buf) - - if errt, ok := err.(net.Error); ok && errt.Timeout() { - // read timeout is expected, keep looping. - continue - } - - // Any other error closes the connection, forcing a reconnect. - logFunc(LvlTcpLogTarget, "monitor closing connection", Err(err)) - conn.Close() - return - } -} - -// String returns a string representation of this target. -func (tcp *Tcp) String() string { - return fmt.Sprintf("TcpTarget[%s:%d]", tcp.params.IP, tcp.params.Port) -} - -func (tcp *Tcp) sleep(backoff int64) int64 { - select { - case <-tcp.shutdown: - case <-time.After(time.Millisecond * time.Duration(backoff)): - } - - nextBackoff := backoff + (backoff >> 1) - if nextBackoff > MaxRetryBackoffMillis { - nextBackoff = MaxRetryBackoffMillis - } - return nextBackoff -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/test-tls-client-cert.pem b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/test-tls-client-cert.pem deleted file mode 100644 index 6ce8d042..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/test-tls-client-cert.pem +++ /dev/null @@ -1,43 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDjzCCAnegAwIBAgIRAPYfRSwdzKopBKxYxKqslJUwDQYJKoZIhvcNAQELBQAw -JzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0xOTAz -MjIwMDE0MTVaFw0yMjAzMDYwMDE0MTVaMDsxOTA3BgNVBAMTME1hdHRlcm1vc3Qs -IEluYy4gSW50ZXJuYWwgSW50ZXJtZWRpYXRlIEF1dGhvcml0eTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMjliRdmvnNL4u/Jr/M2dPwQmTJXEBY/Vq9Q -vAU52X3tRMCPxcaFz+x6ftuvdO2NdohXGAmtx9QU5LZcvFeTDpoVEBo9A+4jtLvD -DZYaTNLpJmoSoJHaDbdWX+OAOqyDiWS741LuiMKWHhew9QOisat2ZINPxjmAd9wE -xthTMgzsv7MUqnMer8U5OGQ0Qy7wAmNRc+2K3qPwkxe2RUvcte50DUFNgxEginsh -vrkOXR383vUCZfu72qu8oggjiQpyTllu5je2Ap6JLjYLkEMiMqrYADuWor/ZHwa6 -WrFqVETxWfAV5u9Eh0wZM/KKYwRQuw9y+Nans77FmUl1tVWWNN8CAwEAAaOBoTCB -njAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQY4Uqswyr2hO/HetZt2RDxJdTIPjBi -BgNVHSMEWzBZgBRFZXVg2Z5tNIsWeWjBLEy2yzKbMKErpCkwJzElMCMGA1UEAwwc -TWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQYIUEifGUOM+bIFZo1tkjZB5YGBr -0xEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEdexL30Q0zBHmPAH8 -LhdK7dbzW1CmILbxRZlKAwRN+hKRXiMW3MHIkhNuoV9Aev602Q+ja4lWsRi/ktOL -ni1FWx5gSScgdG8JGj47dOmoT3vXKX7+umiv4rQLPDl9/DKMuv204OYJq6VT+uNU -6C6kL157jGJEO76H4fMZ8oYsD7Sq0zjiNKtuCYii0ngH3j3gB1jACLqRgveU7MdT -pqOV2KfY31+h8VBtkUvljNztQ9xNY8Fjmt0SMf7E3FaUcaar3ZCr70G5aU3dKbe7 -47vGOBa5tCqw4YK0jgDKid3IJQul9a3J1mSsH8Wy3to9cAV4KGZBQLnzCX15a/+v -3yVh ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDfjCCAmagAwIBAgIUEifGUOM+bIFZo1tkjZB5YGBr0xEwDQYJKoZIhvcNAQEL -BQAwJzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0x -OTAzMjEyMTI4NDNaFw0yOTAzMTgyMTI4NDNaMCcxJTAjBgNVBAMMHE1hdHRlcm1v -c3QsIEluYy4gSW50ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQDH0Xq5rMBGpKOVWTpb5MnaJIWFP/vOtvEk+7hVrfOfe1/5x0Kk3UgAHj85 -otaEZD1Lhn/JLkEqCiE/UXMJFwJDlNcO4CkdKBSpYX4bKAqy5q/X3QwioMSNpJG1 -+YYrNGBH0sgKcKjyCaLhmqYLD0xZDVOmWIYBU9jUPyXw5U0tnsVrTqGMxVkm1xCY -krCWN1ZoUrLvL0MCZc5qpxoPTopr9UO9cqSBSuy6BVWVuEWBZhpqHt+ul8VxhzzY -q1k4l7r2qw+/wm1iJBedTeBVeWNag8JaVfLgu+/W7oJVlPO32Po7pnvHp8iJ3b4K -zXyVHaTX4S6Em+6LV8855TYrShzlAgMBAAGjgaEwgZ4wHQYDVR0OBBYEFEVldWDZ -nm00ixZ5aMEsTLbLMpswMGIGA1UdIwRbMFmAFEVldWDZnm00ixZ5aMEsTLbLMpsw -oSukKTAnMSUwIwYDVQQDDBxNYXR0ZXJtb3N0LCBJbmMuIEludGVybmFsIENBghQS -J8ZQ4z5sgVmjW2SNkHlgYGvTETAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN -BgkqhkiG9w0BAQsFAAOCAQEAPiCWFmopyAkY2T3Zyo4yaRPhX1+VOTMKJtY6EUhq -/GHz6kzEyvCUBf0N892cibGxekrEoItY9NqO6RQRfowg+Gn5kc13z4NyL2W8/eoT -Xy0ZvfaQbU++fQ6pVtWtMblDMU9xiYd7/MDvJpO328l1Vhcdp8kEi+lCvpy0sCRc -PxzPhbgCMAbZEGx+4TMQd4SZKzlRxW/2fflpReh6v1Dv0VDUSYQWwsUnaLpdKHfh -a5k0vuySYcszE4YKlY0zakeFlJfp7fBp1xTwcdW8aTfw15EicPMwTc6xxA4JJUJx -cddu817n1nayK5u6r9Qh1oIVkr0nC9YELMMy4dpPgJ88SA== ------END CERTIFICATE----- diff --git a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/testing.go b/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/testing.go deleted file mode 100644 index 6b41a7e4..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/shared/mlog/testing.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "io" - "strings" - "sync" - "testing" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// testingWriter is an io.Writer that writes through t.Log -type testingWriter struct { - tb testing.TB -} - -func (tw *testingWriter) Write(b []byte) (int, error) { - tw.tb.Log(strings.TrimSpace(string(b))) - return len(b), nil -} - -// NewTestingLogger creates a Logger that proxies logs through a testing interface. -// This allows tests that spin up App instances to avoid spewing logs unless the test fails or -verbose is specified. -func NewTestingLogger(tb testing.TB, writer io.Writer) *Logger { - logWriter := &testingWriter{tb} - multiWriter := io.MultiWriter(logWriter, writer) - logWriterSync := zapcore.AddSync(multiWriter) - - testingLogger := &Logger{ - consoleLevel: zap.NewAtomicLevelAt(getZapLevel("debug")), - fileLevel: zap.NewAtomicLevelAt(getZapLevel("info")), - logrLogger: newLogr(), - mutex: &sync.RWMutex{}, - } - - logWriterCore := zapcore.NewCore(makeEncoder(true, false), zapcore.Lock(logWriterSync), testingLogger.consoleLevel) - - testingLogger.zap = zap.New(logWriterCore, - zap.AddCaller(), - ) - return testingLogger -} diff --git a/vendor/github.com/mattermost/mattermost-server/v5/utils/jsonutils/json.go b/vendor/github.com/mattermost/mattermost-server/v5/utils/jsonutils/json.go deleted file mode 100644 index 9d5e7872..00000000 --- a/vendor/github.com/mattermost/mattermost-server/v5/utils/jsonutils/json.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package jsonutils - -import ( - "bytes" - "encoding/json" - - "github.com/pkg/errors" -) - -type HumanizedJSONError struct { - Err error - Line int - Character int -} - -func (e *HumanizedJSONError) Error() string { - return e.Err.Error() -} - -// HumanizeJSONError extracts error offsets and annotates the error with useful context -func HumanizeJSONError(err error, data []byte) error { - if syntaxError, ok := err.(*json.SyntaxError); ok { - return NewHumanizedJSONError(syntaxError, data, syntaxError.Offset) - } else if unmarshalError, ok := err.(*json.UnmarshalTypeError); ok { - return NewHumanizedJSONError(unmarshalError, data, unmarshalError.Offset) - } else { - return err - } -} - -func NewHumanizedJSONError(err error, data []byte, offset int64) *HumanizedJSONError { - if err == nil { - return nil - } - - if offset < 0 || offset > int64(len(data)) { - return &HumanizedJSONError{ - Err: errors.Wrapf(err, "invalid offset %d", offset), - } - } - - lineSep := []byte{'\n'} - - line := bytes.Count(data[:offset], lineSep) + 1 - lastLineOffset := bytes.LastIndex(data[:offset], lineSep) - character := int(offset) - (lastLineOffset + 1) + 1 - - return &HumanizedJSONError{ - Line: line, - Character: character, - Err: errors.Wrapf(err, "parsing error at line %d, character %d", line, character), - } -} diff --git a/vendor/github.com/olahol/melody/session.go b/vendor/github.com/olahol/melody/session.go index 879a724d..ad79d54f 100644 --- a/vendor/github.com/olahol/melody/session.go +++ b/vendor/github.com/olahol/melody/session.go @@ -223,6 +223,15 @@ func (s *Session) MustGet(key string) interface{} { panic("Key \"" + key + "\" does not exist") } +// UnSet will delete the key and has no return value +func (s *Session) UnSet(key string) { + s.rwmutex.Lock() + defer s.rwmutex.Unlock() + if s.Keys != nil { + delete(s.Keys, key) + } +} + // IsClosed returns the status of the connection. func (s *Session) IsClosed() bool { return s.closed() diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md index 9f8439cc..d53f4397 100644 --- a/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -553,7 +553,7 @@ complete solutions exist out there. ## Versioning -Go-toml follows [Semantic Versioning](http://semver.org/). The supported version +Go-toml follows [Semantic Versioning](https://semver.org). The supported version of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this document. The last two major versions of Go are supported (see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). diff --git a/vendor/github.com/pelletier/go-toml/v2/ci.sh b/vendor/github.com/pelletier/go-toml/v2/ci.sh index d916c5f2..05c76f29 100644 --- a/vendor/github.com/pelletier/go-toml/v2/ci.sh +++ b/vendor/github.com/pelletier/go-toml/v2/ci.sh @@ -77,7 +77,7 @@ cover() { pushd "$dir" go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out.tmp ./... - cat coverage.out.tmp | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out + cat coverage.out.tmp | grep -v fuzz | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out go tool cover -func=coverage.out popd diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go index 07aceb90..6ab1d823 100644 --- a/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -357,9 +357,9 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r if !ctx.inline { b = enc.encodeComment(ctx.indent, options.comment, b) + b = enc.indent(ctx.indent, b) } - b = enc.indent(ctx.indent, b) b = enc.encodeKey(b, ctx.key) b = append(b, " = "...) @@ -577,11 +577,23 @@ func (enc *Encoder) encodeKey(b []byte, k string) []byte { } } -func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - if v.Type().Key().Kind() != reflect.String { - return nil, fmt.Errorf("toml: type %s is not supported as a map key", v.Type().Key().Kind()) +func (enc *Encoder) keyToString(k reflect.Value) (string, error) { + keyType := k.Type() + switch { + case keyType.Kind() == reflect.String: + return k.String(), nil + + case keyType.Implements(textMarshalerType): + keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err) + } + return string(keyB), nil } + return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind()) +} +func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { var ( t table emptyValueOptions valueOptions @@ -589,13 +601,17 @@ func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte iter := v.MapRange() for iter.Next() { - k := iter.Key().String() v := iter.Value() if isNil(v) { continue } + k, err := enc.keyToString(iter.Key()) + if err != nil { + return nil, err + } + if willConvertToTableOrArrayTable(ctx, v) { t.pushTable(k, v, emptyValueOptions) } else { diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index 70f6ec57..39350343 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -60,7 +60,7 @@ func (d *Decoder) DisallowUnknownFields() *Decoder { // are ignored. See Decoder.DisallowUnknownFields() to change this behavior. // // When a TOML local date, time, or date-time is decoded into a time.Time, its -// value is represented in time.Local timezone. Otherwise the approriate Local* +// value is represented in time.Local timezone. Otherwise the appropriate Local* // structure is used. For time values, precision up to the nanosecond is // supported by truncating extra digits. // @@ -417,7 +417,10 @@ func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn h vt := v.Type() // Create the key for the map element. Convert to key type. - mk := reflect.ValueOf(string(key.Node().Data)).Convert(vt.Key()) + mk, err := d.keyFromData(vt.Key(), key.Node().Data) + if err != nil { + return reflect.Value{}, err + } // If the map does not exist, create it. if v.IsNil() { @@ -746,7 +749,7 @@ func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) e } return d.unmarshalInlineTable(itable, elem) default: - return unstable.NewParserError(itable.Data, "cannot store inline table in Go type %s", v.Kind()) + return unstable.NewParserError(d.p.Raw(itable.Raw), "cannot store inline table in Go type %s", v.Kind()) } it := itable.Children() @@ -887,6 +890,11 @@ func init() { } func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error { + kind := v.Kind() + if kind == reflect.Float32 || kind == reflect.Float64 { + return d.unmarshalFloat(value, v) + } + i, err := parseInteger(value.Data) if err != nil { return err @@ -894,7 +902,7 @@ func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error var r reflect.Value - switch v.Kind() { + switch kind { case reflect.Int64: v.SetInt(i) return nil @@ -1004,6 +1012,31 @@ func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Nod return reflect.Value{}, d.handleValue(value, v) } +func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, error) { + switch { + case stringType.AssignableTo(keyType): + return reflect.ValueOf(string(data)), nil + + case stringType.ConvertibleTo(keyType): + return reflect.ValueOf(string(data)).Convert(keyType), nil + + case keyType.Implements(textUnmarshalerType): + mk := reflect.New(keyType.Elem()) + if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { + return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) + } + return mk, nil + + case reflect.PtrTo(keyType).Implements(textUnmarshalerType): + mk := reflect.New(keyType) + if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { + return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) + } + return mk.Elem(), nil + } + return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType) +} + func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { // contains the replacement for v var rv reflect.Value @@ -1014,16 +1047,9 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node case reflect.Map: vt := v.Type() - mk := reflect.ValueOf(string(key.Node().Data)) - mkt := stringType - - keyType := vt.Key() - if !mkt.AssignableTo(keyType) { - if !mkt.ConvertibleTo(keyType) { - return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", mkt, keyType) - } - - mk = mk.Convert(keyType) + mk, err := d.keyFromData(vt.Key(), key.Node().Data) + if err != nil { + return reflect.Value{}, err } // If the map does not exist, create it. @@ -1034,15 +1060,9 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node mv := v.MapIndex(mk) set := false - if !mv.IsValid() { + if !mv.IsValid() || key.IsLast() { set = true mv = reflect.New(v.Type().Elem()).Elem() - } else { - if key.IsLast() { - var x interface{} - mv = reflect.ValueOf(&x).Elem() - set = true - } } nv, err := d.handleKeyValueInner(key, value, mv) @@ -1072,6 +1092,19 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node d.errorContext.Field = path f := fieldByIndex(v, path) + + if !f.CanSet() { + // If the field is not settable, need to take a slower path and make a copy of + // the struct itself to a new location. + nvp := reflect.New(v.Type()) + nvp.Elem().Set(v) + v = nvp.Elem() + _, err := d.handleKeyValuePart(key, value, v) + if err != nil { + return reflect.Value{}, err + } + return nvp.Elem(), nil + } x, err := d.handleKeyValueInner(key, value, f) if err != nil { return reflect.Value{}, err diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go index b60d9bfd..f526bf2c 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go @@ -58,7 +58,7 @@ func (c *Iterator) Node() *Node { // - Table and ArrayTable's children represent a dotted key (same as // KeyValue, but without the first node being the value). // -// When relevant, Raw describes the range of bytes this node is refering to in +// When relevant, Raw describes the range of bytes this node is referring to in // the input document. Use Parser.Raw() to retrieve the actual bytes. type Node struct { Kind Kind diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go index 52db88e7..a8eb0529 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go @@ -49,8 +49,6 @@ func NewParserError(highlight []byte, format string, args ...interface{}) error // For performance reasons, go-toml doesn't make a copy of the input bytes to // the parser. Make sure to copy all the bytes you need to outlive the slice // given to the parser. -// -// The parser doesn't provide nodes for comments yet, nor for whitespace. type Parser struct { data []byte builder builder @@ -58,6 +56,8 @@ type Parser struct { left []byte err error first bool + + KeepComments bool } // Data returns the slice provided to the last call to Reset. @@ -132,16 +132,54 @@ func (p *Parser) NextExpression() bool { } // Expression returns a pointer to the node representing the last successfully -// parsed expresion. +// parsed expression. func (p *Parser) Expression() *Node { return p.builder.NodeAt(p.ref) } -// Error returns any error that has occured during parsing. +// Error returns any error that has occurred during parsing. func (p *Parser) Error() error { return p.err } +// Position describes a position in the input. +type Position struct { + // Number of bytes from the beginning of the input. + Offset int + // Line number, starting at 1. + Line int + // Column number, starting at 1. + Column int +} + +// Shape describes the position of a range in the input. +type Shape struct { + Start Position + End Position +} + +func (p *Parser) position(b []byte) Position { + offset := danger.SubsliceOffset(p.data, b) + + lead := p.data[:offset] + + return Position{ + Offset: offset, + Line: bytes.Count(lead, []byte{'\n'}) + 1, + Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}), + } +} + +// Shape returns the shape of the given range in the input. Will +// panic if the range is not a subslice of the input. +func (p *Parser) Shape(r Range) Shape { + raw := p.Raw(r) + return Shape{ + Start: p.position(raw), + End: p.position(raw[r.Length:]), + } +} + func (p *Parser) parseNewline(b []byte) ([]byte, error) { if b[0] == '\n' { return b[1:], nil @@ -155,6 +193,19 @@ func (p *Parser) parseNewline(b []byte) ([]byte, error) { return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) } +func (p *Parser) parseComment(b []byte) (reference, []byte, error) { + ref := invalidReference + data, rest, err := scanComment(b) + if p.KeepComments && err == nil { + ref = p.builder.Push(Node{ + Kind: Comment, + Raw: p.Range(data), + Data: data, + }) + } + return ref, rest, err +} + func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { // expression = ws [ comment ] // expression =/ ws keyval ws [ comment ] @@ -168,7 +219,7 @@ func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { } if b[0] == '#' { - _, rest, err := scanComment(b) + ref, rest, err := p.parseComment(b) return ref, rest, err } @@ -190,7 +241,10 @@ func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { b = p.parseWhitespace(b) if len(b) > 0 && b[0] == '#' { - _, rest, err := scanComment(b) + cref, rest, err := p.parseComment(b) + if cref != invalidReference { + p.builder.Chain(ref, cref) + } return ref, rest, err } @@ -402,6 +456,7 @@ func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] parent := p.builder.Push(Node{ Kind: InlineTable, + Raw: p.Range(b[:1]), }) first := true @@ -470,17 +525,33 @@ func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { Kind: Array, }) + // First indicates whether the parser is looking for the first element + // (non-comment) of the array. first := true - var lastChild reference + lastChild := invalidReference + + addChild := func(valueRef reference) { + if lastChild == invalidReference { + p.builder.AttachChild(parent, valueRef) + } else { + p.builder.Chain(lastChild, valueRef) + } + lastChild = valueRef + } var err error for len(b) > 0 { - b, err = p.parseOptionalWhitespaceCommentNewline(b) + cref := invalidReference + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } + if cref != invalidReference { + addChild(cref) + } + if len(b) == 0 { return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") } @@ -495,10 +566,13 @@ func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { } b = b[1:] - b, err = p.parseOptionalWhitespaceCommentNewline(b) + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } + if cref != invalidReference { + addChild(cref) + } } else if !first { return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") } @@ -514,17 +588,16 @@ func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { return parent, nil, err } - if first { - p.builder.AttachChild(parent, valueRef) - } else { - p.builder.Chain(lastChild, valueRef) - } - lastChild = valueRef + addChild(valueRef) - b, err = p.parseOptionalWhitespaceCommentNewline(b) + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } + if cref != invalidReference { + addChild(cref) + } + first = false } @@ -533,15 +606,34 @@ func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { return parent, rest, err } -func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { +func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) { + rootCommentRef := invalidReference + latestCommentRef := invalidReference + + addComment := func(ref reference) { + if rootCommentRef == invalidReference { + rootCommentRef = ref + } else if latestCommentRef == invalidReference { + p.builder.AttachChild(rootCommentRef, ref) + latestCommentRef = ref + } else { + p.builder.Chain(latestCommentRef, ref) + latestCommentRef = ref + } + } + for len(b) > 0 { var err error b = p.parseWhitespace(b) if len(b) > 0 && b[0] == '#' { - _, b, err = scanComment(b) + var ref reference + ref, b, err = p.parseComment(b) if err != nil { - return nil, err + return invalidReference, nil, err + } + if ref != invalidReference { + addComment(ref) } } @@ -552,14 +644,14 @@ func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) if b[0] == '\n' || b[0] == '\r' { b, err = p.parseNewline(b) if err != nil { - return nil, err + return invalidReference, nil, err } } else { break } } - return b, nil + return rootCommentRef, b, nil } func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go index af22ebbe..0512181d 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go @@ -151,7 +151,6 @@ func scanWhitespace(b []byte) ([]byte, []byte) { return b, b[len(b):] } -//nolint:unparam func scanComment(b []byte) ([]byte, []byte, error) { // comment-start-symbol = %x23 ; # // non-ascii = %x80-D7FF / %xE000-10FFFF diff --git a/vendor/github.com/rs/xid/.golangci.yml b/vendor/github.com/rs/xid/.golangci.yml new file mode 100644 index 00000000..7929600a --- /dev/null +++ b/vendor/github.com/rs/xid/.golangci.yml @@ -0,0 +1,5 @@ +run: + tests: false + +output: + sort-results: true diff --git a/vendor/github.com/rs/xid/README.md b/vendor/github.com/rs/xid/README.md index 5bf462e8..974e67d2 100644 --- a/vendor/github.com/rs/xid/README.md +++ b/vendor/github.com/rs/xid/README.md @@ -70,6 +70,9 @@ References: - Ruby port by [Valar](https://github.com/valarpirai/): https://github.com/valarpirai/ruby_xid - Java port by [0xShamil](https://github.com/0xShamil/): https://github.com/0xShamil/java-xid - Dart port by [Peter Bwire](https://github.com/pitabwire): https://pub.dev/packages/xid +- PostgreSQL port by [Rasmus Holm](https://github.com/crholm): https://github.com/modfin/pg-xid +- Swift port by [Uditha Atukorala](https://github.com/uditha-atukorala): https://github.com/uditha-atukorala/swift-xid +- C++ port by [Uditha Atukorala](https://github.com/uditha-atukorala): https://github.com/uditha-atukorala/libxid ## Install diff --git a/vendor/github.com/rs/xid/id.go b/vendor/github.com/rs/xid/id.go index 1f536b41..fcd7a041 100644 --- a/vendor/github.com/rs/xid/id.go +++ b/vendor/github.com/rs/xid/id.go @@ -43,7 +43,7 @@ package xid import ( "bytes" - "crypto/md5" + "crypto/sha256" "crypto/rand" "database/sql/driver" "encoding/binary" @@ -72,13 +72,11 @@ const ( ) var ( - // objectIDCounter is atomically incremented when generating a new ObjectId - // using NewObjectId() function. It's used as a counter part of an id. - // This id is initialized with a random value. + // objectIDCounter is atomically incremented when generating a new ObjectId. It's + // used as the counter part of an id. This id is initialized with a random value. objectIDCounter = randInt() - // machineId stores machine id generated once and used in subsequent calls - // to NewObjectId function. + // machineID is generated once and used in subsequent calls to the New* functions. machineID = readMachineID() // pid stores the current process id @@ -107,9 +105,9 @@ func init() { } } -// readMachineId generates machine id and puts it into the machineId global -// variable. If this function fails to get the hostname, it will cause -// a runtime error. +// readMachineID generates a machine ID, derived from a platform-specific machine ID +// value, or else the machine's hostname, or else a randomly-generated number. +// It panics if all of these methods fail. func readMachineID() []byte { id := make([]byte, 3) hid, err := readPlatformMachineID() @@ -117,7 +115,7 @@ func readMachineID() []byte { hid, err = os.Hostname() } if err == nil && len(hid) != 0 { - hw := md5.New() + hw := sha256.New() hw.Write([]byte(hid)) copy(id, hw.Sum(nil)) } else { @@ -148,7 +146,7 @@ func NewWithTime(t time.Time) ID { var id ID // Timestamp, 4 bytes, big endian binary.BigEndian.PutUint32(id[:], uint32(t.Unix())) - // Machine, first 3 bytes of md5(hostname) + // Machine ID, 3 bytes id[4] = machineID[0] id[5] = machineID[1] id[6] = machineID[2] @@ -239,6 +237,7 @@ func (id *ID) UnmarshalText(text []byte) error { } } if !decode(id, text) { + *id = nilID return ErrInvalidID } return nil @@ -264,6 +263,10 @@ func decode(id *ID, src []byte) bool { _ = id[11] id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4 + // check the last byte + if encoding[(id[11]<<4)&0x1F] != src[19] { + return false + } id[10] = dec[src[16]]<<3 | dec[src[17]]>>2 id[9] = dec[src[14]]<<5 | dec[src[15]] id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3 @@ -275,16 +278,7 @@ func decode(id *ID, src []byte) bool { id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 - - // Validate that there are no discarer bits (padding) in src that would - // cause the string-encoded id not to equal src. - var check [4]byte - - check[3] = encoding[(id[11]<<4)&0x1F] - check[2] = encoding[(id[11]>>1)&0x1F] - check[1] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F] - check[0] = encoding[id[10]>>3] - return bytes.Equal([]byte(src[16:20]), check[:]) + return true } // Time returns the timestamp part of the id. @@ -344,6 +338,11 @@ func (id ID) IsNil() bool { return id == nilID } +// Alias of IsNil +func (id ID) IsZero() bool { + return id.IsNil() +} + // NilID returns a zero value for `xid.ID`. func NilID() ID { return nilID diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index b042c896..d1d4a85f 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -9,7 +9,7 @@ the last thing you want from your Logging library (again...). This does not mean Logrus is dead. Logrus will continue to be maintained for security, (backwards compatible) bug fixes, and performance (where we are -limited by the interface). +limited by the interface). I believe Logrus' biggest contribution is to have played a part in today's widespread use of structured logging in Golang. There doesn't seem to be a @@ -43,7 +43,7 @@ plain text): With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash or Splunk: -```json +```text {"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} @@ -99,7 +99,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: +environment via benchmarks: ``` go test -bench=.*CallerTracing ``` @@ -317,6 +317,8 @@ log.SetLevel(log.InfoLevel) It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose environment if your application has that. +Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging). + #### Entries Besides the fields added with `WithField` or `WithFields` some fields are diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go index 72e8e3a1..074fd4b8 100644 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -4,6 +4,7 @@ import ( "bufio" "io" "runtime" + "strings" ) // Writer at INFO level. See WriterLevel for details. @@ -20,15 +21,18 @@ func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { return NewEntry(logger).WriterLevel(level) } +// Writer returns an io.Writer that writes to the logger at the info log level func (entry *Entry) Writer() *io.PipeWriter { return entry.WriterLevel(InfoLevel) } +// WriterLevel returns an io.Writer that writes to the logger at the given log level func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() var printFunc func(args ...interface{}) + // Determine which log function to use based on the specified log level switch level { case TraceLevel: printFunc = entry.Trace @@ -48,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { printFunc = entry.Print } + // Start a new goroutine to scan the input and write it to the logger using the specified print function. + // It splits the input into chunks of up to 64KB to avoid buffer overflows. go entry.writerScanner(reader, printFunc) + + // Set a finalizer function to close the writer when it is garbage collected runtime.SetFinalizer(writer, writerFinalizer) return writer } +// writerScanner scans the input from the reader and writes it to the logger func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { scanner := bufio.NewScanner(reader) + + // Set the buffer size to the maximum token size to avoid buffer overflows + scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize) + + // Define a split function to split the input into chunks of up to 64KB + chunkSize := bufio.MaxScanTokenSize // 64KB + splitFunc := func(data []byte, atEOF bool) (int, []byte, error) { + if len(data) >= chunkSize { + return chunkSize, data[:chunkSize], nil + } + + return bufio.ScanLines(data, atEOF) + } + + // Use the custom split function to split the input + scanner.Split(splitFunc) + + // Scan the input and write it to the logger using the specified print function for scanner.Scan() { - printFunc(scanner.Text()) + printFunc(strings.TrimRight(scanner.Text(), "\r\n")) } + + // If there was an error while scanning the input, log an error if err := scanner.Err(); err != nil { entry.Errorf("Error while reading from Writer: %s", err) } + + // Close the reader when we are done reader.Close() } +// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected func writerFinalizer(writer *io.PipeWriter) { writer.Close() } diff --git a/vendor/github.com/slack-go/slack/block.go b/vendor/github.com/slack-go/slack/block.go index 240f5527..18222bd2 100644 --- a/vendor/github.com/slack-go/slack/block.go +++ b/vendor/github.com/slack-go/slack/block.go @@ -50,6 +50,7 @@ type BlockAction struct { SelectedConversations []string `json:"selected_conversations"` SelectedDate string `json:"selected_date"` SelectedTime string `json:"selected_time"` + SelectedDateTime int64 `json:"selected_date_time"` InitialOption OptionBlockObject `json:"initial_option"` InitialUser string `json:"initial_user"` InitialChannel string `json:"initial_channel"` diff --git a/vendor/github.com/slack-go/slack/block_conv.go b/vendor/github.com/slack-go/slack/block_conv.go index 555e4811..4ab58de3 100644 --- a/vendor/github.com/slack-go/slack/block_conv.go +++ b/vendor/github.com/slack-go/slack/block_conv.go @@ -110,6 +110,8 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error { e = &DatePickerBlockElement{} case "timepicker": e = &TimePickerBlockElement{} + case "datetimepicker": + e = &DateTimePickerBlockElement{} case "plain_text_input": e = &PlainTextInputBlockElement{} case "email_text_input": @@ -190,6 +192,8 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error { blockElement = &DatePickerBlockElement{} case "timepicker": blockElement = &TimePickerBlockElement{} + case "datetimepicker": + blockElement = &DateTimePickerBlockElement{} case "plain_text_input": blockElement = &PlainTextInputBlockElement{} case "email_text_input": @@ -233,6 +237,7 @@ func (a *Accessory) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the Unmarshaller interface for Accessory, so that any JSON // unmarshalling is delegated and proper type determination can be made before unmarshal +// Note: datetimepicker is not supported in Accessory func (a *Accessory) UnmarshalJSON(data []byte) error { var r json.RawMessage diff --git a/vendor/github.com/slack-go/slack/block_element.go b/vendor/github.com/slack-go/slack/block_element.go index aba29c6b..a70d8f2d 100644 --- a/vendor/github.com/slack-go/slack/block_element.go +++ b/vendor/github.com/slack-go/slack/block_element.go @@ -9,6 +9,7 @@ const ( METOverflow MessageElementType = "overflow" METDatepicker MessageElementType = "datepicker" METTimepicker MessageElementType = "timepicker" + METDatetimepicker MessageElementType = "datetimepicker" METPlainTextInput MessageElementType = "plain_text_input" METRadioButtons MessageElementType = "radio_buttons" METEmailTextInput MessageElementType = "email_text_input" @@ -392,6 +393,29 @@ func NewTimePickerBlockElement(actionID string) *TimePickerBlockElement { } } +// DateTimePickerBlockElement defines an element that allows the selection of both +// a date and a time of day formatted as a UNIX timestamp. +// More Information: https://api.slack.com/reference/messaging/block-elements#datetimepicker +type DateTimePickerBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + InitialDateTime int64 `json:"initial_date_time,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s DateTimePickerBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewDatePickerBlockElement returns an instance of a datetime picker element +func NewDateTimePickerBlockElement(actionID string) *DateTimePickerBlockElement { + return &DateTimePickerBlockElement{ + Type: METDatetimepicker, + ActionID: actionID, + } +} + // EmailTextInputBlockElement creates a field where a user can enter email // data. // email-text-input elements are currently only available in modals. diff --git a/vendor/github.com/slack-go/slack/block_rich_text.go b/vendor/github.com/slack-go/slack/block_rich_text.go index 555a6198..01e5cdb9 100644 --- a/vendor/github.com/slack-go/slack/block_rich_text.go +++ b/vendor/github.com/slack-go/slack/block_rich_text.go @@ -294,7 +294,7 @@ func NewRichTextSectionLinkElement(url, text string, style *RichTextSectionTextS type RichTextSectionTeamElement struct { Type RichTextSectionElementType `json:"type"` TeamID string `json:"team_id"` - Style *RichTextSectionTextStyle `json:"style.omitempty"` + Style *RichTextSectionTextStyle `json:"style,omitempty"` } func (r RichTextSectionTeamElement) RichTextSectionElementType() RichTextSectionElementType { diff --git a/vendor/github.com/slack-go/slack/messages.go b/vendor/github.com/slack-go/slack/messages.go index 33340456..13a1ede9 100644 --- a/vendor/github.com/slack-go/slack/messages.go +++ b/vendor/github.com/slack-go/slack/messages.go @@ -100,10 +100,11 @@ type Msg struct { Members []string `json:"members,omitempty"` // channels.replies, groups.replies, im.replies, mpim.replies - ReplyCount int `json:"reply_count,omitempty"` - Replies []Reply `json:"replies,omitempty"` - ParentUserId string `json:"parent_user_id,omitempty"` - LatestReply string `json:"latest_reply,omitempty"` + ReplyCount int `json:"reply_count,omitempty"` + ReplyUsers []string `json:"reply_users,omitempty"` + Replies []Reply `json:"replies,omitempty"` + ParentUserId string `json:"parent_user_id,omitempty"` + LatestReply string `json:"latest_reply,omitempty"` // file_share, file_comment, file_mention Files []File `json:"files,omitempty"` diff --git a/vendor/github.com/slack-go/slack/users.go b/vendor/github.com/slack-go/slack/users.go index 55f42118..a3b4c066 100644 --- a/vendor/github.com/slack-go/slack/users.go +++ b/vendor/github.com/slack-go/slack/users.go @@ -130,6 +130,7 @@ type User struct { IsAppUser bool `json:"is_app_user"` IsInvitedUser bool `json:"is_invited_user"` Has2FA bool `json:"has_2fa"` + TwoFactorType *string `json:"two_factor_type"` HasFiles bool `json:"has_files"` Presence string `json:"presence"` Locale string `json:"locale"` diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go index 199480cd..39f65852 100644 --- a/vendor/github.com/spf13/afero/afero.go +++ b/vendor/github.com/spf13/afero/afero.go @@ -97,7 +97,7 @@ type Fs interface { // Chown changes the uid and gid of the named file. Chown(name string, uid, gid int) error - //Chtimes changes the access and modification times of the named file + // Chtimes changes the access and modification times of the named file Chtimes(name string, atime time.Time, mtime time.Time) error } diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go index 70a1d916..2e72793a 100644 --- a/vendor/github.com/spf13/afero/basepath.go +++ b/vendor/github.com/spf13/afero/basepath.go @@ -40,7 +40,6 @@ func (f *BasePathFile) Name() string { func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) { if rdf, ok := f.File.(fs.ReadDirFile); ok { return rdf.ReadDir(n) - } return readDirFile{f.File}.ReadDir(n) } diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go index 6ff8f309..184d6dd7 100644 --- a/vendor/github.com/spf13/afero/copyOnWriteFs.go +++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go @@ -223,7 +223,7 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, return nil, err } if isaDir { - if err = u.layer.MkdirAll(dir, 0777); err != nil { + if err = u.layer.MkdirAll(dir, 0o777); err != nil { return nil, err } return u.layer.OpenFile(name, flag, perm) @@ -247,8 +247,9 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, // This function handles the 9 different possibilities caused // by the union which are the intersection of the following... -// layer: doesn't exist, exists as a file, and exists as a directory -// base: doesn't exist, exists as a file, and exists as a directory +// +// layer: doesn't exist, exists as a file, and exists as a directory +// base: doesn't exist, exists as a file, and exists as a directory func (u *CopyOnWriteFs) Open(name string) (File, error) { // Since the overlay overrides the base we check that first b, err := u.isBaseFile(name) @@ -322,5 +323,5 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { } func (u *CopyOnWriteFs) Create(name string) (File, error) { - return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) + return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666) } diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go index 386c9cdc..fa6abe1e 100644 --- a/vendor/github.com/spf13/afero/ioutil.go +++ b/vendor/github.com/spf13/afero/ioutil.go @@ -141,8 +141,10 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error { // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. -var randNum uint32 -var randmu sync.Mutex +var ( + randNum uint32 + randmu sync.Mutex +) func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) @@ -190,7 +192,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { nconflict := 0 for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextRandom()+suffix) - f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() @@ -214,6 +216,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { func (a Afero) TempDir(dir, prefix string) (name string, err error) { return TempDir(a.Fs, dir, prefix) } + func TempDir(fs Fs, dir, prefix string) (name string, err error) { if dir == "" { dir = os.TempDir() @@ -222,7 +225,7 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) { nconflict := 0 for i := 0; i < 10000; i++ { try := filepath.Join(dir, prefix+nextRandom()) - err = fs.Mkdir(try, 0700) + err = fs.Mkdir(try, 0o700) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go index 3cf4693b..62fe4498 100644 --- a/vendor/github.com/spf13/afero/mem/file.go +++ b/vendor/github.com/spf13/afero/mem/file.go @@ -245,7 +245,7 @@ func (f *File) Truncate(size int64) error { defer f.fileData.Unlock() if size > int64(len(f.fileData.data)) { diff := size - int64(len(f.fileData.data)) - f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) + f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{0o0}, int(diff))...) } else { f.fileData.data = f.fileData.data[0:size] } @@ -285,7 +285,7 @@ func (f *File) Write(b []byte) (n int, err error) { tail = f.fileData.data[n+int(cur):] } if diff > 0 { - f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...) + f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...) f.fileData.data = append(f.fileData.data, tail...) } else { f.fileData.data = append(f.fileData.data[:cur], b...) @@ -321,16 +321,19 @@ func (s *FileInfo) Name() string { s.Unlock() return name } + func (s *FileInfo) Mode() os.FileMode { s.Lock() defer s.Unlock() return s.mode } + func (s *FileInfo) ModTime() time.Time { s.Lock() defer s.Unlock() return s.modtime } + func (s *FileInfo) IsDir() bool { s.Lock() defer s.Unlock() diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index d06975e7..e6b7d70b 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -15,6 +15,7 @@ package afero import ( "fmt" + "io" "log" "os" "path/filepath" @@ -43,7 +44,7 @@ func (m *MemMapFs) getData() map[string]*mem.FileData { // Root should always exist, right? // TODO: what about windows? root := mem.CreateDir(FilePathSeparator) - mem.SetMode(root, os.ModeDir|0755) + mem.SetMode(root, os.ModeDir|0o755) m.data[FilePathSeparator] = root }) return m.data @@ -96,12 +97,12 @@ func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) { pdir := filepath.Dir(filepath.Clean(f.Name())) err := m.lockfreeMkdir(pdir, perm) if err != nil { - //log.Println("Mkdir error:", err) + // log.Println("Mkdir error:", err) return } parent, err = m.lockfreeOpen(pdir) if err != nil { - //log.Println("Open after Mkdir error:", err) + // log.Println("Open after Mkdir error:", err) return } } @@ -237,7 +238,7 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data()) } if flag&os.O_APPEND > 0 { - _, err = file.Seek(0, os.SEEK_END) + _, err = file.Seek(0, io.SeekEnd) if err != nil { file.Close() return nil, err @@ -319,6 +320,18 @@ func (m *MemMapFs) Rename(oldname, newname string) error { } else { return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound} } + + for p, fileData := range m.getData() { + if strings.HasPrefix(p, oldname+FilePathSeparator) { + m.mu.RUnlock() + m.mu.Lock() + delete(m.getData(), p) + p := strings.Replace(p, oldname, newname, 1) + m.getData()[p] = fileData + m.mu.Unlock() + m.mu.RLock() + } + } return nil } diff --git a/vendor/github.com/spf13/afero/regexpfs.go b/vendor/github.com/spf13/afero/regexpfs.go index ac359c62..218f3b23 100644 --- a/vendor/github.com/spf13/afero/regexpfs.go +++ b/vendor/github.com/spf13/afero/regexpfs.go @@ -10,7 +10,6 @@ import ( // The RegexpFs filters files (not directories) by regular expression. Only // files matching the given regexp will be allowed, all others get a ENOENT error ( // "No such file or directory"). -// type RegexpFs struct { re *regexp.Regexp source Fs diff --git a/vendor/github.com/spf13/afero/symlink.go b/vendor/github.com/spf13/afero/symlink.go index d1c6ea53..aa6ae125 100644 --- a/vendor/github.com/spf13/afero/symlink.go +++ b/vendor/github.com/spf13/afero/symlink.go @@ -21,9 +21,9 @@ import ( // filesystems saying so. // It indicates support for 3 symlink related interfaces that implement the // behaviors of the os methods: -// - Lstat -// - Symlink, and -// - Readlink +// - Lstat +// - Symlink, and +// - Readlink type Symlinker interface { Lstater Linker diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go index 333d367f..62dd6c93 100644 --- a/vendor/github.com/spf13/afero/unionFile.go +++ b/vendor/github.com/spf13/afero/unionFile.go @@ -47,7 +47,7 @@ func (f *UnionFile) Read(s []byte) (int, error) { if (err == nil || err == io.EOF) && f.Base != nil { // advance the file position also in the base file, the next // call may be a write at this position (or a seek with SEEK_CUR) - if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil { + if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil { // only overwrite err in case the seek fails: we need to // report an eventual io.EOF to the caller err = seekErr @@ -130,7 +130,7 @@ func (f *UnionFile) Name() string { type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) { - var files = make(map[string]os.FileInfo) + files := make(map[string]os.FileInfo) for _, fi := range lofi { files[fi.Name()] = fi @@ -151,7 +151,6 @@ var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, err } return rfi, nil - } // Readdir will weave the two directories together and @@ -275,7 +274,7 @@ func copyFile(base Fs, layer Fs, name string, bfh File) error { return err } if !exists { - err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? + err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME? if err != nil { return err } diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go index cb7de23f..9e4cba27 100644 --- a/vendor/github.com/spf13/afero/util.go +++ b/vendor/github.com/spf13/afero/util.go @@ -43,7 +43,7 @@ func WriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { if err != os.ErrExist { return err @@ -71,7 +71,7 @@ func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { return } @@ -124,7 +124,7 @@ func GetTempDir(fs Fs, subPath string) string { return addSlash(dir) } - err := fs.MkdirAll(dir, 0777) + err := fs.MkdirAll(dir, 0o777) if err != nil { panic(err) } @@ -197,7 +197,6 @@ func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, err // readerContains reports whether any of the subslices is within r. func readerContainsAny(r io.Reader, subslices ...[]byte) bool { - if r == nil || len(subslices) == 0 { return false } diff --git a/vendor/github.com/spf13/cast/README.md b/vendor/github.com/spf13/cast/README.md index 120a5734..58141f02 100644 --- a/vendor/github.com/spf13/cast/README.md +++ b/vendor/github.com/spf13/cast/README.md @@ -1,7 +1,8 @@ -cast -==== -[![GoDoc](https://godoc.org/github.com/spf13/cast?status.svg)](https://godoc.org/github.com/spf13/cast) -[![Build Status](https://github.com/spf13/cast/actions/workflows/go.yml/badge.svg)](https://github.com/spf13/cast/actions/workflows/go.yml) +# cast + +[![Build Status](https://github.com/spf13/cast/actions/workflows/ci.yml/badge.svg)](https://github.com/spf13/cast/actions/workflows/ci.yml) +[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/cast)](https://pkg.go.dev/mod/github.com/spf13/cast) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast)](https://goreportcard.com/report/github.com/spf13/cast) Easy and safe casting from one type to another in Go @@ -17,7 +18,7 @@ interface into a bool, etc. Cast does this intelligently when an obvious conversion is possible. It doesn’t make any attempts to guess what you meant, for example you can only convert a string to an int when it is a string representation of an int such as “8”. Cast was developed for use in -[Hugo](http://hugo.spf13.com), a website engine which uses YAML, TOML or JSON +[Hugo](https://gohugo.io), a website engine which uses YAML, TOML or JSON for meta data. ## Why use Cast? @@ -72,4 +73,3 @@ the code for a complete set. var eight interface{} = 8 cast.ToInt(eight) // 8 cast.ToInt(nil) // 0 - diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go index 514d759b..d49bbf83 100644 --- a/vendor/github.com/spf13/cast/caste.go +++ b/vendor/github.com/spf13/cast/caste.go @@ -98,10 +98,31 @@ func ToBoolE(i interface{}) (bool, error) { case nil: return false, nil case int: - if i.(int) != 0 { - return true, nil - } - return false, nil + return b != 0, nil + case int64: + return b != 0, nil + case int32: + return b != 0, nil + case int16: + return b != 0, nil + case int8: + return b != 0, nil + case uint: + return b != 0, nil + case uint64: + return b != 0, nil + case uint32: + return b != 0, nil + case uint16: + return b != 0, nil + case uint8: + return b != 0, nil + case float64: + return b != 0, nil + case float32: + return b != 0, nil + case time.Duration: + return b != 0, nil case string: return strconv.ParseBool(i.(string)) case json.Number: @@ -1385,6 +1406,8 @@ func (f timeFormat) hasTimezone() bool { var ( timeFormats = []timeFormat{ + // Keep common formats at the top. + {"2006-01-02", timeFormatNoTimezone}, {time.RFC3339, timeFormatNumericTimezone}, {"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone {time.RFC1123Z, timeFormatNumericTimezone}, @@ -1400,7 +1423,6 @@ var ( {time.UnixDate, timeFormatNamedTimezone}, {time.RubyDate, timeFormatNumericTimezone}, {"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone}, - {"2006-01-02", timeFormatNoTimezone}, {"02 Jan 2006", timeFormatNoTimezone}, {"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone}, {"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone}, diff --git a/vendor/github.com/spf13/viper/.golangci.yaml b/vendor/github.com/spf13/viper/.golangci.yaml index 16e03965..acd9eeba 100644 --- a/vendor/github.com/spf13/viper/.golangci.yaml +++ b/vendor/github.com/spf13/viper/.golangci.yaml @@ -16,7 +16,6 @@ linters: disable-all: true enable: - bodyclose - - deadcode - dogsled - dupl - durationcheck @@ -43,14 +42,12 @@ linters: - rowserrcheck - sqlclosecheck - staticcheck - - structcheck - stylecheck - tparallel - typecheck - unconvert - unparam - unused - - varcheck - wastedassign - whitespace @@ -83,6 +80,11 @@ linters: # - goheader # - gomodguard + # deprecated + # - deadcode + # - structcheck + # - varcheck + # don't enable: # - asciicheck # - funlen diff --git a/vendor/github.com/spf13/viper/Makefile b/vendor/github.com/spf13/viper/Makefile index 3f4234d3..e8d3baaa 100644 --- a/vendor/github.com/spf13/viper/Makefile +++ b/vendor/github.com/spf13/viper/Makefile @@ -15,8 +15,8 @@ TEST_FORMAT = short-verbose endif # Dependency versions -GOTESTSUM_VERSION = 1.8.0 -GOLANGCI_VERSION = 1.50.1 +GOTESTSUM_VERSION = 1.9.0 +GOLANGCI_VERSION = 1.52.2 # Add the ability to override some variables # Use with care diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index cd392905..4184d2a1 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -27,6 +27,9 @@ Many Go projects are built using Viper including: * [doctl](https://github.com/digitalocean/doctl) * [Clairctl](https://github.com/jgsqware/clairctl) * [Mercure](https://mercure.rocks) +* [Meshery](https://github.com/meshery/meshery) +* [Bearer](https://github.com/bearer/bearer) +* [Coder](https://github.com/coder/coder) ## Install diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index 64e65750..95009a14 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -31,6 +31,11 @@ func (pe ConfigParseError) Error() string { return fmt.Sprintf("While parsing config: %s", pe.err.Error()) } +// Unwrap returns the wrapped error. +func (pe ConfigParseError) Unwrap() error { + return pe.err +} + // toCaseInsensitiveValue checks if the value is a map; // if so, create a copy and lower-case the keys recursively. func toCaseInsensitiveValue(value interface{}) interface{} { diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index 06610fc5..7fb1e191 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -25,7 +25,6 @@ import ( "errors" "fmt" "io" - "log" "os" "path/filepath" "reflect" @@ -206,6 +205,7 @@ type Viper struct { envKeyReplacer StringReplacer allowEmptyEnv bool + parents []string config map[string]interface{} override map[string]interface{} defaults map[string]interface{} @@ -232,6 +232,7 @@ func New() *Viper { v.configPermissions = os.FileMode(0o644) v.fs = afero.NewOsFs() v.config = make(map[string]interface{}) + v.parents = []string{} v.override = make(map[string]interface{}) v.defaults = make(map[string]interface{}) v.kvstore = make(map[string]interface{}) @@ -439,13 +440,14 @@ func (v *Viper) WatchConfig() { go func() { watcher, err := newWatcher() if err != nil { - log.Fatal(err) + v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err)) + os.Exit(1) } defer watcher.Close() // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way filename, err := v.getConfigFile() if err != nil { - log.Printf("error: %v\n", err) + v.logger.Error(fmt.Sprintf("get config file: %s", err)) initWG.Done() return } @@ -474,7 +476,7 @@ func (v *Viper) WatchConfig() { realConfigFile = currentConfigFile err := v.ReadInConfig() if err != nil { - log.Printf("error reading config file: %v\n", err) + v.logger.Error(fmt.Sprintf("read config file: %s", err)) } if v.onConfigChange != nil { v.onConfigChange(event) @@ -486,7 +488,7 @@ func (v *Viper) WatchConfig() { case err, ok := <-watcher.Errors: if ok { // 'Errors' channel is not closed - log.Printf("watcher error: %v\n", err) + v.logger.Error(fmt.Sprintf("watcher error: %s", err)) } eventsWG.Done() return @@ -928,6 +930,8 @@ func (v *Viper) Get(key string) interface{} { return cast.ToStringSlice(val) case []int: return cast.ToIntSlice(val) + case []time.Duration: + return cast.ToDurationSlice(val) } } @@ -946,6 +950,10 @@ func (v *Viper) Sub(key string) *Viper { } if reflect.TypeOf(data).Kind() == reflect.Map { + subv.parents = append(v.parents, strings.ToLower(key)) + subv.automaticEnvApplied = v.automaticEnvApplied + subv.envPrefix = v.envPrefix + subv.envKeyReplacer = v.envKeyReplacer subv.config = cast.ToStringMap(data) return subv } @@ -1099,7 +1107,7 @@ func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) } -// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot +// defaultDecoderConfig returns default mapstructure.DecoderConfig with support // of time.Duration values & string slices func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { c := &mapstructure.DecoderConfig{ @@ -1274,8 +1282,15 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { s = strings.TrimSuffix(s, "]") res, _ := readAsCSV(s) return cast.ToIntSlice(res) + case "durationSlice": + s := strings.TrimPrefix(flag.ValueString(), "[") + s = strings.TrimSuffix(s, "]") + slice := strings.Split(s, ",") + return cast.ToDurationSlice(slice) case "stringToString": return stringToStringConv(flag.ValueString()) + case "stringToInt": + return stringToIntConv(flag.ValueString()) default: return flag.ValueString() } @@ -1286,9 +1301,10 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { // Env override next if v.automaticEnvApplied { + envKey := strings.Join(append(v.parents, lcaseKey), ".") // even if it hasn't been registered, if automaticEnv is used, // check any Get request - if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok { + if val, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok { return val } if nested && v.isPathShadowedInAutoEnv(path) != "" { @@ -1355,6 +1371,13 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { return cast.ToIntSlice(res) case "stringToString": return stringToStringConv(flag.ValueString()) + case "stringToInt": + return stringToIntConv(flag.ValueString()) + case "durationSlice": + s := strings.TrimPrefix(flag.ValueString(), "[") + s = strings.TrimSuffix(s, "]") + slice := strings.Split(s, ",") + return cast.ToDurationSlice(slice) default: return flag.ValueString() } @@ -1398,6 +1421,30 @@ func stringToStringConv(val string) interface{} { return out } +// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68 +// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap +func stringToIntConv(val string) interface{} { + val = strings.Trim(val, "[]") + // An empty string would cause an empty map + if len(val) == 0 { + return map[string]interface{}{} + } + ss := strings.Split(val, ",") + out := make(map[string]interface{}, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return nil + } + var err error + out[kv[0]], err = strconv.Atoi(kv[1]) + if err != nil { + return nil + } + } + return out +} + // IsSet checks to see if the key has been set in any of the data locations. // IsSet is case-insensitive for a key. func IsSet(key string) bool { return v.IsSet(key) } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 95d8e59d..b774da88 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 7880b8f9..84dbd6c7 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 339515b8..b1d94aec 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -30,9 +30,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -43,9 +43,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -98,7 +98,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -109,7 +109,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -119,7 +119,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -134,8 +134,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -146,8 +146,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -155,10 +155,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a return EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -169,7 +203,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -179,7 +213,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -193,10 +227,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,8 +259,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -237,8 +271,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -266,10 +300,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -280,7 +314,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -288,10 +322,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -301,7 +385,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -311,7 +395,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -353,7 +437,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -363,7 +447,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -391,9 +475,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -403,10 +487,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -416,10 +500,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -429,9 +513,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -442,7 +526,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -455,7 +539,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -468,7 +552,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -481,7 +565,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -493,7 +577,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -505,7 +589,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -517,7 +601,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -529,7 +613,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -541,7 +625,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -553,7 +637,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -565,7 +649,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -577,7 +661,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -589,7 +673,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -599,7 +683,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -609,7 +693,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -651,7 +735,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -693,9 +777,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -705,9 +789,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -717,9 +801,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -729,9 +813,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -741,9 +825,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -753,9 +837,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -765,9 +849,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -777,9 +861,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -805,7 +889,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -815,7 +899,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -826,7 +910,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -837,7 +921,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -847,9 +931,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -859,10 +943,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -872,10 +956,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -885,9 +969,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -897,8 +981,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -908,8 +992,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -920,7 +1004,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) b // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -931,7 +1015,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -941,7 +1025,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -951,7 +1035,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -979,10 +1063,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -992,10 +1076,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1024,9 +1108,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1037,9 +1121,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1050,9 +1134,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1063,9 +1147,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1075,7 +1159,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1088,7 +1172,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1098,7 +1182,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1108,7 +1192,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1139,7 +1223,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1149,7 +1233,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1159,7 +1243,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1169,7 +1253,7 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1179,8 +1263,8 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1190,8 +1274,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1201,7 +1285,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1214,7 +1298,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1228,7 +1312,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1239,7 +1323,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1265,7 +1349,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1277,7 +1361,7 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1289,7 +1373,7 @@ func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1300,7 +1384,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg str // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1311,7 +1395,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,7 +1405,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1331,8 +1415,8 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1342,8 +1426,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1353,8 +1437,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) b // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1364,8 +1448,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1375,7 +1459,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1388,7 +1472,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1402,7 +1486,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1413,7 +1497,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1423,7 +1507,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1433,7 +1517,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1443,7 +1527,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1453,7 +1537,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1463,7 +1547,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1473,7 +1557,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 75944878..00df62a0 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index fa1245b1..a55d1bba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -8,7 +8,6 @@ import ( "fmt" "math" "os" - "path/filepath" "reflect" "regexp" "runtime" @@ -76,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool { return bytes.Equal(exp, act) } +// copyExportedFields iterates downward through nested data structures and creates a copy +// that only contains the exported struct fields. +func copyExportedFields(expected interface{}) interface{} { + if isNil(expected) { + return expected + } + + expectedType := reflect.TypeOf(expected) + expectedKind := expectedType.Kind() + expectedValue := reflect.ValueOf(expected) + + switch expectedKind { + case reflect.Struct: + result := reflect.New(expectedType).Elem() + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.IsExported() + if isExported { + fieldValue := expectedValue.Field(i) + if isNil(fieldValue) || isNil(fieldValue.Interface()) { + continue + } + newValue := copyExportedFields(fieldValue.Interface()) + result.Field(i).Set(reflect.ValueOf(newValue)) + } + } + return result.Interface() + + case reflect.Ptr: + result := reflect.New(expectedType.Elem()) + unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) + result.Elem().Set(reflect.ValueOf(unexportedRemoved)) + return result.Interface() + + case reflect.Array, reflect.Slice: + result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + for i := 0; i < expectedValue.Len(); i++ { + index := expectedValue.Index(i) + if isNil(index) { + continue + } + unexportedRemoved := copyExportedFields(index.Interface()) + result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + case reflect.Map: + result := reflect.MakeMap(expectedType) + for _, k := range expectedValue.MapKeys() { + index := expectedValue.MapIndex(k) + unexportedRemoved := copyExportedFields(index.Interface()) + result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + default: + return expected + } +} + +// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are +// considered equal. This comparison of only exported fields is applied recursively to nested data +// structures. +// +// This function does no assertion of any kind. +func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { + expectedCleaned := copyExportedFields(expected) + actualCleaned := copyExportedFields(actual) + return ObjectsAreEqualValues(expectedCleaned, actualCleaned) +} + // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { @@ -141,12 +211,11 @@ func CallerInfo() []string { } parts := strings.Split(file, "/") - file = parts[len(parts)-1] if len(parts) > 1 { + filename := parts[len(parts)-1] dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - path, _ := filepath.Abs(file) - callers = append(callers, fmt.Sprintf("%s:%d", path, line)) + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } } @@ -273,7 +342,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -296,7 +365,7 @@ func labeledOutput(content ...labeledContent) string { // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -328,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -369,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error { // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -389,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -457,7 +526,7 @@ func truncatingFormat(data interface{}) string { // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -475,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + if aType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + } + + if bType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + } + + expected = copyExportedFields(expected) + actual = copyExportedFields(actual) + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true +} + // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true @@ -530,7 +643,7 @@ func isNil(object interface{}) bool { []reflect.Kind{ reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice}, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, kind) if isNilableKind && value.IsNil() { @@ -542,7 +655,7 @@ func isNil(object interface{}) bool { // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true @@ -585,7 +698,7 @@ func isEmpty(object interface{}) bool { // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -602,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { @@ -633,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) { // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { @@ -666,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { @@ -681,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -704,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -763,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -786,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -796,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true @@ -809,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -818,49 +931,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + if !av.IsValid() { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) } } return true } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) } } @@ -870,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -879,34 +987,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - listKind := reflect.TypeOf(list).Kind() - subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } + subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) if subsetKind == reflect.Map && listKind == reflect.Map { - listValue := reflect.ValueOf(list) - subsetKeys := subsetValue.MapKeys() + subsetMap := reflect.ValueOf(subset) + actualMap := reflect.ValueOf(list) - for i := 0; i < len(subsetKeys); i++ { - subsetKey := subsetKeys[i] - subsetElement := subsetValue.MapIndex(subsetKey).Interface() - listElement := listValue.MapIndex(subsetKey).Interface() + for _, k := range subsetMap.MapKeys() { + ev := subsetMap.MapIndex(k) + av := actualMap.MapIndex(k) - if !ObjectsAreEqual(subsetElement, listElement) { + if !av.IsValid() { + return true + } + if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return true } } @@ -914,8 +1016,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) } - for i := 0; i < subsetValue.Len(); i++ { - element := subsetValue.Index(i).Interface() + subsetList := reflect.ValueOf(subset) + for i := 0; i < subsetList.Len(); i++ { + element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) @@ -1060,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1076,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1117,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1132,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1148,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1207,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1380,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { @@ -1397,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1415,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1438,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1472,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1490,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1603,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,7 +1829,7 @@ type tHelper interface { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1756,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index c9dccc4d..4953981d 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,39 +1,40 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // -// Example Usage +// # Example Usage // // The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// assert.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } // // if you assert many times, use the format below: // -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// func TestSomething(t *testing.T) { -// assert := assert.New(t) +// func TestSomething(t *testing.T) { +// assert := assert.New(t) // -// var a string = "Hello" -// var b string = "Hello" +// var a string = "Hello" +// var b string = "Hello" // -// assert.Equal(a, b, "The two words should be the same.") -// } +// assert.Equal(a, b, "The two words should be the same.") +// } // -// Assertions +// # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 4ed341dd..d8038c28 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index 169de392..96843472 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,24 +1,25 @@ // Package require implements the same assertions as the `assert` package but // stops test execution when a test fails. // -// Example Usage +// # Example Usage // // The following is a complete example using require in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// require.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// require.Equal(t, a, b, "The two words should be the same.") // -// Assertions +// } +// +// # Assertions // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 880853f5..63f85214 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -37,9 +37,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -53,9 +53,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -123,7 +123,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -137,7 +137,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -150,7 +150,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -168,8 +168,8 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,8 +183,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -195,10 +195,50 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args t.FailNow() } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -212,7 +252,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -225,7 +265,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -242,10 +282,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -283,8 +323,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -298,8 +338,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -336,10 +376,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,7 +393,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +404,66 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t t.FailNow() } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -380,7 +476,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -393,7 +489,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -450,7 +546,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -463,7 +559,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -500,9 +596,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -515,10 +611,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -531,10 +627,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -547,9 +643,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -563,7 +659,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -579,7 +675,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -595,7 +691,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -611,7 +707,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -626,7 +722,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -641,7 +737,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -656,7 +752,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -671,7 +767,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -686,7 +782,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -701,7 +797,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -716,7 +812,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -731,7 +827,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -746,7 +842,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -759,7 +855,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -772,7 +868,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -829,7 +925,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -886,9 +982,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -901,9 +997,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -916,9 +1012,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -931,9 +1027,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -946,9 +1042,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -961,9 +1057,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -976,9 +1072,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -991,9 +1087,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1028,7 +1124,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1137,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1055,7 +1151,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,7 +1165,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1082,9 +1178,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,10 +1193,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1113,10 +1209,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1129,9 +1225,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1144,8 +1240,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1158,8 +1254,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1173,7 +1269,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1187,7 +1283,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1200,7 +1296,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1213,7 +1309,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1250,10 +1346,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,10 +1362,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1307,9 +1403,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1323,9 +1419,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1339,9 +1435,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1355,9 +1451,9 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1370,7 +1466,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1386,7 +1482,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1399,7 +1495,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1412,7 +1508,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1452,7 +1548,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1465,7 +1561,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,7 +1574,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1491,7 +1587,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1504,8 +1600,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1518,8 +1614,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1532,7 +1628,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1548,7 +1644,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1565,7 +1661,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1579,7 +1675,7 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1614,7 +1710,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1629,7 +1725,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1644,7 +1740,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1658,7 +1754,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1672,7 +1768,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1685,7 +1781,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1698,8 +1794,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1712,8 +1808,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,8 +1822,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1740,8 +1836,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1754,7 +1850,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1770,7 +1866,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1787,7 +1883,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1801,7 +1897,7 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1814,7 +1910,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1827,7 +1923,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1840,7 +1936,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1853,7 +1949,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1866,7 +1962,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1879,7 +1975,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 960bf6f2..3b5b0933 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -31,9 +31,9 @@ func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...inte // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -44,9 +44,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -99,7 +99,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -110,7 +110,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -120,7 +120,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -135,8 +135,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -147,8 +147,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -156,10 +156,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -170,7 +204,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -180,7 +214,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -194,10 +228,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -226,8 +260,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -238,8 +272,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -267,10 +301,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -281,7 +315,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -289,10 +323,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -302,7 +386,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -312,7 +396,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -354,7 +438,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -364,7 +448,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -392,9 +476,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -404,10 +488,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -417,10 +501,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -430,9 +514,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -443,7 +527,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -456,7 +540,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -469,7 +553,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -482,7 +566,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -494,7 +578,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -506,7 +590,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -518,7 +602,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -530,7 +614,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -542,7 +626,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -554,7 +638,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -566,7 +650,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -578,7 +662,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -590,7 +674,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -600,7 +684,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -610,7 +694,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -652,7 +736,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -694,9 +778,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -706,9 +790,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -718,9 +802,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -730,9 +814,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -742,9 +826,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -754,9 +838,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -766,9 +850,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -778,9 +862,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -806,7 +890,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -816,7 +900,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -827,7 +911,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -838,7 +922,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -848,9 +932,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -860,10 +944,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -873,10 +957,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -886,9 +970,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -898,8 +982,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -909,8 +993,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -921,7 +1005,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -932,7 +1016,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -942,7 +1026,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -952,7 +1036,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -980,10 +1064,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -993,10 +1077,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1025,9 +1109,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1038,9 +1122,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1051,9 +1135,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1064,9 +1148,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1076,7 +1160,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1089,7 +1173,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1099,7 +1183,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1109,7 +1193,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1140,7 +1224,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1150,7 +1234,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1160,7 +1244,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1170,7 +1254,7 @@ func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{} // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1180,8 +1264,8 @@ func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...inte // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1191,8 +1275,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1202,7 +1286,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1215,7 +1299,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1229,7 +1313,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1240,7 +1324,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1266,7 +1350,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1278,7 +1362,7 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1290,7 +1374,7 @@ func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, m // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1301,7 +1385,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1312,7 +1396,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFun // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1322,7 +1406,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFu // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1332,8 +1416,8 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1343,8 +1427,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1354,8 +1438,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1365,8 +1449,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1376,7 +1460,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1389,7 +1473,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1403,7 +1487,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1414,7 +1498,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1424,7 +1508,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1434,7 +1518,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1444,7 +1528,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1454,7 +1538,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1464,7 +1548,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1474,7 +1558,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/suite/doc.go b/vendor/github.com/stretchr/testify/suite/doc.go index f91a245d..8d55a3aa 100644 --- a/vendor/github.com/stretchr/testify/suite/doc.go +++ b/vendor/github.com/stretchr/testify/suite/doc.go @@ -29,37 +29,38 @@ // Suite object has assertion methods. // // A crude example: -// // Basic imports -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/suite" -// ) // -// // Define the suite, and absorb the built-in basic suite -// // functionality from testify - including a T() method which -// // returns the current testing context -// type ExampleTestSuite struct { -// suite.Suite -// VariableThatShouldStartAtFive int -// } +// // Basic imports +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/suite" +// ) // -// // Make sure that VariableThatShouldStartAtFive is set to five -// // before each test -// func (suite *ExampleTestSuite) SetupTest() { -// suite.VariableThatShouldStartAtFive = 5 -// } +// // Define the suite, and absorb the built-in basic suite +// // functionality from testify - including a T() method which +// // returns the current testing context +// type ExampleTestSuite struct { +// suite.Suite +// VariableThatShouldStartAtFive int +// } // -// // All methods that begin with "Test" are run as tests within a -// // suite. -// func (suite *ExampleTestSuite) TestExample() { -// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) -// suite.Equal(5, suite.VariableThatShouldStartAtFive) -// } +// // Make sure that VariableThatShouldStartAtFive is set to five +// // before each test +// func (suite *ExampleTestSuite) SetupTest() { +// suite.VariableThatShouldStartAtFive = 5 +// } // -// // In order for 'go test' to run this suite, we need to create -// // a normal test function and pass our suite to suite.Run -// func TestExampleTestSuite(t *testing.T) { -// suite.Run(t, new(ExampleTestSuite)) -// } +// // All methods that begin with "Test" are run as tests within a +// // suite. +// func (suite *ExampleTestSuite) TestExample() { +// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// } +// +// // In order for 'go test' to run this suite, we need to create +// // a normal test function and pass our suite to suite.Run +// func TestExampleTestSuite(t *testing.T) { +// suite.Run(t, new(ExampleTestSuite)) +// } package suite diff --git a/vendor/github.com/stretchr/testify/suite/interfaces.go b/vendor/github.com/stretchr/testify/suite/interfaces.go index 8b98a8af..fed037d7 100644 --- a/vendor/github.com/stretchr/testify/suite/interfaces.go +++ b/vendor/github.com/stretchr/testify/suite/interfaces.go @@ -7,6 +7,7 @@ import "testing" type TestingSuite interface { T() *testing.T SetT(*testing.T) + SetS(suite TestingSuite) } // SetupAllSuite has a SetupSuite method, which will run before the @@ -51,3 +52,15 @@ type AfterTest interface { type WithStats interface { HandleStats(suiteName string, stats *SuiteInformation) } + +// SetupSubTest has a SetupSubTest method, which will run before each +// subtest in the suite. +type SetupSubTest interface { + SetupSubTest() +} + +// TearDownSubTest has a TearDownSubTest method, which will run after +// each subtest in the suite have been run. +type TearDownSubTest interface { + TearDownSubTest() +} diff --git a/vendor/github.com/stretchr/testify/suite/suite.go b/vendor/github.com/stretchr/testify/suite/suite.go index 89559187..8b4202d8 100644 --- a/vendor/github.com/stretchr/testify/suite/suite.go +++ b/vendor/github.com/stretchr/testify/suite/suite.go @@ -22,9 +22,13 @@ var matchMethod = flag.String("testify.m", "", "regular expression to select tes // retrieving the current *testing.T context. type Suite struct { *assert.Assertions + mu sync.RWMutex require *require.Assertions t *testing.T + + // Parent suite to have access to the implemented methods of parent struct + s TestingSuite } // T retrieves the current *testing.T context. @@ -43,6 +47,12 @@ func (suite *Suite) SetT(t *testing.T) { suite.require = require.New(t) } +// SetS needs to set the current test suite as parent +// to get access to the parent methods +func (suite *Suite) SetS(s TestingSuite) { + suite.s = s +} + // Require returns a require context for suite. func (suite *Suite) Require() *require.Assertions { suite.mu.Lock() @@ -85,7 +95,18 @@ func failOnPanic(t *testing.T, r interface{}) { // Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. func (suite *Suite) Run(name string, subtest func()) bool { oldT := suite.T() - defer suite.SetT(oldT) + + if setupSubTest, ok := suite.s.(SetupSubTest); ok { + setupSubTest.SetupSubTest() + } + + defer func() { + suite.SetT(oldT) + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + tearDownSubTest.TearDownSubTest() + } + }() + return oldT.Run(name, func(t *testing.T) { suite.SetT(t) subtest() @@ -98,6 +119,7 @@ func Run(t *testing.T, suite TestingSuite) { defer recoverAndFailOnPanic(t) suite.SetT(t) + suite.SetS(suite) var suiteSetupDone bool diff --git a/vendor/github.com/wiggin77/cfg/.gitignore b/vendor/github.com/wiggin77/cfg/.gitignore deleted file mode 100644 index f1c181ec..00000000 --- a/vendor/github.com/wiggin77/cfg/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/wiggin77/cfg/.travis.yml b/vendor/github.com/wiggin77/cfg/.travis.yml deleted file mode 100644 index 9899b387..00000000 --- a/vendor/github.com/wiggin77/cfg/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go -sudo: false -before_script: - - go vet ./... -
\ No newline at end of file diff --git a/vendor/github.com/wiggin77/cfg/LICENSE b/vendor/github.com/wiggin77/cfg/LICENSE deleted file mode 100644 index 2b0bf7ef..00000000 --- a/vendor/github.com/wiggin77/cfg/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 wiggin77 - -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. diff --git a/vendor/github.com/wiggin77/cfg/README.md b/vendor/github.com/wiggin77/cfg/README.md deleted file mode 100644 index 583a82cb..00000000 --- a/vendor/github.com/wiggin77/cfg/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# cfg - -[![GoDoc](https://godoc.org/github.com/wiggin77/cfg?status.svg)](https://godoc.org/github.com/wiggin77/cfg) -[![Build Status](https://travis-ci.org/wiggin77/cfg.svg?branch=master)](https://travis-ci.org/wiggin77/cfg) - -Go package for app configuration. Supports chained configuration sources for multiple levels of defaults. -Includes APIs for loading Linux style configuration files (name/value pairs) or INI files, map based properties, -or easily create new configuration sources (e.g. load from database). - -Supports monitoring configuration sources for changes, hot loading properties, and notifying listeners of changes. - -## Usage - -```Go -config := &cfg.Config{} -defer config.Shutdown() // stops monitoring - -// load file via filespec string, os.File -src, err := Config.NewSrcFileFromFilespec("./myfile.conf") -if err != nil { - return err -} -// add src to top of chain, meaning first searched -cfg.PrependSource(src) - -// fetch prop 'retries', default to 3 if not found -val := config.Int("retries", 3) -``` - -See [example](./example_test.go) for more complete example, including listening for configuration changes. - -Config API parses the following data types: - -| type | method | example property values | -| ------- | ------ | -------- | -| string | Config.String | test, "" | -| int | Config.Int | -1, 77, 0 | -| int64 | Config.Int64 | -9223372036854775, 372036854775808 | -| float64 | Config.Float64 | -77.3456, 95642331.1 | -| bool | Config.Bool | T,t,true,True,1,0,False,false,f,F | -| time.Duration | Config.Duration | "10ms", "2 hours", "5 min" * | - -\* Units of measure supported: ms, sec, min, hour, day, week, year. diff --git a/vendor/github.com/wiggin77/cfg/config.go b/vendor/github.com/wiggin77/cfg/config.go deleted file mode 100644 index 0e958102..00000000 --- a/vendor/github.com/wiggin77/cfg/config.go +++ /dev/null @@ -1,366 +0,0 @@ -package cfg - -import ( - "errors" - "fmt" - "strconv" - "strings" - "sync" - "time" - - "github.com/wiggin77/cfg/timeconv" -) - -// ErrNotFound returned when an operation is attempted on a -// resource that doesn't exist, such as fetching a non-existing -// property name. -var ErrNotFound = errors.New("not found") - -type sourceEntry struct { - src Source - props map[string]string -} - -// Config provides methods for retrieving property values from one or more -// configuration sources. -type Config struct { - mutexSrc sync.RWMutex - mutexListeners sync.RWMutex - srcs []*sourceEntry - chgListeners []ChangedListener - shutdown chan interface{} - wantPanicOnError bool -} - -// PrependSource inserts one or more `Sources` at the beginning of -// the list of sources such that the first source will be the -// source checked first when resolving a property value. -func (config *Config) PrependSource(srcs ...Source) { - arr := config.wrapSources(srcs...) - - config.mutexSrc.Lock() - if config.shutdown == nil { - config.shutdown = make(chan interface{}) - } - config.srcs = append(arr, config.srcs...) - config.mutexSrc.Unlock() - - for _, se := range arr { - if _, ok := se.src.(SourceMonitored); ok { - config.monitor(se) - } - } -} - -// AppendSource appends one or more `Sources` at the end of -// the list of sources such that the last source will be the -// source checked last when resolving a property value. -func (config *Config) AppendSource(srcs ...Source) { - arr := config.wrapSources(srcs...) - - config.mutexSrc.Lock() - if config.shutdown == nil { - config.shutdown = make(chan interface{}) - } - config.srcs = append(config.srcs, arr...) - config.mutexSrc.Unlock() - - for _, se := range arr { - if _, ok := se.src.(SourceMonitored); ok { - config.monitor(se) - } - } -} - -// wrapSources wraps one or more Source's and returns -// them as an array of `sourceEntry`. -func (config *Config) wrapSources(srcs ...Source) []*sourceEntry { - arr := make([]*sourceEntry, 0, len(srcs)) - for _, src := range srcs { - se := &sourceEntry{src: src} - config.reloadProps(se) - arr = append(arr, se) - } - return arr -} - -// SetWantPanicOnError sets the flag determining if Config -// should panic when `GetProps` or `GetLastModified` errors -// for a `Source`. -func (config *Config) SetWantPanicOnError(b bool) { - config.mutexSrc.Lock() - config.wantPanicOnError = b - config.mutexSrc.Unlock() -} - -// ShouldPanicOnError gets the flag determining if Config -// should panic when `GetProps` or `GetLastModified` errors -// for a `Source`. -func (config *Config) ShouldPanicOnError() (b bool) { - config.mutexSrc.RLock() - b = config.wantPanicOnError - config.mutexSrc.RUnlock() - return b -} - -// getProp returns the value of a named property. -// Each `Source` is checked, in the order created by adding via -// `AppendSource` and `PrependSource`, until a value for the -// property is found. -func (config *Config) getProp(name string) (val string, ok bool) { - config.mutexSrc.RLock() - defer config.mutexSrc.RUnlock() - - var s string - for _, se := range config.srcs { - if se.props != nil { - if s, ok = se.props[name]; ok { - val = strings.TrimSpace(s) - return - } - } - } - return -} - -// String returns the value of the named prop as a string. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -func (config *Config) String(name string, def string) (val string, err error) { - if v, ok := config.getProp(name); ok { - val = v - err = nil - return - } - - err = ErrNotFound - val = def - return -} - -// Int returns the value of the named prop as an `int`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Int(name string, def int) (val int, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - var i int64 - if i, err = strconv.ParseInt(s, 10, 32); err == nil { - val = int(i) - } - } - if err != nil { - val = def - } - return -} - -// Int64 returns the value of the named prop as an `int64`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Int64(name string, def int64) (val int64, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - val, err = strconv.ParseInt(s, 10, 64) - } - if err != nil { - val = def - } - return -} - -// Float64 returns the value of the named prop as a `float64`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Float64(name string, def float64) (val float64, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - val, err = strconv.ParseFloat(s, 64) - } - if err != nil { - val = def - } - return -} - -// Bool returns the value of the named prop as a `bool`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// Supports (t, true, 1, y, yes) for true, and (f, false, 0, n, no) for false, -// all case-insensitive. -// -// See config.String -func (config *Config) Bool(name string, def bool) (val bool, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - switch strings.ToLower(s) { - case "t", "true", "1", "y", "yes": - val = true - case "f", "false", "0", "n", "no": - val = false - default: - err = errors.New("invalid syntax") - } - } - if err != nil { - val = def - } - return -} - -// Duration returns the value of the named prop as a `time.Duration`, representing -// a span of time. -// -// Units of measure are supported: ms, sec, min, hour, day, week, year. -// See config.UnitsToMillis for a complete list of units supported. -// -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Duration(name string, def time.Duration) (val time.Duration, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - var ms int64 - ms, err = timeconv.ParseMilliseconds(s) - val = time.Duration(ms) * time.Millisecond - } - if err != nil { - val = def - } - return -} - -// AddChangedListener adds a listener that will receive notifications -// whenever one or more property values change within the config. -func (config *Config) AddChangedListener(l ChangedListener) { - config.mutexListeners.Lock() - defer config.mutexListeners.Unlock() - - config.chgListeners = append(config.chgListeners, l) -} - -// RemoveChangedListener removes all instances of a ChangedListener. -// Returns `ErrNotFound` if the listener was not present. -func (config *Config) RemoveChangedListener(l ChangedListener) error { - config.mutexListeners.Lock() - defer config.mutexListeners.Unlock() - - dest := make([]ChangedListener, 0, len(config.chgListeners)) - err := ErrNotFound - - // Remove all instances of the listener by - // copying list while filtering. - for _, s := range config.chgListeners { - if s != l { - dest = append(dest, s) - } else { - err = nil - } - } - config.chgListeners = dest - return err -} - -// Shutdown can be called to stop monitoring of all config sources. -func (config *Config) Shutdown() { - config.mutexSrc.RLock() - defer config.mutexSrc.RUnlock() - if config.shutdown != nil { - close(config.shutdown) - } -} - -// onSourceChanged is called whenever one or more properties of a -// config source has changed. -func (config *Config) onSourceChanged(src SourceMonitored) { - defer func() { - if p := recover(); p != nil { - fmt.Println(p) - } - }() - config.mutexListeners.RLock() - defer config.mutexListeners.RUnlock() - for _, l := range config.chgListeners { - l.ConfigChanged(config, src) - } -} - -// monitor periodically checks a config source for changes. -func (config *Config) monitor(se *sourceEntry) { - go func(se *sourceEntry, shutdown <-chan interface{}) { - var src SourceMonitored - var ok bool - if src, ok = se.src.(SourceMonitored); !ok { - return - } - paused := false - last := time.Time{} - freq := src.GetMonitorFreq() - if freq <= 0 { - paused = true - freq = 10 - last, _ = src.GetLastModified() - } - timer := time.NewTimer(freq) - for { - select { - case <-timer.C: - if !paused { - if latest, err := src.GetLastModified(); err != nil { - if config.ShouldPanicOnError() { - panic(fmt.Sprintf("error <%v> getting last modified for %v", err, src)) - } - } else { - if last.Before(latest) { - last = latest - config.reloadProps(se) - // TODO: calc diff and provide detailed changes - config.onSourceChanged(src) - } - } - } - freq = src.GetMonitorFreq() - if freq <= 0 { - paused = true - freq = 10 - } else { - paused = false - } - timer.Reset(freq) - case <-shutdown: - // stop the timer and exit - if !timer.Stop() { - <-timer.C - } - return - } - } - }(se, config.shutdown) -} - -// reloadProps causes a Source to reload its properties. -func (config *Config) reloadProps(se *sourceEntry) { - config.mutexSrc.Lock() - defer config.mutexSrc.Unlock() - - m, err := se.src.GetProps() - if err != nil { - if config.wantPanicOnError { - panic(fmt.Sprintf("GetProps error for %v", se.src)) - } - return - } - - se.props = make(map[string]string) - for k, v := range m { - se.props[k] = v - } -} diff --git a/vendor/github.com/wiggin77/cfg/ini/ini.go b/vendor/github.com/wiggin77/cfg/ini/ini.go deleted file mode 100644 index d28d7444..00000000 --- a/vendor/github.com/wiggin77/cfg/ini/ini.go +++ /dev/null @@ -1,167 +0,0 @@ -package ini - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "sync" - "time" -) - -// Ini provides parsing and querying of INI format or simple name/value pairs -// such as a simple config file. -// A name/value pair format is just an INI with no sections, and properties can -// be queried using an empty section name. -type Ini struct { - mutex sync.RWMutex - m map[string]*Section - lm time.Time -} - -// LoadFromFilespec loads an INI file from string containing path and filename. -func (ini *Ini) LoadFromFilespec(filespec string) error { - f, err := os.Open(filespec) - if err != nil { - return err - } - return ini.LoadFromFile(f) -} - -// LoadFromFile loads an INI file from `os.File`. -func (ini *Ini) LoadFromFile(file *os.File) error { - - fi, err := file.Stat() - if err != nil { - return err - } - lm := fi.ModTime() - - if err := ini.LoadFromReader(file); err != nil { - return err - } - ini.lm = lm - return nil -} - -// LoadFromReader loads an INI file from an `io.Reader`. -func (ini *Ini) LoadFromReader(reader io.Reader) error { - data, err := ioutil.ReadAll(reader) - if err != nil { - return err - } - return ini.LoadFromString(string(data)) -} - -// LoadFromString parses an INI from a string . -func (ini *Ini) LoadFromString(s string) error { - m, err := getSections(s) - if err != nil { - return err - } - ini.mutex.Lock() - ini.m = m - ini.lm = time.Now() - ini.mutex.Unlock() - return nil -} - -// GetLastModified returns the last modified timestamp of the -// INI contents. -func (ini *Ini) GetLastModified() time.Time { - return ini.lm -} - -// GetSectionNames returns the names of all sections in this INI. -// Note, the returned section names are a snapshot in time, meaning -// other goroutines may change the contents of this INI as soon as -// the method returns. -func (ini *Ini) GetSectionNames() []string { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - arr := make([]string, 0, len(ini.m)) - for key := range ini.m { - arr = append(arr, key) - } - return arr -} - -// GetKeys returns the names of all keys in the specified section. -// Note, the returned key names are a snapshot in time, meaning other -// goroutines may change the contents of this INI as soon as the -// method returns. -func (ini *Ini) GetKeys(sectionName string) ([]string, error) { - sec, err := ini.getSection(sectionName) - if err != nil { - return nil, err - } - return sec.getKeys(), nil -} - -// getSection returns the named section. -func (ini *Ini) getSection(sectionName string) (*Section, error) { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - sec, ok := ini.m[sectionName] - if !ok { - return nil, fmt.Errorf("section '%s' not found", sectionName) - } - return sec, nil -} - -// GetFlattenedKeys returns all section names plus keys as one -// flattened array. -func (ini *Ini) GetFlattenedKeys() []string { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - arr := make([]string, 0, len(ini.m)*2) - for _, section := range ini.m { - keys := section.getKeys() - for _, key := range keys { - name := section.GetName() - if name != "" { - key = name + "." + key - } - arr = append(arr, key) - } - } - return arr -} - -// GetProp returns the value of the specified key in the named section. -func (ini *Ini) GetProp(section string, key string) (val string, ok bool) { - sec, err := ini.getSection(section) - if err != nil { - return val, false - } - return sec.GetProp(key) -} - -// ToMap returns a flattened map of the section name plus keys mapped -// to values. -func (ini *Ini) ToMap() map[string]string { - m := make(map[string]string) - - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - for _, section := range ini.m { - for _, key := range section.getKeys() { - val, ok := section.GetProp(key) - if ok { - name := section.GetName() - var mapkey string - if name != "" { - mapkey = name + "." + key - } else { - mapkey = key - } - m[mapkey] = val - } - } - } - return m -} diff --git a/vendor/github.com/wiggin77/cfg/ini/parser.go b/vendor/github.com/wiggin77/cfg/ini/parser.go deleted file mode 100644 index 28916409..00000000 --- a/vendor/github.com/wiggin77/cfg/ini/parser.go +++ /dev/null @@ -1,142 +0,0 @@ -package ini - -import ( - "fmt" - "strings" - - "github.com/wiggin77/merror" -) - -// LF is linefeed -const LF byte = 0x0A - -// CR is carriage return -const CR byte = 0x0D - -// getSections parses an INI formatted string, or string containing just name/value pairs, -// returns map of `Section`'s. -// -// Any name/value pairs appearing before a section name are added to the section named -// with an empty string (""). Also true for Linux-style config files where all props -// are outside a named section. -// -// Any errors encountered are aggregated and returned, along with the partially parsed -// sections. -func getSections(str string) (map[string]*Section, error) { - merr := merror.New() - mapSections := make(map[string]*Section) - lines := buildLineArray(str) - section := newSection("") - - for _, line := range lines { - name, ok := parseSection(line) - if ok { - // A section name encountered. Stop processing the current one. - // Don't add the current section to the map if the section name is blank - // and the prop map is empty. - nameCurr := section.GetName() - if nameCurr != "" || section.hasKeys() { - mapSections[nameCurr] = section - } - // Start processing a new section. - section = newSection(name) - } else { - // Parse the property and add to the current section, or ignore if comment. - if k, v, comment, err := parseProp(line); !comment && err == nil { - section.setProp(k, v) - } else if err != nil { - merr.Append(err) // aggregate errors - } - } - - } - // If the current section is not empty, add it. - if section.hasKeys() { - mapSections[section.GetName()] = section - } - return mapSections, merr.ErrorOrNil() -} - -// buildLineArray parses the given string buffer and creates a list of strings, -// one for each line in the string buffer. -// -// A line is considered to be terminated by any one of a line feed ('\n'), -// a carriage return ('\r'), or a carriage return followed immediately by a -// linefeed. -// -// Lines prefixed with ';' or '#' are considered comments and skipped. -func buildLineArray(str string) []string { - arr := make([]string, 0, 10) - str = str + "\n" - - iLen := len(str) - iPos, iBegin := 0, 0 - var ch byte - - for iPos < iLen { - ch = str[iPos] - if ch == LF || ch == CR { - sub := str[iBegin:iPos] - sub = strings.TrimSpace(sub) - if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") { - arr = append(arr, sub) - } - iPos++ - if ch == CR && iPos < iLen && str[iPos] == LF { - iPos++ - } - iBegin = iPos - } else { - iPos++ - } - } - return arr -} - -// parseSection parses the specified string for a section name enclosed in square brackets. -// Returns the section name found, or `ok=false` if `str` is not a section header. -func parseSection(str string) (name string, ok bool) { - str = strings.TrimSpace(str) - if !strings.HasPrefix(str, "[") { - return "", false - } - iCloser := strings.Index(str, "]") - if iCloser == -1 { - return "", false - } - return strings.TrimSpace(str[1:iCloser]), true -} - -// parseProp parses the specified string and extracts a key/value pair. -// -// If the string is a comment (prefixed with ';' or '#') then `comment=true` -// and key will be empty. -func parseProp(str string) (key string, val string, comment bool, err error) { - iLen := len(str) - iEqPos := strings.Index(str, "=") - if iEqPos == -1 { - return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str) - } - - key = str[0:iEqPos] - key = strings.TrimSpace(key) - if iEqPos+1 < iLen { - val = str[iEqPos+1:] - val = strings.TrimSpace(val) - } - - // Check that the key has at least 1 char. - if key == "" { - return "", "", false, fmt.Errorf("key is empty for '%s'", str) - } - - // Check if this line is a comment that just happens - // to have an equals sign in it. Not an error, but not a - // useable line either. - if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") { - key = "" - val = "" - comment = true - } - return key, val, comment, err -} diff --git a/vendor/github.com/wiggin77/cfg/ini/section.go b/vendor/github.com/wiggin77/cfg/ini/section.go deleted file mode 100644 index 18c4c254..00000000 --- a/vendor/github.com/wiggin77/cfg/ini/section.go +++ /dev/null @@ -1,109 +0,0 @@ -package ini - -import ( - "fmt" - "strings" - "sync" -) - -// Section represents a section in an INI file. The section has a name, which is -// enclosed in square brackets in the file. The section also has an array of -// key/value pairs. -type Section struct { - name string - props map[string]string - mtx sync.RWMutex -} - -func newSection(name string) *Section { - sec := &Section{} - sec.name = name - sec.props = make(map[string]string) - return sec -} - -// addLines addes an array of strings containing name/value pairs -// of the format `key=value`. -//func addLines(lines []string) { -// TODO -//} - -// GetName returns the name of the section. -func (sec *Section) GetName() (name string) { - sec.mtx.RLock() - name = sec.name - sec.mtx.RUnlock() - return -} - -// GetProp returns the value associated with the given key, or -// `ok=false` if key does not exist. -func (sec *Section) GetProp(key string) (val string, ok bool) { - sec.mtx.RLock() - val, ok = sec.props[key] - sec.mtx.RUnlock() - return -} - -// SetProp sets the value associated with the given key. -func (sec *Section) setProp(key string, val string) { - sec.mtx.Lock() - sec.props[key] = val - sec.mtx.Unlock() -} - -// hasKeys returns true if there are one or more properties in -// this section. -func (sec *Section) hasKeys() (b bool) { - sec.mtx.RLock() - b = len(sec.props) > 0 - sec.mtx.RUnlock() - return -} - -// getKeys returns an array containing all keys in this section. -func (sec *Section) getKeys() []string { - sec.mtx.RLock() - defer sec.mtx.RUnlock() - - arr := make([]string, len(sec.props)) - idx := 0 - for k := range sec.props { - arr[idx] = k - idx++ - } - return arr -} - -// combine the given section with this one. -func (sec *Section) combine(sec2 *Section) { - sec.mtx.Lock() - sec2.mtx.RLock() - defer sec.mtx.Unlock() - defer sec2.mtx.RUnlock() - - for k, v := range sec2.props { - sec.props[k] = v - } -} - -// String returns a string representation of this section. -func (sec *Section) String() string { - return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly()) -} - -// StringPropsOnly returns a string representation of this section -// without the section header. -func (sec *Section) StringPropsOnly() string { - sec.mtx.RLock() - defer sec.mtx.RUnlock() - sb := &strings.Builder{} - - for k, v := range sec.props { - sb.WriteString(k) - sb.WriteString("=") - sb.WriteString(v) - sb.WriteString("\n") - } - return sb.String() -} diff --git a/vendor/github.com/wiggin77/cfg/listener.go b/vendor/github.com/wiggin77/cfg/listener.go deleted file mode 100644 index 12ea4e45..00000000 --- a/vendor/github.com/wiggin77/cfg/listener.go +++ /dev/null @@ -1,11 +0,0 @@ -package cfg - -// ChangedListener interface is for receiving notifications -// when one or more properties within monitored config sources -// (SourceMonitored) have changed values. -type ChangedListener interface { - - // Changed is called when one or more properties in a `SourceMonitored` has a - // changed value. - ConfigChanged(cfg *Config, src SourceMonitored) -} diff --git a/vendor/github.com/wiggin77/cfg/nocopy.go b/vendor/github.com/wiggin77/cfg/nocopy.go deleted file mode 100644 index f2450c0b..00000000 --- a/vendor/github.com/wiggin77/cfg/nocopy.go +++ /dev/null @@ -1,11 +0,0 @@ -package cfg - -// noCopy may be embedded into structs which must not be copied -// after the first use. -// -// See https://golang.org/issues/8005#issuecomment-190753527 -// for details. -type noCopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} diff --git a/vendor/github.com/wiggin77/cfg/source.go b/vendor/github.com/wiggin77/cfg/source.go deleted file mode 100644 index 09083e97..00000000 --- a/vendor/github.com/wiggin77/cfg/source.go +++ /dev/null @@ -1,58 +0,0 @@ -package cfg - -import ( - "sync" - "time" -) - -// Source is the interface required for any source of name/value pairs. -type Source interface { - - // GetProps fetches all the properties from a source and returns - // them as a map. - GetProps() (map[string]string, error) -} - -// SourceMonitored is the interface required for any config source that is -// monitored for changes. -type SourceMonitored interface { - Source - - // GetLastModified returns the time of the latest modification to any - // property value within the source. If a source does not support - // modifying properties at runtime then the zero value for `Time` - // should be returned to ensure reload events are not generated. - GetLastModified() (time.Time, error) - - // GetMonitorFreq returns the frequency as a `time.Duration` between - // checks for changes to this config source. - // - // Returning zero (or less) will temporarily suspend calls to `GetLastModified` - // and `GetMonitorFreq` will be called every 10 seconds until resumed, after which - // `GetMontitorFreq` will be called at a frequency roughly equal to the `time.Duration` - // returned. - GetMonitorFreq() time.Duration -} - -// AbstractSourceMonitor can be embedded in a custom `Source` to provide the -// basic plumbing for monitor frequency. -type AbstractSourceMonitor struct { - mutex sync.RWMutex - freq time.Duration -} - -// GetMonitorFreq returns the frequency as a `time.Duration` between -// checks for changes to this config source. -func (asm *AbstractSourceMonitor) GetMonitorFreq() (freq time.Duration) { - asm.mutex.RLock() - freq = asm.freq - asm.mutex.RUnlock() - return -} - -// SetMonitorFreq sets the frequency between checks for changes to this config source. -func (asm *AbstractSourceMonitor) SetMonitorFreq(freq time.Duration) { - asm.mutex.Lock() - asm.freq = freq - asm.mutex.Unlock() -} diff --git a/vendor/github.com/wiggin77/cfg/srcfile.go b/vendor/github.com/wiggin77/cfg/srcfile.go deleted file mode 100644 index f42c69fa..00000000 --- a/vendor/github.com/wiggin77/cfg/srcfile.go +++ /dev/null @@ -1,63 +0,0 @@ -package cfg - -import ( - "os" - "time" - - "github.com/wiggin77/cfg/ini" -) - -// SrcFile is a configuration `Source` backed by a file containing -// name/value pairs or INI format. -type SrcFile struct { - AbstractSourceMonitor - ini ini.Ini - file *os.File -} - -// NewSrcFileFromFilespec creates a new SrcFile with the specified filespec. -func NewSrcFileFromFilespec(filespec string) (*SrcFile, error) { - file, err := os.Open(filespec) - if err != nil { - return nil, err - } - return NewSrcFile(file) -} - -// NewSrcFile creates a new SrcFile with the specified os.File. -func NewSrcFile(file *os.File) (*SrcFile, error) { - sf := &SrcFile{} - sf.freq = time.Minute - sf.file = file - if err := sf.ini.LoadFromFile(file); err != nil { - return nil, err - } - return sf, nil -} - -// GetProps fetches all the properties from a source and returns -// them as a map. -func (sf *SrcFile) GetProps() (map[string]string, error) { - lm, err := sf.GetLastModified() - if err != nil { - return nil, err - } - - // Check if we need to reload. - if sf.ini.GetLastModified() != lm { - if err := sf.ini.LoadFromFile(sf.file); err != nil { - return nil, err - } - } - return sf.ini.ToMap(), nil -} - -// GetLastModified returns the time of the latest modification to any -// property value within the source. -func (sf *SrcFile) GetLastModified() (time.Time, error) { - fi, err := sf.file.Stat() - if err != nil { - return time.Now(), err - } - return fi.ModTime(), nil -} diff --git a/vendor/github.com/wiggin77/cfg/srcmap.go b/vendor/github.com/wiggin77/cfg/srcmap.go deleted file mode 100644 index 321db27a..00000000 --- a/vendor/github.com/wiggin77/cfg/srcmap.go +++ /dev/null @@ -1,78 +0,0 @@ -package cfg - -import ( - "time" -) - -// SrcMap is a configuration `Source` backed by a simple map. -type SrcMap struct { - AbstractSourceMonitor - m map[string]string - lm time.Time -} - -// NewSrcMap creates an empty `SrcMap`. -func NewSrcMap() *SrcMap { - sm := &SrcMap{} - sm.m = make(map[string]string) - sm.lm = time.Now() - sm.freq = time.Minute - return sm -} - -// NewSrcMapFromMap creates a `SrcMap` containing a copy of the -// specified map. -func NewSrcMapFromMap(mapIn map[string]string) *SrcMap { - sm := NewSrcMap() - sm.PutAll(mapIn) - return sm -} - -// Put inserts or updates a value in the `SrcMap`. -func (sm *SrcMap) Put(key string, val string) { - sm.mutex.Lock() - sm.m[key] = val - sm.lm = time.Now() - sm.mutex.Unlock() -} - -// PutAll inserts a copy of `mapIn` into the `SrcMap` -func (sm *SrcMap) PutAll(mapIn map[string]string) { - sm.mutex.Lock() - defer sm.mutex.Unlock() - - for k, v := range mapIn { - sm.m[k] = v - } - sm.lm = time.Now() -} - -// GetProps fetches all the properties from a source and returns -// them as a map. -func (sm *SrcMap) GetProps() (m map[string]string, err error) { - sm.mutex.RLock() - m = sm.m - sm.mutex.RUnlock() - return -} - -// GetLastModified returns the time of the latest modification to any -// property value within the source. If a source does not support -// modifying properties at runtime then the zero value for `Time` -// should be returned to ensure reload events are not generated. -func (sm *SrcMap) GetLastModified() (last time.Time, err error) { - sm.mutex.RLock() - last = sm.lm - sm.mutex.RUnlock() - return -} - -// GetMonitorFreq returns the frequency as a `time.Duration` between -// checks for changes to this config source. Defaults to 1 minute -// unless changed with `SetMonitorFreq`. -func (sm *SrcMap) GetMonitorFreq() (freq time.Duration) { - sm.mutex.RLock() - freq = sm.freq - sm.mutex.RUnlock() - return -} diff --git a/vendor/github.com/wiggin77/cfg/timeconv/parse.go b/vendor/github.com/wiggin77/cfg/timeconv/parse.go deleted file mode 100644 index 218ef43a..00000000 --- a/vendor/github.com/wiggin77/cfg/timeconv/parse.go +++ /dev/null @@ -1,108 +0,0 @@ -package timeconv - -import ( - "fmt" - "math" - "regexp" - "strconv" - "strings" -) - -// MillisPerSecond is the number of millseconds per second. -const MillisPerSecond int64 = 1000 - -// MillisPerMinute is the number of millseconds per minute. -const MillisPerMinute int64 = MillisPerSecond * 60 - -// MillisPerHour is the number of millseconds per hour. -const MillisPerHour int64 = MillisPerMinute * 60 - -// MillisPerDay is the number of millseconds per day. -const MillisPerDay int64 = MillisPerHour * 24 - -// MillisPerWeek is the number of millseconds per week. -const MillisPerWeek int64 = MillisPerDay * 7 - -// MillisPerYear is the approximate number of millseconds per year. -const MillisPerYear int64 = MillisPerDay*365 + int64((float64(MillisPerDay) * 0.25)) - -// ParseMilliseconds parses a string containing a number plus -// a unit of measure for time and returns the number of milliseconds -// it represents. -// -// Example: -// * "1 second" returns 1000 -// * "1 minute" returns 60000 -// * "1 hour" returns 3600000 -// -// See config.UnitsToMillis for a list of supported units of measure. -func ParseMilliseconds(str string) (int64, error) { - s := strings.TrimSpace(str) - reg := regexp.MustCompile("([0-9\\.\\-+]*)(.*)") - matches := reg.FindStringSubmatch(s) - if matches == nil || len(matches) < 1 || matches[1] == "" { - return 0, fmt.Errorf("invalid syntax - '%s'", s) - } - digits := matches[1] - units := "ms" - if len(matches) > 1 && matches[2] != "" { - units = matches[2] - } - - fDigits, err := strconv.ParseFloat(digits, 64) - if err != nil { - return 0, err - } - - msPerUnit, err := UnitsToMillis(units) - if err != nil { - return 0, err - } - - // Check for overflow. - fms := float64(msPerUnit) * fDigits - if fms > math.MaxInt64 || fms < math.MinInt64 { - return 0, fmt.Errorf("out of range - '%s' overflows", s) - } - ms := int64(fms) - return ms, nil -} - -// UnitsToMillis returns the number of milliseconds represented by the specified unit of measure. -// -// Example: -// * "second" returns 1000 <br/> -// * "minute" returns 60000 <br/> -// * "hour" returns 3600000 <br/> -// -// Supported units of measure: -// * "milliseconds", "millis", "ms", "millisecond" -// * "seconds", "sec", "s", "second" -// * "minutes", "mins", "min", "m", "minute" -// * "hours", "h", "hour" -// * "days", "d", "day" -// * "weeks", "w", "week" -// * "years", "y", "year" -func UnitsToMillis(units string) (ms int64, err error) { - u := strings.TrimSpace(units) - u = strings.ToLower(u) - switch u { - case "milliseconds", "millisecond", "millis", "ms": - ms = 1 - case "seconds", "second", "sec", "s": - ms = MillisPerSecond - case "minutes", "minute", "mins", "min", "m": - ms = MillisPerMinute - case "hours", "hour", "h": - ms = MillisPerHour - case "days", "day", "d": - ms = MillisPerDay - case "weeks", "week", "w": - ms = MillisPerWeek - case "years", "year", "y": - ms = MillisPerYear - default: - err = fmt.Errorf("invalid syntax - '%s' not a supported unit of measure", u) - } - return -} |