diff --git a/cmd/scanner/main.go b/cmd/scanner/main.go index 8dad51f..8042acd 100644 --- a/cmd/scanner/main.go +++ b/cmd/scanner/main.go @@ -11,8 +11,8 @@ import ( "time" "github.com/sentriz/gonic/db" + "github.com/sentriz/gonic/tags" - "github.com/dhowden/tag" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" "github.com/karrick/godirwalk" @@ -65,16 +65,11 @@ func isCover(filename string) bool { return ok } -func readTags(fullPath string) (tag.Metadata, error) { - trackData, err := os.Open(fullPath) +func readTags(fullPath string) (tags.Metadata, error) { + tags, err := tags.Read(fullPath) if err != nil { return nil, fmt.Errorf("when tags from disk: %v", err) } - defer trackData.Close() - tags, err := tag.ReadFrom(trackData) - if err != nil { - return nil, err - } return tags, nil } @@ -130,19 +125,15 @@ func handleFile(fullPath string, info *godirwalk.Dirent) error { return nil } tags, err := readTags(fullPath) - fmt.Println(tags.Raw()) - os.Exit(0) if err != nil { return fmt.Errorf("when reading tags: %v", err) } - trackNumber, totalTracks := tags.Track() - discNumber, TotalDiscs := tags.Disc() track.Path = fullPath track.Title = tags.Title() - track.DiscNumber = uint(discNumber) - track.TotalDiscs = uint(TotalDiscs) - track.TotalTracks = uint(totalTracks) - track.TrackNumber = uint(trackNumber) + track.DiscNumber = uint(tags.Disc()) + track.TotalDiscs = uint(tags.TotalDiscs()) + track.TrackNumber = uint(tags.Track()) + track.TotalTracks = uint(tags.TotalTracks()) track.Year = uint(tags.Year()) // set artist { artist := db.Artist{ diff --git a/go.mod b/go.mod index c27dac5..f808abc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sentriz/gonic require ( cloud.google.com/go v0.37.1 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect - github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca + github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca // indirect github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/go-sql-driver/mysql v1.4.1 // indirect github.com/gofrs/uuid v3.2.0+incompatible // indirect @@ -11,10 +11,15 @@ require ( github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect github.com/jinzhu/now v1.0.0 // indirect github.com/karrick/godirwalk v1.8.0 + github.com/labstack/gommon v0.2.8 // indirect github.com/lib/pq v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.1 // indirect + github.com/mattn/go-isatty v0.0.7 // indirect github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/myesui/uuid v1.0.0 // indirect github.com/twinj/uuid v1.0.0 + github.com/valyala/fasttemplate v1.0.1 // indirect + github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628 // indirect golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect google.golang.org/appengine v1.5.0 // indirect ) diff --git a/go.sum b/go.sum index 471d4a0..fb2aef5 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,15 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= +github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -95,6 +102,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk= github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628 h1:hYOyf8es7yvMucqlPar3CdobJeY0+0OmR5iT4hHYW54= +github.com/wtolson/go-taglib v0.0.0-20180718000046-586eb63c2628/go.mod h1:p+WHGfN/a+Ol37Pm7EIOO/6Cylieb2qn1jmKfxtSsUg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= @@ -137,6 +150,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/handler/handler.go b/handler/handler.go index 0ae7d06..3eb17f7 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -34,9 +34,9 @@ func respondRaw(w http.ResponseWriter, r *http.Request, code int, sub *subsonic. log.Printf("could not marshall to json: %v\n", err) } callback := r.URL.Query().Get("callback") - w.Write([]byte(fmt.Sprintf("%s(", callback))) + w.Write([]byte(fmt.Sprintf(`%s({"subsonic-response":`, callback))) w.Write(data) - w.Write([]byte(");")) + w.Write([]byte("});")) default: w.Header().Set("Content-Type", "application/xml") data, err := xml.Marshal(sub) diff --git a/handler/middleware.go b/handler/middleware.go index 53f75eb..fd20bba 100644 --- a/handler/middleware.go +++ b/handler/middleware.go @@ -44,7 +44,9 @@ func (c *Controller) CheckParameters(next http.HandlerFunc) http.HandlerFunc { if param != "" { continue } - respondError(w, r, 10, fmt.Sprintf("please provide a `%s` parameter", req)) + respondError(w, r, + 10, fmt.Sprintf("please provide a `%s` parameter", req), + ) return } username := r.URL.Query().Get("u") @@ -54,7 +56,9 @@ func (c *Controller) CheckParameters(next http.HandlerFunc) http.HandlerFunc { passwordAuth := token == "" && salt == "" tokenAuth := password == "" if tokenAuth == passwordAuth { - respondError(w, r, 10, "please provide parameters `t` and `s`, or just `p`") + respondError(w, r, + 10, "please provide parameters `t` and `s`, or just `p`", + ) return } user := db.User{ diff --git a/tags/format.go b/tags/format.go new file mode 100644 index 0000000..3b41e83 --- /dev/null +++ b/tags/format.go @@ -0,0 +1,19 @@ +package tags + +type probeData struct { + Format *probeFormat `json:"format"` +} + +type probeFormat struct { + Filename string `json:"filename"` + NBStreams int `json:"nb_streams"` + NBPrograms int `json:"nb_programs"` + FormatName string `json:"format_name"` + FormatLongName string `json:"format_long_name"` + StartTime float64 `json:"start_time,string"` + Duration float64 `json:"duration,string"` + Size string `json:"size"` + Bitrate int `json:"bit_rate,string"` + ProbeScore int `json:"probe_score"` + Tags map[string]string `json:"tags"` +} diff --git a/tags/static/test_flac b/tags/static/test_flac new file mode 100644 index 0000000..c4bc261 Binary files /dev/null and b/tags/static/test_flac differ diff --git a/tags/static/test_mp3 b/tags/static/test_mp3 new file mode 100644 index 0000000..402084a Binary files /dev/null and b/tags/static/test_mp3 differ diff --git a/tags/tags.go b/tags/tags.go new file mode 100644 index 0000000..4c92bf8 --- /dev/null +++ b/tags/tags.go @@ -0,0 +1,203 @@ +package tags + +import ( + "encoding/json" + "fmt" + "os/exec" + "strconv" +) + +var ( + titleFields = []string{ + "TITLE", + "title", + } + albumFields = []string{ + "ALBUM", + "album", + } + artistFields = []string{ + "ARTIST", + "artist", + } + albumArtistFields = []string{ + "ALBUM ARTIST", + "ALBUMARTIST", + "ALBUM_ARTIST", + "album artist", + "album_artist", + "albumartist", + } + composerFields = []string{ + "COMPOSER", + "composer", + } + genreFields = []string{ + "GENRE", + "genre", + } + yearFields = []string{ + "YEAR", + "year", + } + trackFields = []string{ + "TRACK", + "track", + } + totaltrackFields = []string{ + "TOTALTRACKS", + "TRACKC", + "TRACKTOTAL", + "totaltracks", + "trackc", + "tracktotal", + } + discFields = []string{ + "DISC", + "disc", + } + totaldiscFields = []string{ + "DISCC", + "DISCTOTAL", + "TOTALDISCS", + "discc", + "disctotal", + "totaldiscs", + } + lyricsFields = []string{ + "LYRICS", + "lyrics", + } + commentFields = []string{ + "COMMENT", + "COMMENTS", + "comment", + "comments", + } +) + +func firstExisting(keys *[]string, map_ *map[string]string) string { + for _, field := range *keys { + v, ok := (*map_)[field] + if !ok { + continue + } + return v + } + return "" +} + +type Metadata interface { + Title() string + Album() string + Artist() string + AlbumArtist() string + Composer() string + Genre() string + Year() int + Track() int + TotalTracks() int + Disc() int + TotalDiscs() int + Lyrics() string + Comment() string + Length() float64 + Format() string + Bitrate() int +} + +type Track struct { + format *probeFormat +} + +func (t *Track) Title() string { + return firstExisting(&titleFields, &t.format.Tags) +} + +func (t *Track) Album() string { + return firstExisting(&albumFields, &t.format.Tags) +} + +func (t *Track) Artist() string { + return firstExisting(&artistFields, &t.format.Tags) +} + +func (t *Track) AlbumArtist() string { + return firstExisting(&albumArtistFields, &t.format.Tags) +} + +func (t *Track) Composer() string { + return firstExisting(&composerFields, &t.format.Tags) +} + +func (t *Track) Genre() string { + return firstExisting(&genreFields, &t.format.Tags) +} + +func (t *Track) Year() int { + i, _ := strconv.Atoi(firstExisting(&yearFields, &t.format.Tags)) + return i +} + +func (t *Track) Track() int { + i, _ := strconv.Atoi(firstExisting(&trackFields, &t.format.Tags)) + return i +} + +func (t *Track) TotalTracks() int { + i, _ := strconv.Atoi(firstExisting(&totaltrackFields, &t.format.Tags)) + return i +} + +func (t *Track) Disc() int { + i, _ := strconv.Atoi(firstExisting(&discFields, &t.format.Tags)) + return i +} + +func (t *Track) TotalDiscs() int { + i, _ := strconv.Atoi(firstExisting(&totaldiscFields, &t.format.Tags)) + return i +} + +func (t *Track) Lyrics() string { + return firstExisting(&lyricsFields, &t.format.Tags) +} + +func (t *Track) Comment() string { + return firstExisting(&commentFields, &t.format.Tags) +} + +func (t *Track) Format() string { + return t.format.FormatName +} + +func (t *Track) Length() float64 { + return t.format.Duration +} + +func (t *Track) Bitrate() int { + return t.format.Bitrate +} + +func Read(filename string) (Metadata, error) { + command := exec.Command( + "ffprobe", + "-print_format", "json", + "-show_format", + filename, + ) + probe, err := command.Output() + if err != nil { + return nil, fmt.Errorf("when running ffprobe with `%s`: %v\n", + filename, err) + } + var data probeData + err = json.Unmarshal(probe, &data) + if err != nil { + return nil, fmt.Errorf("when unmarshalling: %v\n", err) + } + track := Track{ + format: data.Format, + } + return &track, nil +} diff --git a/tags/tags_test.go b/tags/tags_test.go new file mode 100644 index 0000000..96817f7 --- /dev/null +++ b/tags/tags_test.go @@ -0,0 +1,37 @@ +package tags + +import ( + "fmt" + "os" + "testing" + "time" +) + +func TestRead(t *testing.T) { + start := time.Now() + cwd, _ := os.Getwd() + data, err := Read( + fmt.Sprintf("%s/static/test_flac", cwd), + ) + if err != nil { + t.Errorf("when reading tags: %v\n", err) + return + } + length := data.Length() + if length != 160.4 { + t.Errorf("expected length to be `160.4`, got %f", length) + } + bitrate := data.Bitrate() + if bitrate != 815694 { + t.Errorf("expected bitrate to be `815694`, got %d", bitrate) + } + format := data.Format() + if format != "flac" { + t.Errorf("expected format to be `flac`, got %s", format) + } + fmt.Println(data.Title()) + fmt.Println(data.Album()) + fmt.Println(data.AlbumArtist()) + fmt.Println(data.Year()) + fmt.Printf("it's been %s\n", time.Since(start)) +} diff --git a/vendor/github.com/dgrijalva/jwt-go/.gitignore b/vendor/github.com/dgrijalva/jwt-go/.gitignore deleted file mode 100644 index 80bed65..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -bin - - diff --git a/vendor/github.com/dgrijalva/jwt-go/.travis.yml b/vendor/github.com/dgrijalva/jwt-go/.travis.yml deleted file mode 100644 index 1027f56..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -script: - - go vet ./... - - go test -v ./... - -go: - - 1.3 - - 1.4 - - 1.5 - - 1.6 - - 1.7 - - tip diff --git a/vendor/github.com/dgrijalva/jwt-go/LICENSE b/vendor/github.com/dgrijalva/jwt-go/LICENSE deleted file mode 100644 index df83a9c..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -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/dgrijalva/jwt-go/MIGRATION_GUIDE.md b/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md deleted file mode 100644 index 7fc1f79..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md +++ /dev/null @@ -1,97 +0,0 @@ -## Migration Guide from v2 -> v3 - -Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code. - -### `Token.Claims` is now an interface type - -The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`. - -`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property. - -The old example for parsing a token looked like this.. - -```go - if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { - fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) - } -``` - -is now directly mapped to... - -```go - if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { - claims := token.Claims.(jwt.MapClaims) - fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) - } -``` - -`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type. - -```go - type MyCustomClaims struct { - User string - *StandardClaims - } - - if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil { - claims := token.Claims.(*MyCustomClaims) - fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt) - } -``` - -### `ParseFromRequest` has been moved - -To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`. - -`Extractors` do the work of picking the token string out of a request. The interface is simple and composable. - -This simple parsing example: - -```go - if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil { - fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) - } -``` - -is directly mapped to: - -```go - if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil { - claims := token.Claims.(jwt.MapClaims) - fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) - } -``` - -There are several concrete `Extractor` types provided for your convenience: - -* `HeaderExtractor` will search a list of headers until one contains content. -* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content. -* `MultiExtractor` will try a list of `Extractors` in order until one returns content. -* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token. -* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument -* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header - - -### RSA signing methods no longer accept `[]byte` keys - -Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse. - -To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types. - -```go - func keyLookupFunc(*Token) (interface{}, error) { - // Don't forget to validate the alg is what you expect: - if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { - return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) - } - - // Look up key - key, err := lookupPublicKey(token.Header["kid"]) - if err != nil { - return nil, err - } - - // Unpack key from PEM encoded PKCS8 - return jwt.ParseRSAPublicKeyFromPEM(key) - } -``` diff --git a/vendor/github.com/dgrijalva/jwt-go/README.md b/vendor/github.com/dgrijalva/jwt-go/README.md deleted file mode 100644 index d358d88..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# jwt-go - -[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go) -[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go) - -A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) - -**NEW VERSION COMING:** There have been a lot of improvements suggested since the version 3.0.0 released in 2016. I'm working now on cutting two different releases: 3.2.0 will contain any non-breaking changes or enhancements. 4.0.0 will follow shortly which will include breaking changes. See the 4.0.0 milestone to get an idea of what's coming. If you have other ideas, or would like to participate in 4.0.0, now's the time. If you depend on this library and don't want to be interrupted, I recommend you use your dependency mangement tool to pin to version 3. - -**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail. - -**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. - -## What the heck is a JWT? - -JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. - -In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way. - -The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. - -The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own. - -## What's in the box? - -This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. - -## Examples - -See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage: - -* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac) -* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac) -* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples) - -## Extensions - -This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. - -Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go - -## Compliance - -This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences: - -* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. - -## Project Status & Versioning - -This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). - -This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). - -While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v3`. It will do the right thing WRT semantic versioning. - -**BREAKING CHANGES:*** -* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. - -## Usage Tips - -### Signing vs Encryption - -A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: - -* The author of the token was in the possession of the signing secret -* The data has not been modified since it was signed - -It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. - -### Choosing a Signing Method - -There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. - -Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. - -Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. - -### Signing Methods and Key Types - -Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: - -* The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation -* The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation -* The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation - -### JWT and OAuth - -It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. - -Without going too far down the rabbit hole, here's a description of the interaction of these technologies: - -* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. -* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. -* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. - -## More - -Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). - -The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. diff --git a/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md b/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md deleted file mode 100644 index 6370298..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md +++ /dev/null @@ -1,118 +0,0 @@ -## `jwt-go` Version History - -#### 3.2.0 - -* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation -* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate -* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before. -* Deprecated `ParseFromRequestWithClaims` to simplify API in the future. - -#### 3.1.0 - -* Improvements to `jwt` command line tool -* Added `SkipClaimsValidation` option to `Parser` -* Documentation updates - -#### 3.0.0 - -* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code - * Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. - * `ParseFromRequest` has been moved to `request` subpackage and usage has changed - * The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. -* Other Additions and Changes - * Added `Claims` interface type to allow users to decode the claims into a custom type - * Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. - * Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage - * Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` - * Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. - * Added several new, more specific, validation errors to error type bitmask - * Moved examples from README to executable example files - * Signing method registry is now thread safe - * Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) - -#### 2.7.0 - -This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. - -* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying -* Error text for expired tokens includes how long it's been expired -* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` -* Documentation updates - -#### 2.6.0 - -* Exposed inner error within ValidationError -* Fixed validation errors when using UseJSONNumber flag -* Added several unit tests - -#### 2.5.0 - -* Added support for signing method none. You shouldn't use this. The API tries to make this clear. -* Updated/fixed some documentation -* Added more helpful error message when trying to parse tokens that begin with `BEARER ` - -#### 2.4.0 - -* Added new type, Parser, to allow for configuration of various parsing parameters - * You can now specify a list of valid signing methods. Anything outside this set will be rejected. - * You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON -* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) -* Fixed some bugs with ECDSA parsing - -#### 2.3.0 - -* Added support for ECDSA signing methods -* Added support for RSA PSS signing methods (requires go v1.4) - -#### 2.2.0 - -* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. - -#### 2.1.0 - -Backwards compatible API change that was missed in 2.0.0. - -* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` - -#### 2.0.0 - -There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. - -The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. - -It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. - -* **Compatibility Breaking Changes** - * `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` - * `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` - * `KeyFunc` now returns `interface{}` instead of `[]byte` - * `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key - * `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key -* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. - * Added public package global `SigningMethodHS256` - * Added public package global `SigningMethodHS384` - * Added public package global `SigningMethodHS512` -* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. - * Added public package global `SigningMethodRS256` - * Added public package global `SigningMethodRS384` - * Added public package global `SigningMethodRS512` -* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. -* Refactored the RSA implementation to be easier to read -* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` - -#### 1.0.2 - -* Fixed bug in parsing public keys from certificates -* Added more tests around the parsing of keys for RS256 -* Code refactoring in RS256 implementation. No functional changes - -#### 1.0.1 - -* Fixed panic if RS256 signing method was passed an invalid key - -#### 1.0.0 - -* First versioned release -* API stabilized -* Supports creating, signing, parsing, and validating JWT tokens -* Supports RS256 and HS256 signing methods \ No newline at end of file diff --git a/vendor/github.com/dgrijalva/jwt-go/claims.go b/vendor/github.com/dgrijalva/jwt-go/claims.go deleted file mode 100644 index f0228f0..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/claims.go +++ /dev/null @@ -1,134 +0,0 @@ -package jwt - -import ( - "crypto/subtle" - "fmt" - "time" -) - -// For a type to be a Claims object, it must just have a Valid method that determines -// if the token is invalid for any supported reason -type Claims interface { - Valid() error -} - -// Structured version of Claims Section, as referenced at -// https://tools.ietf.org/html/rfc7519#section-4.1 -// See examples for how to use this with your own claim types -type StandardClaims struct { - Audience string `json:"aud,omitempty"` - ExpiresAt int64 `json:"exp,omitempty"` - Id string `json:"jti,omitempty"` - IssuedAt int64 `json:"iat,omitempty"` - Issuer string `json:"iss,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - Subject string `json:"sub,omitempty"` -} - -// Validates time based claims "exp, iat, nbf". -// There is no accounting for clock skew. -// As well, if any of the above claims are not in the token, it will still -// be considered a valid claim. -func (c StandardClaims) Valid() error { - vErr := new(ValidationError) - now := TimeFunc().Unix() - - // The claims below are optional, by default, so if they are set to the - // default value in Go, let's not fail the verification for them. - if c.VerifyExpiresAt(now, false) == false { - delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) - vErr.Inner = fmt.Errorf("token is expired by %v", delta) - vErr.Errors |= ValidationErrorExpired - } - - if c.VerifyIssuedAt(now, false) == false { - vErr.Inner = fmt.Errorf("Token used before issued") - vErr.Errors |= ValidationErrorIssuedAt - } - - if c.VerifyNotBefore(now, false) == false { - vErr.Inner = fmt.Errorf("token is not valid yet") - vErr.Errors |= ValidationErrorNotValidYet - } - - if vErr.valid() { - return nil - } - - return vErr -} - -// Compares the aud claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { - return verifyAud(c.Audience, cmp, req) -} - -// Compares the exp claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { - return verifyExp(c.ExpiresAt, cmp, req) -} - -// Compares the iat claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { - return verifyIat(c.IssuedAt, cmp, req) -} - -// Compares the iss claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { - return verifyIss(c.Issuer, cmp, req) -} - -// Compares the nbf claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { - return verifyNbf(c.NotBefore, cmp, req) -} - -// ----- helpers - -func verifyAud(aud string, cmp string, required bool) bool { - if aud == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { - return true - } else { - return false - } -} - -func verifyExp(exp int64, now int64, required bool) bool { - if exp == 0 { - return !required - } - return now <= exp -} - -func verifyIat(iat int64, now int64, required bool) bool { - if iat == 0 { - return !required - } - return now >= iat -} - -func verifyIss(iss string, cmp string, required bool) bool { - if iss == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { - return true - } else { - return false - } -} - -func verifyNbf(nbf int64, now int64, required bool) bool { - if nbf == 0 { - return !required - } - return now >= nbf -} diff --git a/vendor/github.com/dgrijalva/jwt-go/doc.go b/vendor/github.com/dgrijalva/jwt-go/doc.go deleted file mode 100644 index a86dc1a..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html -// -// See README.md for more info. -package jwt diff --git a/vendor/github.com/dgrijalva/jwt-go/ecdsa.go b/vendor/github.com/dgrijalva/jwt-go/ecdsa.go deleted file mode 100644 index f977381..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/ecdsa.go +++ /dev/null @@ -1,148 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rand" - "errors" - "math/big" -) - -var ( - // Sadly this is missing from crypto/ecdsa compared to crypto/rsa - ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") -) - -// Implements the ECDSA family of signing methods signing methods -// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification -type SigningMethodECDSA struct { - Name string - Hash crypto.Hash - KeySize int - CurveBits int -} - -// Specific instances for EC256 and company -var ( - SigningMethodES256 *SigningMethodECDSA - SigningMethodES384 *SigningMethodECDSA - SigningMethodES512 *SigningMethodECDSA -) - -func init() { - // ES256 - SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} - RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { - return SigningMethodES256 - }) - - // ES384 - SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} - RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { - return SigningMethodES384 - }) - - // ES512 - SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} - RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { - return SigningMethodES512 - }) -} - -func (m *SigningMethodECDSA) Alg() string { - return m.Name -} - -// Implements the Verify method from SigningMethod -// For this verify method, key must be an ecdsa.PublicKey struct -func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - // Get the key - var ecdsaKey *ecdsa.PublicKey - switch k := key.(type) { - case *ecdsa.PublicKey: - ecdsaKey = k - default: - return ErrInvalidKeyType - } - - if len(sig) != 2*m.KeySize { - return ErrECDSAVerification - } - - r := big.NewInt(0).SetBytes(sig[:m.KeySize]) - s := big.NewInt(0).SetBytes(sig[m.KeySize:]) - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Verify the signature - if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true { - return nil - } else { - return ErrECDSAVerification - } -} - -// Implements the Sign method from SigningMethod -// For this signing method, key must be an ecdsa.PrivateKey struct -func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { - // Get the key - var ecdsaKey *ecdsa.PrivateKey - switch k := key.(type) { - case *ecdsa.PrivateKey: - ecdsaKey = k - default: - return "", ErrInvalidKeyType - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return r, s - if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { - curveBits := ecdsaKey.Curve.Params().BitSize - - if m.CurveBits != curveBits { - return "", ErrInvalidKey - } - - keyBytes := curveBits / 8 - if curveBits%8 > 0 { - keyBytes += 1 - } - - // We serialize the outpus (r and s) into big-endian byte arrays and pad - // them with zeros on the left to make sure the sizes work out. Both arrays - // must be keyBytes long, and the output must be 2*keyBytes long. - rBytes := r.Bytes() - rBytesPadded := make([]byte, keyBytes) - copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) - - sBytes := s.Bytes() - sBytesPadded := make([]byte, keyBytes) - copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) - - out := append(rBytesPadded, sBytesPadded...) - - return EncodeSegment(out), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go b/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go deleted file mode 100644 index d19624b..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go +++ /dev/null @@ -1,67 +0,0 @@ -package jwt - -import ( - "crypto/ecdsa" - "crypto/x509" - "encoding/pem" - "errors" -) - -var ( - ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") - ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") -) - -// Parse PEM encoded Elliptic Curve Private Key Structure -func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { - return nil, err - } - - var pkey *ecdsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { - return nil, ErrNotECPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 public key -func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - parsedKey = cert.PublicKey - } else { - return nil, err - } - } - - var pkey *ecdsa.PublicKey - var ok bool - if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { - return nil, ErrNotECPublicKey - } - - return pkey, nil -} diff --git a/vendor/github.com/dgrijalva/jwt-go/errors.go b/vendor/github.com/dgrijalva/jwt-go/errors.go deleted file mode 100644 index 1c93024..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/errors.go +++ /dev/null @@ -1,59 +0,0 @@ -package jwt - -import ( - "errors" -) - -// Error constants -var ( - ErrInvalidKey = errors.New("key is invalid") - ErrInvalidKeyType = errors.New("key is of invalid type") - ErrHashUnavailable = errors.New("the requested hash function is unavailable") -) - -// The errors that might occur when parsing and validating a token -const ( - ValidationErrorMalformed uint32 = 1 << iota // Token is malformed - ValidationErrorUnverifiable // Token could not be verified because of signing problems - ValidationErrorSignatureInvalid // Signature validation failed - - // Standard Claim validation errors - ValidationErrorAudience // AUD validation failed - ValidationErrorExpired // EXP validation failed - ValidationErrorIssuedAt // IAT validation failed - ValidationErrorIssuer // ISS validation failed - ValidationErrorNotValidYet // NBF validation failed - ValidationErrorId // JTI validation failed - ValidationErrorClaimsInvalid // Generic claims validation error -) - -// Helper for constructing a ValidationError with a string error message -func NewValidationError(errorText string, errorFlags uint32) *ValidationError { - return &ValidationError{ - text: errorText, - Errors: errorFlags, - } -} - -// The error from Parse if token is not valid -type ValidationError struct { - Inner error // stores the error returned by external dependencies, i.e.: KeyFunc - Errors uint32 // bitfield. see ValidationError... constants - text string // errors that do not have a valid error just have text -} - -// Validation error is an error type -func (e ValidationError) Error() string { - if e.Inner != nil { - return e.Inner.Error() - } else if e.text != "" { - return e.text - } else { - return "token is invalid" - } -} - -// No errors -func (e *ValidationError) valid() bool { - return e.Errors == 0 -} diff --git a/vendor/github.com/dgrijalva/jwt-go/hmac.go b/vendor/github.com/dgrijalva/jwt-go/hmac.go deleted file mode 100644 index addbe5d..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/hmac.go +++ /dev/null @@ -1,95 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/hmac" - "errors" -) - -// Implements the HMAC-SHA family of signing methods signing methods -// Expects key type of []byte for both signing and validation -type SigningMethodHMAC struct { - Name string - Hash crypto.Hash -} - -// Specific instances for HS256 and company -var ( - SigningMethodHS256 *SigningMethodHMAC - SigningMethodHS384 *SigningMethodHMAC - SigningMethodHS512 *SigningMethodHMAC - ErrSignatureInvalid = errors.New("signature is invalid") -) - -func init() { - // HS256 - SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} - RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { - return SigningMethodHS256 - }) - - // HS384 - SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} - RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { - return SigningMethodHS384 - }) - - // HS512 - SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} - RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { - return SigningMethodHS512 - }) -} - -func (m *SigningMethodHMAC) Alg() string { - return m.Name -} - -// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. -func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { - // Verify the key is the right type - keyBytes, ok := key.([]byte) - if !ok { - return ErrInvalidKeyType - } - - // Decode signature, for comparison - sig, err := DecodeSegment(signature) - if err != nil { - return err - } - - // Can we use the specified hashing method? - if !m.Hash.Available() { - return ErrHashUnavailable - } - - // This signing method is symmetric, so we validate the signature - // by reproducing the signature from the signing string and key, then - // comparing that against the provided signature. - hasher := hmac.New(m.Hash.New, keyBytes) - hasher.Write([]byte(signingString)) - if !hmac.Equal(sig, hasher.Sum(nil)) { - return ErrSignatureInvalid - } - - // No validation errors. Signature is good. - return nil -} - -// Implements the Sign method from SigningMethod for this signing method. -// Key must be []byte -func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { - if keyBytes, ok := key.([]byte); ok { - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := hmac.New(m.Hash.New, keyBytes) - hasher.Write([]byte(signingString)) - - return EncodeSegment(hasher.Sum(nil)), nil - } - - return "", ErrInvalidKeyType -} diff --git a/vendor/github.com/dgrijalva/jwt-go/map_claims.go b/vendor/github.com/dgrijalva/jwt-go/map_claims.go deleted file mode 100644 index 291213c..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/map_claims.go +++ /dev/null @@ -1,94 +0,0 @@ -package jwt - -import ( - "encoding/json" - "errors" - // "fmt" -) - -// Claims type that uses the map[string]interface{} for JSON decoding -// This is the default claims type if you don't supply one -type MapClaims map[string]interface{} - -// Compares the aud claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyAudience(cmp string, req bool) bool { - aud, _ := m["aud"].(string) - return verifyAud(aud, cmp, req) -} - -// Compares the exp claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { - switch exp := m["exp"].(type) { - case float64: - return verifyExp(int64(exp), cmp, req) - case json.Number: - v, _ := exp.Int64() - return verifyExp(v, cmp, req) - } - return req == false -} - -// Compares the iat claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { - switch iat := m["iat"].(type) { - case float64: - return verifyIat(int64(iat), cmp, req) - case json.Number: - v, _ := iat.Int64() - return verifyIat(v, cmp, req) - } - return req == false -} - -// Compares the iss claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { - iss, _ := m["iss"].(string) - return verifyIss(iss, cmp, req) -} - -// Compares the nbf claim against cmp. -// If required is false, this method will return true if the value matches or is unset -func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { - switch nbf := m["nbf"].(type) { - case float64: - return verifyNbf(int64(nbf), cmp, req) - case json.Number: - v, _ := nbf.Int64() - return verifyNbf(v, cmp, req) - } - return req == false -} - -// Validates time based claims "exp, iat, nbf". -// There is no accounting for clock skew. -// As well, if any of the above claims are not in the token, it will still -// be considered a valid claim. -func (m MapClaims) Valid() error { - vErr := new(ValidationError) - now := TimeFunc().Unix() - - if m.VerifyExpiresAt(now, false) == false { - vErr.Inner = errors.New("Token is expired") - vErr.Errors |= ValidationErrorExpired - } - - if m.VerifyIssuedAt(now, false) == false { - vErr.Inner = errors.New("Token used before issued") - vErr.Errors |= ValidationErrorIssuedAt - } - - if m.VerifyNotBefore(now, false) == false { - vErr.Inner = errors.New("Token is not valid yet") - vErr.Errors |= ValidationErrorNotValidYet - } - - if vErr.valid() { - return nil - } - - return vErr -} diff --git a/vendor/github.com/dgrijalva/jwt-go/none.go b/vendor/github.com/dgrijalva/jwt-go/none.go deleted file mode 100644 index f04d189..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/none.go +++ /dev/null @@ -1,52 +0,0 @@ -package jwt - -// Implements the none signing method. This is required by the spec -// but you probably should never use it. -var SigningMethodNone *signingMethodNone - -const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" - -var NoneSignatureTypeDisallowedError error - -type signingMethodNone struct{} -type unsafeNoneMagicConstant string - -func init() { - SigningMethodNone = &signingMethodNone{} - NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) - - RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { - return SigningMethodNone - }) -} - -func (m *signingMethodNone) Alg() string { - return "none" -} - -// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { - // Key must be UnsafeAllowNoneSignatureType to prevent accidentally - // accepting 'none' signing method - if _, ok := key.(unsafeNoneMagicConstant); !ok { - return NoneSignatureTypeDisallowedError - } - // If signing method is none, signature must be an empty string - if signature != "" { - return NewValidationError( - "'none' signing method with non-empty signature", - ValidationErrorSignatureInvalid, - ) - } - - // Accept 'none' signing method. - return nil -} - -// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { - if _, ok := key.(unsafeNoneMagicConstant); ok { - return "", nil - } - return "", NoneSignatureTypeDisallowedError -} diff --git a/vendor/github.com/dgrijalva/jwt-go/parser.go b/vendor/github.com/dgrijalva/jwt-go/parser.go deleted file mode 100644 index d6901d9..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/parser.go +++ /dev/null @@ -1,148 +0,0 @@ -package jwt - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" -) - -type Parser struct { - ValidMethods []string // If populated, only these methods will be considered valid - UseJSONNumber bool // Use JSON Number format in JSON decoder - SkipClaimsValidation bool // Skip claims validation during token parsing -} - -// Parse, validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { - return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) -} - -func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { - token, parts, err := p.ParseUnverified(tokenString, claims) - if err != nil { - return token, err - } - - // Verify signing method is in the required set - if p.ValidMethods != nil { - var signingMethodValid = false - var alg = token.Method.Alg() - for _, m := range p.ValidMethods { - if m == alg { - signingMethodValid = true - break - } - } - if !signingMethodValid { - // signing method is not in the listed set - return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) - } - } - - // Lookup key - var key interface{} - if keyFunc == nil { - // keyFunc was not provided. short circuiting validation - return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) - } - if key, err = keyFunc(token); err != nil { - // keyFunc returned an error - if ve, ok := err.(*ValidationError); ok { - return token, ve - } - return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} - } - - vErr := &ValidationError{} - - // Validate Claims - if !p.SkipClaimsValidation { - if err := token.Claims.Valid(); err != nil { - - // If the Claims Valid returned an error, check if it is a validation error, - // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set - if e, ok := err.(*ValidationError); !ok { - vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} - } else { - vErr = e - } - } - } - - // Perform validation - token.Signature = parts[2] - if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { - vErr.Inner = err - vErr.Errors |= ValidationErrorSignatureInvalid - } - - if vErr.valid() { - token.Valid = true - return token, nil - } - - return token, vErr -} - -// WARNING: Don't use this method unless you know what you're doing -// -// This method parses the token but doesn't validate the signature. It's only -// ever useful in cases where you know the signature is valid (because it has -// been checked previously in the stack) and you want to extract values from -// it. -func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { - parts = strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) - } - - token = &Token{Raw: tokenString} - - // parse Header - var headerBytes []byte - if headerBytes, err = DecodeSegment(parts[0]); err != nil { - if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { - return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) - } - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - if err = json.Unmarshal(headerBytes, &token.Header); err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - - // parse Claims - var claimBytes []byte - token.Claims = claims - - if claimBytes, err = DecodeSegment(parts[1]); err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) - if p.UseJSONNumber { - dec.UseNumber() - } - // JSON Decode. Special case for map type to avoid weird pointer behavior - if c, ok := token.Claims.(MapClaims); ok { - err = dec.Decode(&c) - } else { - err = dec.Decode(&claims) - } - // Handle decode error - if err != nil { - return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} - } - - // Lookup signature method - if method, ok := token.Header["alg"].(string); ok { - if token.Method = GetSigningMethod(method); token.Method == nil { - return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) - } - } else { - return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) - } - - return token, parts, nil -} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa.go b/vendor/github.com/dgrijalva/jwt-go/rsa.go deleted file mode 100644 index e4caf1c..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/rsa.go +++ /dev/null @@ -1,101 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" -) - -// Implements the RSA family of signing methods signing methods -// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation -type SigningMethodRSA struct { - Name string - Hash crypto.Hash -} - -// Specific instances for RS256 and company -var ( - SigningMethodRS256 *SigningMethodRSA - SigningMethodRS384 *SigningMethodRSA - SigningMethodRS512 *SigningMethodRSA -) - -func init() { - // RS256 - SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} - RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { - return SigningMethodRS256 - }) - - // RS384 - SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} - RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { - return SigningMethodRS384 - }) - - // RS512 - SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} - RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { - return SigningMethodRS512 - }) -} - -func (m *SigningMethodRSA) Alg() string { - return m.Name -} - -// Implements the Verify method from SigningMethod -// For this signing method, must be an *rsa.PublicKey structure. -func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - var rsaKey *rsa.PublicKey - var ok bool - - if rsaKey, ok = key.(*rsa.PublicKey); !ok { - return ErrInvalidKeyType - } - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Verify the signature - return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) -} - -// Implements the Sign method from SigningMethod -// For this signing method, must be an *rsa.PrivateKey structure. -func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { - var rsaKey *rsa.PrivateKey - var ok bool - - // Validate type of key - if rsaKey, ok = key.(*rsa.PrivateKey); !ok { - return "", ErrInvalidKey - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return the encoded bytes - if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { - return EncodeSegment(sigBytes), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go b/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go deleted file mode 100644 index 10ee9db..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go +++ /dev/null @@ -1,126 +0,0 @@ -// +build go1.4 - -package jwt - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" -) - -// Implements the RSAPSS family of signing methods signing methods -type SigningMethodRSAPSS struct { - *SigningMethodRSA - Options *rsa.PSSOptions -} - -// Specific instances for RS/PS and company -var ( - SigningMethodPS256 *SigningMethodRSAPSS - SigningMethodPS384 *SigningMethodRSAPSS - SigningMethodPS512 *SigningMethodRSAPSS -) - -func init() { - // PS256 - SigningMethodPS256 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ - Name: "PS256", - Hash: crypto.SHA256, - }, - &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA256, - }, - } - RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { - return SigningMethodPS256 - }) - - // PS384 - SigningMethodPS384 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ - Name: "PS384", - Hash: crypto.SHA384, - }, - &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA384, - }, - } - RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { - return SigningMethodPS384 - }) - - // PS512 - SigningMethodPS512 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ - Name: "PS512", - Hash: crypto.SHA512, - }, - &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA512, - }, - } - RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { - return SigningMethodPS512 - }) -} - -// Implements the Verify method from SigningMethod -// For this verify method, key must be an rsa.PublicKey struct -func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - var rsaKey *rsa.PublicKey - switch k := key.(type) { - case *rsa.PublicKey: - rsaKey = k - default: - return ErrInvalidKey - } - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) -} - -// Implements the Sign method from SigningMethod -// For this signing method, key must be an rsa.PrivateKey struct -func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { - var rsaKey *rsa.PrivateKey - - switch k := key.(type) { - case *rsa.PrivateKey: - rsaKey = k - default: - return "", ErrInvalidKeyType - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return the encoded bytes - if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { - return EncodeSegment(sigBytes), nil - } else { - return "", err - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go b/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go deleted file mode 100644 index a5ababf..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go +++ /dev/null @@ -1,101 +0,0 @@ -package jwt - -import ( - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" -) - -var ( - ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key") - ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") - ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") -) - -// Parse PEM encoded PKCS1 or PKCS8 private key -func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - var parsedKey interface{} - if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { - return nil, err - } - } - - var pkey *rsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { - return nil, ErrNotRSAPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 private key protected with password -func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - var parsedKey interface{} - - var blockDecrypted []byte - if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { - return nil, err - } - - if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil { - return nil, err - } - } - - var pkey *rsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { - return nil, ErrNotRSAPrivateKey - } - - return pkey, nil -} - -// Parse PEM encoded PKCS1 or PKCS8 public key -func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, ErrKeyMustBePEMEncoded - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - parsedKey = cert.PublicKey - } else { - return nil, err - } - } - - var pkey *rsa.PublicKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { - return nil, ErrNotRSAPublicKey - } - - return pkey, nil -} diff --git a/vendor/github.com/dgrijalva/jwt-go/signing_method.go b/vendor/github.com/dgrijalva/jwt-go/signing_method.go deleted file mode 100644 index ed1f212..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/signing_method.go +++ /dev/null @@ -1,35 +0,0 @@ -package jwt - -import ( - "sync" -) - -var signingMethods = map[string]func() SigningMethod{} -var signingMethodLock = new(sync.RWMutex) - -// Implement SigningMethod to add new methods for signing or verifying tokens. -type SigningMethod interface { - Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid - Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error - Alg() string // returns the alg identifier for this method (example: 'HS256') -} - -// Register the "alg" name and a factory function for signing method. -// This is typically done during init() in the method's implementation -func RegisterSigningMethod(alg string, f func() SigningMethod) { - signingMethodLock.Lock() - defer signingMethodLock.Unlock() - - signingMethods[alg] = f -} - -// Get a signing method from an "alg" string -func GetSigningMethod(alg string) (method SigningMethod) { - signingMethodLock.RLock() - defer signingMethodLock.RUnlock() - - if methodF, ok := signingMethods[alg]; ok { - method = methodF() - } - return -} diff --git a/vendor/github.com/dgrijalva/jwt-go/token.go b/vendor/github.com/dgrijalva/jwt-go/token.go deleted file mode 100644 index d637e08..0000000 --- a/vendor/github.com/dgrijalva/jwt-go/token.go +++ /dev/null @@ -1,108 +0,0 @@ -package jwt - -import ( - "encoding/base64" - "encoding/json" - "strings" - "time" -) - -// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). -// You can override it to use another time value. This is useful for testing or if your -// server uses a different time zone than your tokens. -var TimeFunc = time.Now - -// Parse methods use this callback function to supply -// the key for verification. The function receives the parsed, -// but unverified Token. This allows you to use properties in the -// Header of the token (such as `kid`) to identify which key to use. -type Keyfunc func(*Token) (interface{}, error) - -// A JWT Token. Different fields will be used depending on whether you're -// creating or parsing/verifying a token. -type Token struct { - Raw string // The raw token. Populated when you Parse a token - Method SigningMethod // The signing method used or to be used - Header map[string]interface{} // The first segment of the token - Claims Claims // The second segment of the token - Signature string // The third segment of the token. Populated when you Parse a token - Valid bool // Is the token valid? Populated when you Parse/Verify a token -} - -// Create a new Token. Takes a signing method -func New(method SigningMethod) *Token { - return NewWithClaims(method, MapClaims{}) -} - -func NewWithClaims(method SigningMethod, claims Claims) *Token { - return &Token{ - Header: map[string]interface{}{ - "typ": "JWT", - "alg": method.Alg(), - }, - Claims: claims, - Method: method, - } -} - -// Get the complete, signed token -func (t *Token) SignedString(key interface{}) (string, error) { - var sig, sstr string - var err error - if sstr, err = t.SigningString(); err != nil { - return "", err - } - if sig, err = t.Method.Sign(sstr, key); err != nil { - return "", err - } - return strings.Join([]string{sstr, sig}, "."), nil -} - -// Generate the signing string. This is the -// most expensive part of the whole deal. Unless you -// need this for something special, just go straight for -// the SignedString. -func (t *Token) SigningString() (string, error) { - var err error - parts := make([]string, 2) - for i, _ := range parts { - var jsonValue []byte - if i == 0 { - if jsonValue, err = json.Marshal(t.Header); err != nil { - return "", err - } - } else { - if jsonValue, err = json.Marshal(t.Claims); err != nil { - return "", err - } - } - - parts[i] = EncodeSegment(jsonValue) - } - return strings.Join(parts, "."), nil -} - -// Parse, validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { - return new(Parser).Parse(tokenString, keyFunc) -} - -func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { - return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) -} - -// Encode JWT specific base64url encoding with padding stripped -func EncodeSegment(seg []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") -} - -// Decode JWT specific base64url encoding with padding stripped -func DecodeSegment(seg string) ([]byte, error) { - if l := len(seg) % 4; l > 0 { - seg += strings.Repeat("=", 4-l) - } - - return base64.URLEncoding.DecodeString(seg) -} diff --git a/vendor/github.com/dhowden/tag/.editorconfig b/vendor/github.com/dhowden/tag/.editorconfig deleted file mode 100644 index 57515d0..0000000 --- a/vendor/github.com/dhowden/tag/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - -[*.go] -indent_style = tab -indent_size = 3 - -[*.md] -trim_trailing_whitespace = false - -[*] -# We recommend you to keep these unchanged -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/vendor/github.com/dhowden/tag/.travis.yml b/vendor/github.com/dhowden/tag/.travis.yml deleted file mode 100644 index 3a81bf9..0000000 --- a/vendor/github.com/dhowden/tag/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go - -go: - - 1.7 - - tip \ No newline at end of file diff --git a/vendor/github.com/dhowden/tag/LICENSE b/vendor/github.com/dhowden/tag/LICENSE deleted file mode 100644 index dfd760c..0000000 --- a/vendor/github.com/dhowden/tag/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright 2015, David Howden -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. diff --git a/vendor/github.com/dhowden/tag/README.md b/vendor/github.com/dhowden/tag/README.md deleted file mode 100644 index de81883..0000000 --- a/vendor/github.com/dhowden/tag/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# MP3/MP4/OGG/FLAC metadata parsing library -[![Build Status](https://travis-ci.org/dhowden/tag.svg?branch=master)](https://travis-ci.org/dhowden/tag) -[![GoDoc](https://godoc.org/github.com/dhowden/tag?status.svg)](https://godoc.org/github.com/dhowden/tag) - -This package provides MP3 (ID3v1,2.{2,3,4}) and MP4 (ACC, M4A, ALAC), OGG and FLAC metadata detection, parsing and artwork extraction. - -Detect and parse tag metadata from an `io.ReadSeeker` (i.e. an `*os.File`): - -```go -m, err := tag.ReadFrom(f) -if err != nil { - log.Fatal(err) -} -log.Print(m.Format()) // The detected format. -log.Print(m.Title()) // The title of the track (see Metadata interface for more details). -``` - -Parsed metadata is exported via a single interface (giving a consistent API for all supported metadata formats). - -```go -// Metadata is an interface which is used to describe metadata retrieved by this package. -type Metadata interface { - Format() Format - FileType() FileType - - Title() string - Album() string - Artist() string - AlbumArtist() string - Composer() string - Genre() string - Year() int - - Track() (int, int) // Number, Total - Disc() (int, int) // Number, Total - - Picture() *Picture // Artwork - Lyrics() string - Comment() string - - Raw() map[string]interface{} // NB: raw tag names are not consistent across formats. -} -``` - -## Audio Data Checksum (SHA1) - -This package also provides a metadata-invariant checksum for audio files: only the audio data is used to -construct the checksum. - -[http://godoc.org/github.com/dhowden/tag#Sum](http://godoc.org/github.com/dhowden/tag#Sum) - -## Tools - -There are simple command-line tools which demonstrate basic tag extraction and summing: - -```console -$ go get github.com/dhowden/tag/... -$ cd $GOPATH/bin -$ ./tag 11\ High\ Hopes.m4a -Metadata Format: MP4 -Title: High Hopes -Album: The Division Bell -Artist: Pink Floyd -Composer: Abbey Road Recording Studios/David Gilmour/Polly Samson -Year: 1994 -Track: 11 of 11 -Disc: 1 of 1 -Picture: Picture{Ext: jpeg, MIMEType: image/jpeg, Type: , Description: , Data.Size: 606109} - -$ ./sum 11\ High\ Hopes.m4a -2ae208c5f00a1f21f5fac9b7f6e0b8e52c06da29 -``` diff --git a/vendor/github.com/dhowden/tag/dsf.go b/vendor/github.com/dhowden/tag/dsf.go deleted file mode 100644 index d826a74..0000000 --- a/vendor/github.com/dhowden/tag/dsf.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "errors" - "io" -) - -// ReadDSFTags reads DSF metadata from the io.ReadSeeker, returning the resulting -// metadata in a Metadata implementation, or non-nil error if there was a problem. -// samples: http://www.2l.no/hires/index.html -func ReadDSFTags(r io.ReadSeeker) (Metadata, error) { - dsd, err := readString(r, 4) - if err != nil { - return nil, err - } - if dsd != "DSD " { - return nil, errors.New("expected 'DSD '") - } - - _, err = r.Seek(int64(16), io.SeekCurrent) - if err != nil { - return nil, err - } - - n4, err := readBytes(r, 8) - if err != nil { - return nil, err - } - id3Pointer := getIntLittleEndian(n4) - - _, err = r.Seek(int64(id3Pointer), io.SeekStart) - if err != nil { - return nil, err - } - - id3, err := ReadID3v2Tags(r) - if err != nil { - return nil, err - } - - return metadataDSF{id3}, nil -} - -type metadataDSF struct { - id3 Metadata -} - -func (m metadataDSF) Format() Format { - return m.id3.Format() -} - -func (m metadataDSF) FileType() FileType { - return DSF -} - -func (m metadataDSF) Title() string { - return m.id3.Title() -} - -func (m metadataDSF) Album() string { - return m.id3.Album() -} - -func (m metadataDSF) Artist() string { - return m.id3.Artist() -} - -func (m metadataDSF) AlbumArtist() string { - return m.id3.AlbumArtist() -} - -func (m metadataDSF) Composer() string { - return m.id3.Composer() -} - -func (m metadataDSF) Year() int { - return m.id3.Year() -} - -func (m metadataDSF) Genre() string { - return m.id3.Genre() -} - -func (m metadataDSF) Track() (int, int) { - return m.id3.Track() -} - -func (m metadataDSF) Disc() (int, int) { - return m.id3.Disc() -} - -func (m metadataDSF) Picture() *Picture { - return m.id3.Picture() -} - -func (m metadataDSF) Lyrics() string { - return m.id3.Lyrics() -} - -func (m metadataDSF) Comment() string { - return m.id3.Comment() -} - -func (m metadataDSF) Raw() map[string]interface{} { - return m.id3.Raw() -} diff --git a/vendor/github.com/dhowden/tag/flac.go b/vendor/github.com/dhowden/tag/flac.go deleted file mode 100644 index c370467..0000000 --- a/vendor/github.com/dhowden/tag/flac.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "errors" - "io" -) - -// blockType is a type which represents an enumeration of valid FLAC blocks -type blockType byte - -// FLAC block types. -const ( - // Stream Info Block 0 - // Padding Block 1 - // Application Block 2 - // Seektable Block 3 - // Cue Sheet Block 5 - vorbisCommentBlock blockType = 4 - pictureBlock blockType = 6 -) - -// ReadFLACTags reads FLAC metadata from the io.ReadSeeker, returning the resulting -// metadata in a Metadata implementation, or non-nil error if there was a problem. -func ReadFLACTags(r io.ReadSeeker) (Metadata, error) { - flac, err := readString(r, 4) - if err != nil { - return nil, err - } - if flac != "fLaC" { - return nil, errors.New("expected 'fLaC'") - } - - m := &metadataFLAC{ - newMetadataVorbis(), - } - - for { - last, err := m.readFLACMetadataBlock(r) - if err != nil { - return nil, err - } - - if last { - break - } - } - return m, nil -} - -type metadataFLAC struct { - *metadataVorbis -} - -func (m *metadataFLAC) readFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) { - blockHeader, err := readBytes(r, 1) - if err != nil { - return - } - - if getBit(blockHeader[0], 7) { - blockHeader[0] ^= (1 << 7) - last = true - } - - blockLen, err := readInt(r, 3) - if err != nil { - return - } - - switch blockType(blockHeader[0]) { - case vorbisCommentBlock: - err = m.readVorbisComment(r) - - case pictureBlock: - err = m.readPictureBlock(r) - - default: - _, err = r.Seek(int64(blockLen), io.SeekCurrent) - } - return -} - -func (m *metadataFLAC) FileType() FileType { - return FLAC -} diff --git a/vendor/github.com/dhowden/tag/id.go b/vendor/github.com/dhowden/tag/id.go deleted file mode 100644 index 2410356..0000000 --- a/vendor/github.com/dhowden/tag/id.go +++ /dev/null @@ -1,81 +0,0 @@ -package tag - -import ( - "fmt" - "io" -) - -// Identify identifies the format and file type of the data in the ReadSeeker. -func Identify(r io.ReadSeeker) (format Format, fileType FileType, err error) { - b, err := readBytes(r, 11) - if err != nil { - return - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - err = fmt.Errorf("could not seek back to original position: %v", err) - return - } - - switch { - case string(b[0:4]) == "fLaC": - return VORBIS, FLAC, nil - - case string(b[0:4]) == "OggS": - return VORBIS, OGG, nil - - case string(b[4:8]) == "ftyp": - b = b[8:11] - fileType = UnknownFileType - switch string(b) { - case "M4A": - fileType = M4A - - case "M4B": - fileType = M4B - - case "M4P": - fileType = M4P - } - return MP4, fileType, nil - - case string(b[0:3]) == "ID3": - b := b[3:] - switch uint(b[0]) { - case 2: - format = ID3v2_2 - case 3: - format = ID3v2_3 - case 4: - format = ID3v2_4 - case 0, 1: - fallthrough - default: - err = fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0])) - return - } - return format, MP3, nil - } - - n, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return - } - - tag, err := readString(r, 3) - if err != nil { - return - } - - _, err = r.Seek(-n, io.SeekCurrent) - if err != nil { - return - } - - if tag != "TAG" { - err = ErrNoTagsFound - return - } - return ID3v1, MP3, nil -} diff --git a/vendor/github.com/dhowden/tag/id3v1.go b/vendor/github.com/dhowden/tag/id3v1.go deleted file mode 100644 index 0953f0b..0000000 --- a/vendor/github.com/dhowden/tag/id3v1.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "errors" - "io" - "strconv" - "strings" -) - -// id3v1Genres is a list of genres as given in the ID3v1 specification. -var id3v1Genres = [...]string{ - "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", - "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", - "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", - "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", - "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", - "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", - "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", - "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", - "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", - "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", - "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", - "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", - "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", - "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", - "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", - "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall", -} - -// ErrNotID3v1 is an error which is returned when no ID3v1 header is found. -var ErrNotID3v1 = errors.New("invalid ID3v1 header") - -// ReadID3v1Tags reads ID3v1 tags from the io.ReadSeeker. Returns ErrNotID3v1 -// if there are no ID3v1 tags, otherwise non-nil error if there was a problem. -func ReadID3v1Tags(r io.ReadSeeker) (Metadata, error) { - _, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return nil, err - } - - if tag, err := readString(r, 3); err != nil { - return nil, err - } else if tag != "TAG" { - return nil, ErrNotID3v1 - } - - title, err := readString(r, 30) - if err != nil { - return nil, err - } - - artist, err := readString(r, 30) - if err != nil { - return nil, err - } - - album, err := readString(r, 30) - if err != nil { - return nil, err - } - - year, err := readString(r, 4) - if err != nil { - return nil, err - } - - commentBytes, err := readBytes(r, 30) - if err != nil { - return nil, err - } - - var comment string - var track int - if commentBytes[28] == 0 { - comment = trimString(string(commentBytes[:28])) - track = int(commentBytes[29]) - } else { - comment = trimString(string(commentBytes)) - } - - var genre string - genreID, err := readBytes(r, 1) - if err != nil { - return nil, err - } - if int(genreID[0]) < len(id3v1Genres) { - genre = id3v1Genres[int(genreID[0])] - } - - m := make(map[string]interface{}) - m["title"] = trimString(title) - m["artist"] = trimString(artist) - m["album"] = trimString(album) - m["year"] = trimString(year) - m["comment"] = trimString(comment) - m["track"] = track - m["genre"] = genre - - return metadataID3v1(m), nil -} - -func trimString(x string) string { - return strings.TrimSpace(strings.Trim(x, "\x00")) -} - -// metadataID3v1 is the implementation of Metadata used for ID3v1 tags. -type metadataID3v1 map[string]interface{} - -func (metadataID3v1) Format() Format { return ID3v1 } -func (metadataID3v1) FileType() FileType { return MP3 } -func (m metadataID3v1) Raw() map[string]interface{} { return m } - -func (m metadataID3v1) Title() string { return m["title"].(string) } -func (m metadataID3v1) Album() string { return m["album"].(string) } -func (m metadataID3v1) Artist() string { return m["artist"].(string) } -func (m metadataID3v1) Genre() string { return m["genre"].(string) } - -func (m metadataID3v1) Year() int { - y := m["year"].(string) - n, err := strconv.Atoi(y) - if err != nil { - return 0 - } - return n -} - -func (m metadataID3v1) Track() (int, int) { return m["track"].(int), 0 } - -func (m metadataID3v1) AlbumArtist() string { return "" } -func (m metadataID3v1) Composer() string { return "" } -func (metadataID3v1) Disc() (int, int) { return 0, 0 } -func (m metadataID3v1) Picture() *Picture { return nil } -func (m metadataID3v1) Lyrics() string { return "" } -func (m metadataID3v1) Comment() string { return m["comment"].(string) } diff --git a/vendor/github.com/dhowden/tag/id3v2.go b/vendor/github.com/dhowden/tag/id3v2.go deleted file mode 100644 index 063e6cb..0000000 --- a/vendor/github.com/dhowden/tag/id3v2.go +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "fmt" - "io" - "regexp" - "strconv" - "strings" -) - -var id3v2Genres = [...]string{ - "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", - "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", - "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", - "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", - "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", - "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", - "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", - "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", - "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", - "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", - "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", - "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", - "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", - "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", - "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", - "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", - "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", - "Britpop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", - "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", - "Christian Rock ", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", - "Synthpop", -} - -// id3v2Header is a type which represents an ID3v2 tag header. -type id3v2Header struct { - Version Format - Unsynchronisation bool - ExtendedHeader bool - Experimental bool - Size int -} - -// readID3v2Header reads the ID3v2 header from the given io.Reader. -// offset it number of bytes of header that was read -func readID3v2Header(r io.Reader) (h *id3v2Header, offset int, err error) { - offset = 10 - b, err := readBytes(r, offset) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 10 bytes (ID3v2Header): %v", err) - } - - if string(b[0:3]) != "ID3" { - return nil, 0, fmt.Errorf("expected to read \"ID3\"") - } - - b = b[3:] - var vers Format - switch uint(b[0]) { - case 2: - vers = ID3v2_2 - case 3: - vers = ID3v2_3 - case 4: - vers = ID3v2_4 - case 0, 1: - fallthrough - default: - return nil, 0, fmt.Errorf("ID3 version: %v, expected: 2, 3 or 4", uint(b[0])) - } - - // NB: We ignore b[1] (the revision) as we don't currently rely on it. - h = &id3v2Header{ - Version: vers, - Unsynchronisation: getBit(b[2], 7), - ExtendedHeader: getBit(b[2], 6), - Experimental: getBit(b[2], 5), - Size: get7BitChunkedInt(b[3:7]), - } - - if h.ExtendedHeader { - switch vers { - case ID3v2_3: - b, err := readBytes(r, 4) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 4 bytes (ID3v23 extended header len): %v", err) - } - // skip header, size is excluding len bytes - extendedHeaderSize := getInt(b) - _, err = readBytes(r, extendedHeaderSize) - if err != nil { - return nil, 0, fmt.Errorf("expected to read %d bytes (ID3v23 skip extended header): %v", extendedHeaderSize, err) - } - offset += extendedHeaderSize - case ID3v2_4: - b, err := readBytes(r, 4) - if err != nil { - return nil, 0, fmt.Errorf("expected to read 4 bytes (ID3v24 extended header len): %v", err) - } - // skip header, size is synchsafe int including len bytes - extendedHeaderSize := get7BitChunkedInt(b) - 4 - _, err = readBytes(r, extendedHeaderSize) - if err != nil { - return nil, 0, fmt.Errorf("expected to read %d bytes (ID3v24 skip extended header): %v", extendedHeaderSize, err) - } - offset += extendedHeaderSize - default: - // nop, only 2.3 and 2.4 should have extended header - } - } - - return h, offset, nil -} - -// id3v2FrameFlags is a type which represents the flags which can be set on an ID3v2 frame. -type id3v2FrameFlags struct { - // Message (ID3 2.3.0 and 2.4.0) - TagAlterPreservation bool - FileAlterPreservation bool - ReadOnly bool - - // Format (ID3 2.3.0 and 2.4.0) - Compression bool - Encryption bool - GroupIdentity bool - // ID3 2.4.0 only (see http://id3.org/id3v2.4.0-structure sec 4.1) - Unsynchronisation bool - DataLengthIndicator bool -} - -func readID3v23FrameFlags(r io.Reader) (*id3v2FrameFlags, error) { - b, err := readBytes(r, 2) - if err != nil { - return nil, err - } - - msg := b[0] - fmt := b[1] - - return &id3v2FrameFlags{ - TagAlterPreservation: getBit(msg, 7), - FileAlterPreservation: getBit(msg, 6), - ReadOnly: getBit(msg, 5), - Compression: getBit(fmt, 7), - Encryption: getBit(fmt, 6), - GroupIdentity: getBit(fmt, 5), - }, nil -} - -func readID3v24FrameFlags(r io.Reader) (*id3v2FrameFlags, error) { - b, err := readBytes(r, 2) - if err != nil { - return nil, err - } - - msg := b[0] - fmt := b[1] - - return &id3v2FrameFlags{ - TagAlterPreservation: getBit(msg, 6), - FileAlterPreservation: getBit(msg, 5), - ReadOnly: getBit(msg, 4), - GroupIdentity: getBit(fmt, 6), - Compression: getBit(fmt, 3), - Encryption: getBit(fmt, 2), - Unsynchronisation: getBit(fmt, 1), - DataLengthIndicator: getBit(fmt, 0), - }, nil - -} - -func readID3v2_2FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 3) - if err != nil { - return - } - size, err = readInt(r, 3) - if err != nil { - return - } - headerSize = 6 - return -} - -func readID3v2_3FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 4) - if err != nil { - return - } - size, err = readInt(r, 4) - if err != nil { - return - } - headerSize = 8 - return -} - -func readID3v2_4FrameHeader(r io.Reader) (name string, size int, headerSize int, err error) { - name, err = readString(r, 4) - if err != nil { - return - } - size, err = read7BitChunkedInt(r, 4) - if err != nil { - return - } - headerSize = 8 - return -} - -// readID3v2Frames reads ID3v2 frames from the given reader using the ID3v2Header. -func readID3v2Frames(r io.Reader, offset int, h *id3v2Header) (map[string]interface{}, error) { - result := make(map[string]interface{}) - - for offset < h.Size { - var err error - var name string - var size, headerSize int - var flags *id3v2FrameFlags - - switch h.Version { - case ID3v2_2: - name, size, headerSize, err = readID3v2_2FrameHeader(r) - - case ID3v2_3: - name, size, headerSize, err = readID3v2_3FrameHeader(r) - if err != nil { - return nil, err - } - flags, err = readID3v23FrameFlags(r) - headerSize += 2 - - case ID3v2_4: - name, size, headerSize, err = readID3v2_4FrameHeader(r) - if err != nil { - return nil, err - } - flags, err = readID3v24FrameFlags(r) - headerSize += 2 - } - - if err != nil { - return nil, err - } - - // FIXME: Do we still need this? - // if size=0, we certainly are in a padding zone. ignore the rest of - // the tags - if size == 0 { - break - } - - offset += headerSize + size - - // Avoid corrupted padding (see http://id3.org/Compliance%20Issues). - if !validID3Frame(h.Version, name) && offset > h.Size { - break - } - - if flags != nil { - if flags.Compression { - _, err = read7BitChunkedInt(r, 4) // read 4 - if err != nil { - return nil, err - } - size -= 4 - } - - if flags.Encryption { - _, err = readBytes(r, 1) // read 1 byte of encryption method - if err != nil { - return nil, err - } - size -= 1 - } - } - - b, err := readBytes(r, size) - if err != nil { - return nil, err - } - - // There can be multiple tag with the same name. Append a number to the - // name if there is more than one. - rawName := name - if _, ok := result[rawName]; ok { - for i := 0; ok; i++ { - rawName = name + "_" + strconv.Itoa(i) - _, ok = result[rawName] - } - } - - switch { - case name == "TXXX" || name == "TXX": - t, err := readTextWithDescrFrame(b, false, true) // no lang, but enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name[0] == 'T': - txt, err := readTFrame(b) - if err != nil { - return nil, err - } - result[rawName] = txt - - case name == "UFID" || name == "UFI": - t, err := readUFID(b) - if err != nil { - return nil, err - } - result[rawName] = t - - case name == "WXXX" || name == "WXX": - t, err := readTextWithDescrFrame(b, false, false) // no lang, no enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name[0] == 'W': - txt, err := readWFrame(b) - if err != nil { - return nil, err - } - result[rawName] = txt - - case name == "COMM" || name == "COM" || name == "USLT" || name == "ULT": - t, err := readTextWithDescrFrame(b, true, true) // both lang and enc - if err != nil { - return nil, err - } - result[rawName] = t - - case name == "APIC": - p, err := readAPICFrame(b) - if err != nil { - return nil, err - } - result[rawName] = p - - case name == "PIC": - p, err := readPICFrame(b) - if err != nil { - return nil, err - } - result[rawName] = p - - default: - result[rawName] = b - } - } - return result, nil -} - -type unsynchroniser struct { - io.Reader - ff bool -} - -// filter io.Reader which skip the Unsynchronisation bytes -func (r *unsynchroniser) Read(p []byte) (int, error) { - b := make([]byte, 1) - i := 0 - for i < len(p) { - if n, err := r.Reader.Read(b); err != nil || n == 0 { - return i, err - } - if r.ff && b[0] == 0x00 { - r.ff = false - continue - } - p[i] = b[0] - i++ - r.ff = (b[0] == 0xFF) - } - return i, nil -} - -// ReadID3v2Tags parses ID3v2.{2,3,4} tags from the io.ReadSeeker into a Metadata, returning -// non-nil error on failure. -func ReadID3v2Tags(r io.ReadSeeker) (Metadata, error) { - h, offset, err := readID3v2Header(r) - if err != nil { - return nil, err - } - - var ur io.Reader = r - if h.Unsynchronisation { - ur = &unsynchroniser{Reader: r} - } - - f, err := readID3v2Frames(ur, offset, h) - if err != nil { - return nil, err - } - return metadataID3v2{header: h, frames: f}, nil -} - -var id3v2genreRe = regexp.MustCompile(`(.*[^(]|.* |^)\(([0-9]+)\) *(.*)$`) - -// id3v2genre parse a id3v2 genre tag and expand the numeric genres -func id3v2genre(genre string) string { - c := true - for c { - orig := genre - if match := id3v2genreRe.FindStringSubmatch(genre); len(match) > 0 { - if genreID, err := strconv.Atoi(match[2]); err == nil { - if genreID < len(id3v2Genres) { - genre = id3v2Genres[genreID] - if match[1] != "" { - genre = strings.TrimSpace(match[1]) + " " + genre - } - if match[3] != "" { - genre = genre + " " + match[3] - } - } - } - } - c = (orig != genre) - } - return strings.Replace(genre, "((", "(", -1) -} diff --git a/vendor/github.com/dhowden/tag/id3v2frames.go b/vendor/github.com/dhowden/tag/id3v2frames.go deleted file mode 100644 index a92afa1..0000000 --- a/vendor/github.com/dhowden/tag/id3v2frames.go +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "strings" - "unicode/utf16" -) - -// DefaultUTF16WithBOMByteOrder is the byte order used when the "UTF16 with BOM" encoding -// is specified without a corresponding BOM in the data. -var DefaultUTF16WithBOMByteOrder binary.ByteOrder = binary.LittleEndian - -// ID3v2.2.0 frames (see http://id3.org/id3v2-00, sec 4). -var id3v22Frames = map[string]string{ - "BUF": "Recommended buffer size", - - "CNT": "Play counter", - "COM": "Comments", - "CRA": "Audio encryption", - "CRM": "Encrypted meta frame", - - "ETC": "Event timing codes", - "EQU": "Equalization", - - "GEO": "General encapsulated object", - - "IPL": "Involved people list", - - "LNK": "Linked information", - - "MCI": "Music CD Identifier", - "MLL": "MPEG location lookup table", - - "PIC": "Attached picture", - "POP": "Popularimeter", - - "REV": "Reverb", - "RVA": "Relative volume adjustment", - - "SLT": "Synchronized lyric/text", - "STC": "Synced tempo codes", - - "TAL": "Album/Movie/Show title", - "TBP": "BPM (Beats Per Minute)", - "TCM": "Composer", - "TCO": "Content type", - "TCR": "Copyright message", - "TDA": "Date", - "TDY": "Playlist delay", - "TEN": "Encoded by", - "TFT": "File type", - "TIM": "Time", - "TKE": "Initial key", - "TLA": "Language(s)", - "TLE": "Length", - "TMT": "Media type", - "TOA": "Original artist(s)/performer(s)", - "TOF": "Original filename", - "TOL": "Original Lyricist(s)/text writer(s)", - "TOR": "Original release year", - "TOT": "Original album/Movie/Show title", - "TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group", - "TP2": "Band/Orchestra/Accompaniment", - "TP3": "Conductor/Performer refinement", - "TP4": "Interpreted, remixed, or otherwise modified by", - "TPA": "Part of a set", - "TPB": "Publisher", - "TRC": "ISRC (International Standard Recording Code)", - "TRD": "Recording dates", - "TRK": "Track number/Position in set", - "TSI": "Size", - "TSS": "Software/hardware and settings used for encoding", - "TT1": "Content group description", - "TT2": "Title/Songname/Content description", - "TT3": "Subtitle/Description refinement", - "TXT": "Lyricist/text writer", - "TXX": "User defined text information frame", - "TYE": "Year", - - "UFI": "Unique file identifier", - "ULT": "Unsychronized lyric/text transcription", - - "WAF": "Official audio file webpage", - "WAR": "Official artist/performer webpage", - "WAS": "Official audio source webpage", - "WCM": "Commercial information", - "WCP": "Copyright/Legal information", - "WPB": "Publishers official webpage", - "WXX": "User defined URL link frame", -} - -// ID3v2.3.0 frames (see http://id3.org/id3v2.3.0#Declared_ID3v2_frames). -var id3v23Frames = map[string]string{ - "AENC": "Audio encryption]", - "APIC": "Attached picture", - "COMM": "Comments", - "COMR": "Commercial frame", - "ENCR": "Encryption method registration", - "EQUA": "Equalization", - "ETCO": "Event timing codes", - "GEOB": "General encapsulated object", - "GRID": "Group identification registration", - "IPLS": "Involved people list", - "LINK": "Linked information", - "MCDI": "Music CD identifier", - "MLLT": "MPEG location lookup table", - "OWNE": "Ownership frame", - "PRIV": "Private frame", - "PCNT": "Play counter", - "POPM": "Popularimeter", - "POSS": "Position synchronisation frame", - "RBUF": "Recommended buffer size", - "RVAD": "Relative volume adjustment", - "RVRB": "Reverb", - "SYLT": "Synchronized lyric/text", - "SYTC": "Synchronized tempo codes", - "TALB": "Album/Movie/Show title", - "TBPM": "BPM (beats per minute)", - "TCMP": "iTunes Compilation Flag", - "TCOM": "Composer", - "TCON": "Content type", - "TCOP": "Copyright message", - "TDAT": "Date", - "TDLY": "Playlist delay", - "TENC": "Encoded by", - "TEXT": "Lyricist/Text writer", - "TFLT": "File type", - "TIME": "Time", - "TIT1": "Content group description", - "TIT2": "Title/songname/content description", - "TIT3": "Subtitle/Description refinement", - "TKEY": "Initial key", - "TLAN": "Language(s)", - "TLEN": "Length", - "TMED": "Media type", - "TOAL": "Original album/movie/show title", - "TOFN": "Original filename", - "TOLY": "Original lyricist(s)/text writer(s)", - "TOPE": "Original artist(s)/performer(s)", - "TORY": "Original release year", - "TOWN": "File owner/licensee", - "TPE1": "Lead performer(s)/Soloist(s)", - "TPE2": "Band/orchestra/accompaniment", - "TPE3": "Conductor/performer refinement", - "TPE4": "Interpreted, remixed, or otherwise modified by", - "TPOS": "Part of a set", - "TPUB": "Publisher", - "TRCK": "Track number/Position in set", - "TRDA": "Recording dates", - "TRSN": "Internet radio station name", - "TRSO": "Internet radio station owner", - "TSIZ": "Size", - "TSO2": "iTunes uses this for Album Artist sort order", - "TSOC": "iTunes uses this for Composer sort order", - "TSRC": "ISRC (international standard recording code)", - "TSSE": "Software/Hardware and settings used for encoding", - "TYER": "Year", - "TXXX": "User defined text information frame", - "UFID": "Unique file identifier", - "USER": "Terms of use", - "USLT": "Unsychronized lyric/text transcription", - "WCOM": "Commercial information", - "WCOP": "Copyright/Legal information", - "WOAF": "Official audio file webpage", - "WOAR": "Official artist/performer webpage", - "WOAS": "Official audio source webpage", - "WORS": "Official internet radio station homepage", - "WPAY": "Payment", - "WPUB": "Publishers official webpage", - "WXXX": "User defined URL link frame", -} - -// ID3v2.4.0 frames (see http://id3.org/id3v2.4.0-frames, sec 4). -var id3v24Frames = map[string]string{ - "AENC": "Audio encryption", - "APIC": "Attached picture", - "ASPI": "Audio seek point index", - - "COMM": "Comments", - "COMR": "Commercial frame", - - "ENCR": "Encryption method registration", - "EQU2": "Equalisation (2)", - "ETCO": "Event timing codes", - - "GEOB": "General encapsulated object", - "GRID": "Group identification registration", - - "LINK": "Linked information", - - "MCDI": "Music CD identifier", - "MLLT": "MPEG location lookup table", - - "OWNE": "Ownership frame", - - "PRIV": "Private frame", - "PCNT": "Play counter", - "POPM": "Popularimeter", - "POSS": "Position synchronisation frame", - - "RBUF": "Recommended buffer size", - "RVA2": "Relative volume adjustment (2)", - "RVRB": "Reverb", - - "SEEK": "Seek frame", - "SIGN": "Signature frame", - "SYLT": "Synchronised lyric/text", - "SYTC": "Synchronised tempo codes", - - "TALB": "Album/Movie/Show title", - "TBPM": "BPM (beats per minute)", - "TCMP": "iTunes Compilation Flag", - "TCOM": "Composer", - "TCON": "Content type", - "TCOP": "Copyright message", - "TDEN": "Encoding time", - "TDLY": "Playlist delay", - "TDOR": "Original release time", - "TDRC": "Recording time", - "TDRL": "Release time", - "TDTG": "Tagging time", - "TENC": "Encoded by", - "TEXT": "Lyricist/Text writer", - "TFLT": "File type", - "TIPL": "Involved people list", - "TIT1": "Content group description", - "TIT2": "Title/songname/content description", - "TIT3": "Subtitle/Description refinement", - "TKEY": "Initial key", - "TLAN": "Language(s)", - "TLEN": "Length", - "TMCL": "Musician credits list", - "TMED": "Media type", - "TMOO": "Mood", - "TOAL": "Original album/movie/show title", - "TOFN": "Original filename", - "TOLY": "Original lyricist(s)/text writer(s)", - "TOPE": "Original artist(s)/performer(s)", - "TOWN": "File owner/licensee", - "TPE1": "Lead performer(s)/Soloist(s)", - "TPE2": "Band/orchestra/accompaniment", - "TPE3": "Conductor/performer refinement", - "TPE4": "Interpreted, remixed, or otherwise modified by", - "TPOS": "Part of a set", - "TPRO": "Produced notice", - "TPUB": "Publisher", - "TRCK": "Track number/Position in set", - "TRSN": "Internet radio station name", - "TRSO": "Internet radio station owner", - "TSO2": "iTunes uses this for Album Artist sort order", - "TSOA": "Album sort order", - "TSOC": "iTunes uses this for Composer sort order", - "TSOP": "Performer sort order", - "TSOT": "Title sort order", - "TSRC": "ISRC (international standard recording code)", - "TSSE": "Software/Hardware and settings used for encoding", - "TSST": "Set subtitle", - "TXXX": "User defined text information frame", - - "UFID": "Unique file identifier", - "USER": "Terms of use", - "USLT": "Unsynchronised lyric/text transcription", - - "WCOM": "Commercial information", - "WCOP": "Copyright/Legal information", - "WOAF": "Official audio file webpage", - "WOAR": "Official artist/performer webpage", - "WOAS": "Official audio source webpage", - "WORS": "Official Internet radio station homepage", - "WPAY": "Payment", - "WPUB": "Publishers official webpage", - "WXXX": "User defined URL link frame", -} - -// ID3 frames that are defined in the specs. -var id3Frames = map[Format]map[string]string{ - ID3v2_2: id3v22Frames, - ID3v2_3: id3v23Frames, - ID3v2_4: id3v24Frames, -} - -func validID3Frame(version Format, name string) bool { - names, ok := id3Frames[version] - if !ok { - return false - } - _, ok = names[name] - return ok -} - -func readWFrame(b []byte) (string, error) { - // Frame text is always encoded in ISO-8859-1 - b = append([]byte{0}, b...) - return readTFrame(b) -} - -func readTFrame(b []byte) (string, error) { - if len(b) == 0 { - return "", nil - } - - txt, err := decodeText(b[0], b[1:]) - if err != nil { - return "", err - } - return strings.Join(strings.Split(txt, string(singleZero)), ""), nil -} - -const ( - encodingISO8859 byte = 0 - encodingUTF16WithBOM byte = 1 - encodingUTF16 byte = 2 - encodingUTF8 byte = 3 -) - -func decodeText(enc byte, b []byte) (string, error) { - if len(b) == 0 { - return "", nil - } - - switch enc { - case encodingISO8859: // ISO-8859-1 - return decodeISO8859(b), nil - - case encodingUTF16WithBOM: // UTF-16 with byte order marker - if len(b) == 1 { - return "", nil - } - return decodeUTF16WithBOM(b) - - case encodingUTF16: // UTF-16 without byte order (assuming BigEndian) - if len(b) == 1 { - return "", nil - } - return decodeUTF16(b, binary.BigEndian) - - case encodingUTF8: // UTF-8 - return string(b), nil - - default: // Fallback to ISO-8859-1 - return decodeISO8859(b), nil - } -} - -var ( - singleZero = []byte{0} - doubleZero = []byte{0, 0} -) - -func dataSplit(b []byte, enc byte) [][]byte { - delim := singleZero - if enc == encodingUTF16 || enc == encodingUTF16WithBOM { - delim = doubleZero - } - - result := bytes.SplitN(b, delim, 2) - if len(result) != 2 { - return result - } - - if len(result[1]) == 0 { - return result - } - - if result[1][0] == 0 { - // there was a double (or triple) 0 and we cut too early - result[0] = append(result[0], result[1][0]) - result[1] = result[1][1:] - } - return result -} - -func decodeISO8859(b []byte) string { - r := make([]rune, len(b)) - for i, x := range b { - r[i] = rune(x) - } - return string(r) -} - -func decodeUTF16WithBOM(b []byte) (string, error) { - if len(b) < 2 { - return "", errors.New("invalid encoding: expected at least 2 bytes for UTF-16 byte order mark") - } - - var bo binary.ByteOrder - switch { - case b[0] == 0xFE && b[1] == 0xFF: - bo = binary.BigEndian - b = b[2:] - - case b[0] == 0xFF && b[1] == 0xFE: - bo = binary.LittleEndian - b = b[2:] - - default: - bo = DefaultUTF16WithBOMByteOrder - } - return decodeUTF16(b, bo) -} - -func decodeUTF16(b []byte, bo binary.ByteOrder) (string, error) { - if len(b)%2 != 0 { - return "", errors.New("invalid encoding: expected even number of bytes for UTF-16 encoded text") - } - s := make([]uint16, 0, len(b)/2) - for i := 0; i < len(b); i += 2 { - s = append(s, bo.Uint16(b[i:i+2])) - } - return string(utf16.Decode(s)), nil -} - -// Comm is a type used in COMM, UFID, TXXX, WXXX and USLT tag. -// It's a text with a description and a specified language -// For WXXX, TXXX and UFID, we don't set a Language -type Comm struct { - Language string - Description string - Text string -} - -// String returns a string representation of the underlying Comm instance. -func (t Comm) String() string { - if t.Language != "" { - return fmt.Sprintf("Text{Lang: '%v', Description: '%v', %v lines}", - t.Language, t.Description, strings.Count(t.Text, "\n")) - } - return fmt.Sprintf("Text{Description: '%v', %v}", t.Description, t.Text) -} - -// IDv2.{3,4} -// -- Header -//
-//
-// -- readTextWithDescrFrame(data, true, true) -// Text encoding $xx -// Language $xx xx xx -// Content descriptor $00 (00) -// Lyrics/text -// -- Header -//
-//
-// -- readTextWithDescrFrame(data, false, ) -// Text encoding $xx -// Description $00 (00) -// Value -func readTextWithDescrFrame(b []byte, hasLang bool, encoded bool) (*Comm, error) { - enc := b[0] - b = b[1:] - - c := &Comm{} - if hasLang { - c.Language = string(b[:3]) - b = b[3:] - } - - descTextSplit := dataSplit(b, enc) - if len(descTextSplit) < 1 { - return nil, fmt.Errorf("error decoding tag description text: invalid encoding") - } - - desc, err := decodeText(enc, descTextSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding tag description text: %v", err) - } - c.Description = desc - - if len(descTextSplit) == 1 { - return c, nil - } - - if !encoded { - enc = byte(0) - } - text, err := decodeText(enc, descTextSplit[1]) - if err != nil { - return nil, fmt.Errorf("error decoding tag text: %v", err) - } - c.Text = text - - return c, nil -} - -// UFID is composed of a provider (frequently a URL and a binary identifier) -// The identifier can be a text (Musicbrainz use texts, but not necessary) -type UFID struct { - Provider string - Identifier []byte -} - -func (u UFID) String() string { - return fmt.Sprintf("%v (%v)", u.Provider, string(u.Identifier)) -} - -func readUFID(b []byte) (*UFID, error) { - result := bytes.SplitN(b, singleZero, 2) - if len(result) != 2 { - return nil, errors.New("expected to split UFID data into 2 pieces") - } - - return &UFID{ - Provider: string(result[0]), - Identifier: result[1], - }, nil -} - -var pictureTypes = map[byte]string{ - 0x00: "Other", - 0x01: "32x32 pixels 'file icon' (PNG only)", - 0x02: "Other file icon", - 0x03: "Cover (front)", - 0x04: "Cover (back)", - 0x05: "Leaflet page", - 0x06: "Media (e.g. lable side of CD)", - 0x07: "Lead artist/lead performer/soloist", - 0x08: "Artist/performer", - 0x09: "Conductor", - 0x0A: "Band/Orchestra", - 0x0B: "Composer", - 0x0C: "Lyricist/text writer", - 0x0D: "Recording Location", - 0x0E: "During recording", - 0x0F: "During performance", - 0x10: "Movie/video screen capture", - 0x11: "A bright coloured fish", - 0x12: "Illustration", - 0x13: "Band/artist logotype", - 0x14: "Publisher/Studio logotype", -} - -// Picture is a type which represents an attached picture extracted from metadata. -type Picture struct { - Ext string // Extension of the picture file. - MIMEType string // MIMEType of the picture. - Type string // Type of the picture (see pictureTypes). - Description string // Description. - Data []byte // Raw picture data. -} - -// String returns a string representation of the underlying Picture instance. -func (p Picture) String() string { - return fmt.Sprintf("Picture{Ext: %v, MIMEType: %v, Type: %v, Description: %v, Data.Size: %v}", - p.Ext, p.MIMEType, p.Type, p.Description, len(p.Data)) -} - -// IDv2.2 -// -- Header -// Attached picture "PIC" -// Frame size $xx xx xx -// -- readPICFrame -// Text encoding $xx -// Image format $xx xx xx -// Picture type $xx -// Description $00 (00) -// Picture data -func readPICFrame(b []byte) (*Picture, error) { - enc := b[0] - ext := string(b[1:4]) - picType := b[4] - - descDataSplit := dataSplit(b[5:], enc) - if len(descDataSplit) != 2 { - return nil, errors.New("error decoding PIC description text: invalid encoding") - } - desc, err := decodeText(enc, descDataSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding PIC description text: %v", err) - } - - var mimeType string - switch ext { - case "jpeg", "jpg": - mimeType = "image/jpeg" - case "png": - mimeType = "image/png" - } - - return &Picture{ - Ext: ext, - MIMEType: mimeType, - Type: pictureTypes[picType], - Description: desc, - Data: descDataSplit[1], - }, nil -} - -// IDv2.{3,4} -// -- Header -//
-// -- readAPICFrame -// Text encoding $xx -// MIME type $00 -// Picture type $xx -// Description $00 (00) -// Picture data -func readAPICFrame(b []byte) (*Picture, error) { - enc := b[0] - mimeDataSplit := bytes.SplitN(b[1:], singleZero, 2) - mimeType := string(mimeDataSplit[0]) - - b = mimeDataSplit[1] - if len(b) < 1 { - return nil, fmt.Errorf("error decoding APIC mimetype") - } - picType := b[0] - - descDataSplit := dataSplit(b[1:], enc) - if len(descDataSplit) != 2 { - return nil, errors.New("error decoding APIC description text: invalid encoding") - } - desc, err := decodeText(enc, descDataSplit[0]) - if err != nil { - return nil, fmt.Errorf("error decoding APIC description text: %v", err) - } - - var ext string - switch mimeType { - case "image/jpeg": - ext = "jpg" - case "image/png": - ext = "png" - } - - return &Picture{ - Ext: ext, - MIMEType: mimeType, - Type: pictureTypes[picType], - Description: desc, - Data: descDataSplit[1], - }, nil -} diff --git a/vendor/github.com/dhowden/tag/id3v2metadata.go b/vendor/github.com/dhowden/tag/id3v2metadata.go deleted file mode 100644 index 6185963..0000000 --- a/vendor/github.com/dhowden/tag/id3v2metadata.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "strconv" - "strings" -) - -type frameNames map[string][2]string - -func (f frameNames) Name(s string, fm Format) string { - l, ok := f[s] - if !ok { - return "" - } - - switch fm { - case ID3v2_2: - return l[0] - case ID3v2_3: - return l[1] - case ID3v2_4: - if s == "year" { - return "TDRC" - } - return l[1] - } - return "" -} - -var frames = frameNames(map[string][2]string{ - "title": [2]string{"TT2", "TIT2"}, - "artist": [2]string{"TP1", "TPE1"}, - "album": [2]string{"TAL", "TALB"}, - "album_artist": [2]string{"TP2", "TPE2"}, - "composer": [2]string{"TCM", "TCOM"}, - "year": [2]string{"TYE", "TYER"}, - "track": [2]string{"TRK", "TRCK"}, - "disc": [2]string{"TPA", "TPOS"}, - "genre": [2]string{"TCO", "TCON"}, - "picture": [2]string{"PIC", "APIC"}, - "lyrics": [2]string{"", "USLT"}, - "comment": [2]string{"COM", "COMM"}, -}) - -// metadataID3v2 is the implementation of Metadata used for ID3v2 tags. -type metadataID3v2 struct { - header *id3v2Header - frames map[string]interface{} -} - -func (m metadataID3v2) getString(k string) string { - v, ok := m.frames[k] - if !ok { - return "" - } - return v.(string) -} - -func (m metadataID3v2) Format() Format { return m.header.Version } -func (m metadataID3v2) FileType() FileType { return MP3 } -func (m metadataID3v2) Raw() map[string]interface{} { return m.frames } - -func (m metadataID3v2) Title() string { - return m.getString(frames.Name("title", m.Format())) -} - -func (m metadataID3v2) Artist() string { - return m.getString(frames.Name("artist", m.Format())) -} - -func (m metadataID3v2) Album() string { - return m.getString(frames.Name("album", m.Format())) -} - -func (m metadataID3v2) AlbumArtist() string { - return m.getString(frames.Name("album_artist", m.Format())) -} - -func (m metadataID3v2) Composer() string { - return m.getString(frames.Name("composer", m.Format())) -} - -func (m metadataID3v2) Genre() string { - return id3v2genre(m.getString(frames.Name("genre", m.Format()))) -} - -func (m metadataID3v2) Year() int { - year, _ := strconv.Atoi(m.getString(frames.Name("year", m.Format()))) - return year -} - -func parseXofN(s string) (x, n int) { - xn := strings.Split(s, "/") - if len(xn) != 2 { - x, _ = strconv.Atoi(s) - return x, 0 - } - x, _ = strconv.Atoi(strings.TrimSpace(xn[0])) - n, _ = strconv.Atoi(strings.TrimSpace(xn[1])) - return x, n -} - -func (m metadataID3v2) Track() (int, int) { - return parseXofN(m.getString(frames.Name("track", m.Format()))) -} - -func (m metadataID3v2) Disc() (int, int) { - return parseXofN(m.getString(frames.Name("disc", m.Format()))) -} - -func (m metadataID3v2) Lyrics() string { - t, ok := m.frames[frames.Name("lyrics", m.Format())] - if !ok { - return "" - } - return t.(*Comm).Text -} - -func (m metadataID3v2) Comment() string { - t, ok := m.frames[frames.Name("comment", m.Format())] - if !ok { - return "" - } - // id3v23 has Text, id3v24 has Description - if t.(*Comm).Description == "" { - return trimString(t.(*Comm).Text) - } - return trimString(t.(*Comm).Description) -} - -func (m metadataID3v2) Picture() *Picture { - v, ok := m.frames[frames.Name("picture", m.Format())] - if !ok { - return nil - } - return v.(*Picture) -} diff --git a/vendor/github.com/dhowden/tag/mp4.go b/vendor/github.com/dhowden/tag/mp4.go deleted file mode 100644 index 3970d91..0000000 --- a/vendor/github.com/dhowden/tag/mp4.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "strconv" -) - -var atomTypes = map[int]string{ - 0: "implicit", // automatic based on atom name - 1: "text", - 13: "jpeg", - 14: "png", - 21: "uint8", -} - -// NB: atoms does not include "----", this is handled separately -var atoms = atomNames(map[string]string{ - "\xa9alb": "album", - "\xa9art": "artist", - "\xa9ART": "artist", - "aART": "album_artist", - "\xa9day": "year", - "\xa9nam": "title", - "\xa9gen": "genre", - "trkn": "track", - "\xa9wrt": "composer", - "\xa9too": "encoder", - "cprt": "copyright", - "covr": "picture", - "\xa9grp": "grouping", - "keyw": "keyword", - "\xa9lyr": "lyrics", - "\xa9cmt": "comment", - "tmpo": "tempo", - "cpil": "compilation", - "disk": "disc", -}) - -// Detect PNG image if "implicit" class is used -var pngHeader = []byte{137, 80, 78, 71, 13, 10, 26, 10} - -type atomNames map[string]string - -func (f atomNames) Name(n string) []string { - res := make([]string, 1) - for k, v := range f { - if v == n { - res = append(res, k) - } - } - return res -} - -// metadataMP4 is the implementation of Metadata for MP4 tag (atom) data. -type metadataMP4 struct { - fileType FileType - data map[string]interface{} -} - -// ReadAtoms reads MP4 metadata atoms from the io.ReadSeeker into a Metadata, returning -// non-nil error if there was a problem. -func ReadAtoms(r io.ReadSeeker) (Metadata, error) { - m := metadataMP4{ - data: make(map[string]interface{}), - fileType: UnknownFileType, - } - err := m.readAtoms(r) - return m, err -} - -func (m metadataMP4) readAtoms(r io.ReadSeeker) error { - for { - name, size, err := readAtomHeader(r) - if err != nil { - if err == io.EOF { - return nil - } - return err - } - - switch name { - case "meta": - // next_item_id (int32) - _, err := readBytes(r, 4) - if err != nil { - return err - } - fallthrough - - case "moov", "udta", "ilst": - return m.readAtoms(r) - } - - _, ok := atoms[name] - if name == "----" { - name, size, err = readCustomAtom(r, size) - if err != nil { - return err - } - - if name != "----" { - ok = true - } - } - - if !ok { - _, err := r.Seek(int64(size-8), io.SeekCurrent) - if err != nil { - return err - } - continue - } - - err = m.readAtomData(r, name, size-8) - if err != nil { - return err - } - } -} - -func (m metadataMP4) readAtomData(r io.ReadSeeker, name string, size uint32) error { - b, err := readBytes(r, int(size)) - if err != nil { - return err - } - - if len(b) < 8 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, got %d", 8, len(b)) - } - - // "data" + size (4 bytes each) - b = b[8:] - - if len(b) < 3 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for class, got %d", 3, len(b)) - } - class := getInt(b[1:4]) - contentType, ok := atomTypes[class] - if !ok { - return fmt.Errorf("invalid content type: %v (%x) (%x)", class, b[1:4], b) - } - - // 4: atom version (1 byte) + atom flags (3 bytes) - // 4: NULL (usually locale indicator) - if len(b) < 8 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for atom version and flags, got %d", 8, len(b)) - } - b = b[8:] - - if name == "trkn" || name == "disk" { - if len(b) < 6 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for track and disk numbers, got %d", 6, len(b)) - } - - m.data[name] = int(b[3]) - m.data[name+"_count"] = int(b[5]) - return nil - } - - if contentType == "implicit" { - if name == "covr" { - if bytes.HasPrefix(b, pngHeader) { - contentType = "png" - } - // TODO(dhowden): Detect JPEG formats too (harder). - } - } - - var data interface{} - switch contentType { - case "implicit": - if _, ok := atoms[name]; ok { - return fmt.Errorf("unhandled implicit content type for required atom: %q", name) - } - return nil - - case "text": - data = string(b) - - case "uint8": - if len(b) < 1 { - return fmt.Errorf("invalid encoding: expected at least %d bytes, for integer tag data, got %d", 1, len(b)) - } - data = getInt(b[:1]) - - case "jpeg", "png": - data = &Picture{ - Ext: contentType, - MIMEType: "image/" + contentType, - Data: b, - } - } - m.data[name] = data - - return nil -} - -func readAtomHeader(r io.ReadSeeker) (name string, size uint32, err error) { - err = binary.Read(r, binary.BigEndian, &size) - if err != nil { - return - } - name, err = readString(r, 4) - return -} - -// Generic atom. -// Should have 3 sub atoms : mean, name and data. -// We check that mean is "com.apple.iTunes" and we use the subname as -// the name, and move to the data atom. -// If anything goes wrong, we jump at the end of the "----" atom. -func readCustomAtom(r io.ReadSeeker, size uint32) (string, uint32, error) { - subNames := make(map[string]string) - var dataSize uint32 - - for size > 8 { - subName, subSize, err := readAtomHeader(r) - if err != nil { - return "", 0, err - } - - // Remove the size of the atom from the size counter - size -= subSize - - switch subName { - case "mean", "name": - b, err := readBytes(r, int(subSize-8)) - if err != nil { - return "", 0, err - } - - if len(b) < 4 { - return "", 0, fmt.Errorf("invalid encoding: expected at least %d bytes, got %d", 4, len(b)) - } - subNames[subName] = string(b[4:]) - - case "data": - // Found the "data" atom, rewind - dataSize = subSize + 8 // will need to re-read "data" + size (4 + 4) - _, err := r.Seek(-8, io.SeekCurrent) - if err != nil { - return "", 0, err - } - } - } - - // there should remain only the header size - if size != 8 { - err := errors.New("---- atom out of bounds") - return "", 0, err - } - - if subNames["mean"] != "com.apple.iTunes" || subNames["name"] == "" || dataSize == 0 { - return "----", 0, nil - } - return subNames["name"], dataSize, nil -} - -func (metadataMP4) Format() Format { return MP4 } -func (m metadataMP4) FileType() FileType { return m.fileType } - -func (m metadataMP4) Raw() map[string]interface{} { return m.data } - -func (m metadataMP4) getString(n []string) string { - for _, k := range n { - if x, ok := m.data[k]; ok { - return x.(string) - } - } - return "" -} - -func (m metadataMP4) getInt(n []string) int { - for _, k := range n { - if x, ok := m.data[k]; ok { - return x.(int) - } - } - return 0 -} - -func (m metadataMP4) Title() string { - return m.getString(atoms.Name("title")) -} - -func (m metadataMP4) Artist() string { - return m.getString(atoms.Name("artist")) -} - -func (m metadataMP4) Album() string { - return m.getString(atoms.Name("album")) -} - -func (m metadataMP4) AlbumArtist() string { - return m.getString(atoms.Name("album_artist")) -} - -func (m metadataMP4) Composer() string { - return m.getString(atoms.Name("composer")) -} - -func (m metadataMP4) Genre() string { - return m.getString(atoms.Name("genre")) -} - -func (m metadataMP4) Year() int { - date := m.getString(atoms.Name("year")) - if len(date) >= 4 { - year, _ := strconv.Atoi(date[:4]) - return year - } - return 0 -} - -func (m metadataMP4) Track() (int, int) { - x := m.getInt([]string{"trkn"}) - if n, ok := m.data["trkn_count"]; ok { - return x, n.(int) - } - return x, 0 -} - -func (m metadataMP4) Disc() (int, int) { - x := m.getInt([]string{"disk"}) - if n, ok := m.data["disk_count"]; ok { - return x, n.(int) - } - return x, 0 -} - -func (m metadataMP4) Lyrics() string { - t, ok := m.data["\xa9lyr"] - if !ok { - return "" - } - return t.(string) -} - -func (m metadataMP4) Comment() string { - t, ok := m.data["\xa9cmt"] - if !ok { - return "" - } - return t.(string) -} - -func (m metadataMP4) Picture() *Picture { - v, ok := m.data["covr"] - if !ok { - return nil - } - p, _ := v.(*Picture) - return p -} diff --git a/vendor/github.com/dhowden/tag/ogg.go b/vendor/github.com/dhowden/tag/ogg.go deleted file mode 100644 index bc26d4a..0000000 --- a/vendor/github.com/dhowden/tag/ogg.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "errors" - "io" -) - -const ( - idType int = 1 - commentType int = 3 -) - -// ReadOGGTags reads OGG metadata from the io.ReadSeeker, returning the resulting -// metadata in a Metadata implementation, or non-nil error if there was a problem. -// See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html -// and http://www.xiph.org/ogg/doc/framing.html for details. -func ReadOGGTags(r io.ReadSeeker) (Metadata, error) { - oggs, err := readString(r, 4) - if err != nil { - return nil, err - } - if oggs != "OggS" { - return nil, errors.New("expected 'OggS'") - } - - // Skip 22 bytes of Page header to read page_segments length byte at position 26 - // See http://www.xiph.org/ogg/doc/framing.html - _, err = r.Seek(22, io.SeekCurrent) - if err != nil { - return nil, err - } - - nS, err := readInt(r, 1) - if err != nil { - return nil, err - } - - // Seek and discard the segments - _, err = r.Seek(int64(nS), io.SeekCurrent) - if err != nil { - return nil, err - } - - // First packet type is identification, type 1 - t, err := readInt(r, 1) - if err != nil { - return nil, err - } - if t != idType { - return nil, errors.New("expected 'vorbis' identification type 1") - } - - // Seek and discard 29 bytes from common and identification header - // See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 - _, err = r.Seek(29, io.SeekCurrent) - if err != nil { - return nil, err - } - - // Beginning of a new page. Comment packet is on a separate page - // See http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2 - oggs, err = readString(r, 4) - if err != nil { - return nil, err - } - if oggs != "OggS" { - return nil, errors.New("expected 'OggS'") - } - - // Skip page 2 header, same as line 30 - _, err = r.Seek(22, io.SeekCurrent) - if err != nil { - return nil, err - } - - nS, err = readInt(r, 1) - if err != nil { - return nil, err - } - - _, err = r.Seek(int64(nS), io.SeekCurrent) - if err != nil { - return nil, err - } - - // Packet type is comment, type 3 - t, err = readInt(r, 1) - if err != nil { - return nil, err - } - if t != commentType { - return nil, errors.New("expected 'vorbis' comment type 3") - } - - // Seek and discard 6 bytes from common header - _, err = r.Seek(6, io.SeekCurrent) - if err != nil { - return nil, err - } - - m := &metadataOGG{ - newMetadataVorbis(), - } - - err = m.readVorbisComment(r) - return m, err -} - -type metadataOGG struct { - *metadataVorbis -} - -func (m *metadataOGG) FileType() FileType { - return OGG -} diff --git a/vendor/github.com/dhowden/tag/sum.go b/vendor/github.com/dhowden/tag/sum.go deleted file mode 100644 index 97b27a7..0000000 --- a/vendor/github.com/dhowden/tag/sum.go +++ /dev/null @@ -1,219 +0,0 @@ -package tag - -import ( - "crypto/sha1" - "encoding/binary" - "errors" - "fmt" - "hash" - "io" -) - -// Sum creates a checksum of the audio file data provided by the io.ReadSeeker which is metadata -// (ID3, MP4) invariant. -func Sum(r io.ReadSeeker) (string, error) { - b, err := readBytes(r, 11) - if err != nil { - return "", err - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("could not seek back to original position: %v", err) - } - - switch { - case string(b[0:4]) == "fLaC": - return SumFLAC(r) - - case string(b[4:11]) == "ftypM4A": - return SumAtoms(r) - - case string(b[0:3]) == "ID3": - return SumID3v2(r) - } - - h, err := SumID3v1(r) - if err != nil { - if err == ErrNotID3v1 { - return SumAll(r) - } - return "", err - } - return h, nil -} - -// SumAll returns a checksum of the content from the reader (until EOF). -func SumAll(r io.ReadSeeker) (string, error) { - h := sha1.New() - _, err := io.Copy(h, r) - if err != nil { - return "", nil - } - return hashSum(h), nil -} - -// SumAtoms constructs a checksum of MP4 audio file data provided by the io.ReadSeeker which is -// metadata invariant. -func SumAtoms(r io.ReadSeeker) (string, error) { - for { - var size uint32 - err := binary.Read(r, binary.BigEndian, &size) - if err != nil { - if err == io.EOF { - return "", fmt.Errorf("reached EOF before audio data") - } - return "", err - } - - name, err := readString(r, 4) - if err != nil { - return "", err - } - - switch name { - case "meta": - // next_item_id (int32) - _, err := r.Seek(4, io.SeekCurrent) - if err != nil { - return "", err - } - fallthrough - - case "moov", "udta", "ilst": - continue - - case "mdat": // stop when we get to the data - h := sha1.New() - _, err := io.CopyN(h, r, int64(size-8)) - if err != nil { - return "", fmt.Errorf("error reading audio data: %v", err) - } - return hashSum(h), nil - } - - _, err = r.Seek(int64(size-8), io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("error reading '%v' tag: %v", name, err) - } - } -} - -func sizeToEndOffset(r io.ReadSeeker, offset int64) (int64, error) { - n, err := r.Seek(-128, io.SeekEnd) - if err != nil { - return 0, fmt.Errorf("error seeking end offset (%d bytes): %v", offset, err) - } - - _, err = r.Seek(-n, io.SeekCurrent) - if err != nil { - return 0, fmt.Errorf("error seeking back to original position: %v", err) - } - return n, nil -} - -// SumID3v1 constructs a checksum of MP3 audio file data (assumed to have ID3v1 tags) provided -// by the io.ReadSeeker which is metadata invariant. -func SumID3v1(r io.ReadSeeker) (string, error) { - n, err := sizeToEndOffset(r, 128) - if err != nil { - return "", fmt.Errorf("error determining read size to ID3v1 header: %v", err) - } - - // TODO: improve this check??? - if n <= 0 { - return "", fmt.Errorf("file size must be greater than 128 bytes (ID3v1 header size) for MP3") - } - - h := sha1.New() - _, err = io.CopyN(h, r, n) - if err != nil { - return "", fmt.Errorf("error reading %v bytes: %v", n, err) - } - return hashSum(h), nil -} - -// SumID3v2 constructs a checksum of MP3 audio file data (assumed to have ID3v2 tags) provided by the -// io.ReadSeeker which is metadata invariant. -func SumID3v2(r io.ReadSeeker) (string, error) { - header, _, err := readID3v2Header(r) - if err != nil { - return "", fmt.Errorf("error reading ID3v2 header: %v", err) - } - - _, err = r.Seek(int64(header.Size), io.SeekCurrent) - if err != nil { - return "", fmt.Errorf("error seeking to end of ID3V2 header: %v", err) - } - - n, err := sizeToEndOffset(r, 128) - if err != nil { - return "", fmt.Errorf("error determining read size to ID3v1 header: %v", err) - } - - // TODO: remove this check????? - if n < 0 { - return "", fmt.Errorf("file size must be greater than 128 bytes for MP3: %v bytes", n) - } - - h := sha1.New() - _, err = io.CopyN(h, r, n) - if err != nil { - return "", fmt.Errorf("error reading %v bytes: %v", n, err) - } - return hashSum(h), nil -} - -// SumFLAC costructs a checksum of the FLAC audio file data provided by the io.ReadSeeker (ignores -// metadata fields). -func SumFLAC(r io.ReadSeeker) (string, error) { - flac, err := readString(r, 4) - if err != nil { - return "", err - } - if flac != "fLaC" { - return "", errors.New("expected 'fLaC'") - } - - for { - last, err := skipFLACMetadataBlock(r) - if err != nil { - return "", err - } - - if last { - break - } - } - - h := sha1.New() - _, err = io.Copy(h, r) - if err != nil { - return "", fmt.Errorf("error reading data bytes from FLAC: %v", err) - } - return hashSum(h), nil -} - -func skipFLACMetadataBlock(r io.ReadSeeker) (last bool, err error) { - blockHeader, err := readBytes(r, 1) - if err != nil { - return - } - - if getBit(blockHeader[0], 7) { - blockHeader[0] ^= (1 << 7) - last = true - } - - blockLen, err := readInt(r, 3) - if err != nil { - return - } - - _, err = r.Seek(int64(blockLen), io.SeekCurrent) - return -} - -func hashSum(h hash.Hash) string { - return fmt.Sprintf("%x", h.Sum([]byte{})) -} diff --git a/vendor/github.com/dhowden/tag/tag.go b/vendor/github.com/dhowden/tag/tag.go deleted file mode 100644 index 306f1d7..0000000 --- a/vendor/github.com/dhowden/tag/tag.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package tag provides MP3 (ID3: v1, 2.2, 2.3 and 2.4), MP4, FLAC and OGG metadata detection, -// parsing and artwork extraction. -// -// Detect and parse tag metadata from an io.ReadSeeker (i.e. an *os.File): -// m, err := tag.ReadFrom(f) -// if err != nil { -// log.Fatal(err) -// } -// log.Print(m.Format()) // The detected format. -// log.Print(m.Title()) // The title of the track (see Metadata interface for more details). -package tag - -import ( - "errors" - "fmt" - "io" -) - -// ErrNoTagsFound is the error returned by ReadFrom when the metadata format -// cannot be identified. -var ErrNoTagsFound = errors.New("no tags found") - -// ReadFrom detects and parses audio file metadata tags (currently supports ID3v1,2.{2,3,4}, MP4, FLAC/OGG). -// Returns non-nil error if the format of the given data could not be determined, or if there was a problem -// parsing the data. -func ReadFrom(r io.ReadSeeker) (Metadata, error) { - b, err := readBytes(r, 11) - if err != nil { - return nil, err - } - - _, err = r.Seek(-11, io.SeekCurrent) - if err != nil { - return nil, fmt.Errorf("could not seek back to original position: %v", err) - } - - switch { - case string(b[0:4]) == "fLaC": - return ReadFLACTags(r) - - case string(b[0:4]) == "OggS": - return ReadOGGTags(r) - - case string(b[4:8]) == "ftyp": - return ReadAtoms(r) - - case string(b[0:3]) == "ID3": - return ReadID3v2Tags(r) - - case string(b[0:4]) == "DSD ": - return ReadDSFTags(r) - } - - m, err := ReadID3v1Tags(r) - if err != nil { - if err == ErrNotID3v1 { - err = ErrNoTagsFound - } - return nil, err - } - return m, nil -} - -// Format is an enumeration of metadata types supported by this package. -type Format string - -// Supported tag formats. -const ( - UnknownFormat Format = "" // Unknown Format. - ID3v1 Format = "ID3v1" // ID3v1 tag format. - ID3v2_2 Format = "ID3v2.2" // ID3v2.2 tag format. - ID3v2_3 Format = "ID3v2.3" // ID3v2.3 tag format (most common). - ID3v2_4 Format = "ID3v2.4" // ID3v2.4 tag format. - MP4 Format = "MP4" // MP4 tag (atom) format (see http://www.ftyps.com/ for a full file type list) - VORBIS Format = "VORBIS" // Vorbis Comment tag format. -) - -// FileType is an enumeration of the audio file types supported by this package, in particular -// there are audio file types which share metadata formats, and this type is used to distinguish -// between them. -type FileType string - -// Supported file types. -const ( - UnknownFileType FileType = "" // Unknown FileType. - MP3 FileType = "MP3" // MP3 file - M4A FileType = "M4A" // M4A file Apple iTunes (ACC) Audio - M4B FileType = "M4B" // M4A file Apple iTunes (ACC) Audio Book - M4P FileType = "M4P" // M4A file Apple iTunes (ACC) AES Protected Audio - ALAC FileType = "ALAC" // Apple Lossless file FIXME: actually detect this - FLAC FileType = "FLAC" // FLAC file - OGG FileType = "OGG" // OGG file - DSF FileType = "DSF" // DSF file DSD Sony format see https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf -) - -// Metadata is an interface which is used to describe metadata retrieved by this package. -type Metadata interface { - // Format returns the metadata Format used to encode the data. - Format() Format - - // FileType returns the file type of the audio file. - FileType() FileType - - // Title returns the title of the track. - Title() string - - // Album returns the album name of the track. - Album() string - - // Artist returns the artist name of the track. - Artist() string - - // AlbumArtist returns the album artist name of the track. - AlbumArtist() string - - // Composer returns the composer of the track. - Composer() string - - // Year returns the year of the track. - Year() int - - // Genre returns the genre of the track. - Genre() string - - // Track returns the track number and total tracks, or zero values if unavailable. - Track() (int, int) - - // Disc returns the disc number and total discs, or zero values if unavailable. - Disc() (int, int) - - // Picture returns a picture, or nil if not available. - Picture() *Picture - - // Lyrics returns the lyrics, or an empty string if unavailable. - Lyrics() string - - // Comment returns the comment, or an empty string if unavailable. - Comment() string - - // Raw returns the raw mapping of retrieved tag names and associated values. - // NB: tag/atom names are not standardised between formats. - Raw() map[string]interface{} -} diff --git a/vendor/github.com/dhowden/tag/util.go b/vendor/github.com/dhowden/tag/util.go deleted file mode 100644 index ff9c4f1..0000000 --- a/vendor/github.com/dhowden/tag/util.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "encoding/binary" - "io" -) - -func getBit(b byte, n uint) bool { - x := byte(1 << n) - return (b & x) == x -} - -func get7BitChunkedInt(b []byte) int { - var n int - for _, x := range b { - n = n << 7 - n |= int(x) - } - return n -} - -func getInt(b []byte) int { - var n int - for _, x := range b { - n = n << 8 - n |= int(x) - } - return n -} - -func getIntLittleEndian(b []byte) int { - var n int - for i := len(b) - 1; i >= 0; i-- { - n = n << 8 - n |= int(b[i]) - } - return n -} - -func readBytes(r io.Reader, n int) ([]byte, error) { - b := make([]byte, n) - _, err := io.ReadFull(r, b) - if err != nil { - return nil, err - } - return b, nil -} - -func readString(r io.Reader, n int) (string, error) { - b, err := readBytes(r, n) - if err != nil { - return "", err - } - return string(b), nil -} - -func readInt(r io.Reader, n int) (int, error) { - b, err := readBytes(r, n) - if err != nil { - return 0, err - } - return getInt(b), nil -} - -func read7BitChunkedInt(r io.Reader, n int) (int, error) { - b, err := readBytes(r, n) - if err != nil { - return 0, err - } - return get7BitChunkedInt(b), nil -} - -func readInt32LittleEndian(r io.Reader) (int, error) { - var n int32 - err := binary.Read(r, binary.LittleEndian, &n) - return int(n), err -} diff --git a/vendor/github.com/dhowden/tag/vorbis.go b/vendor/github.com/dhowden/tag/vorbis.go deleted file mode 100644 index 9f5ecb8..0000000 --- a/vendor/github.com/dhowden/tag/vorbis.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2015, David Howden -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tag - -import ( - "errors" - "fmt" - "io" - "strconv" - "strings" - "time" -) - -func newMetadataVorbis() *metadataVorbis { - return &metadataVorbis{ - c: make(map[string]string), - } -} - -type metadataVorbis struct { - c map[string]string // the vorbis comments - p *Picture -} - -func (m *metadataVorbis) readVorbisComment(r io.Reader) error { - vendorLen, err := readInt32LittleEndian(r) - if err != nil { - return err - } - - if vendorLen < 0 { - return fmt.Errorf("invalid encoding: expected positive length, got %d", vendorLen) - } - - vendor, err := readString(r, vendorLen) - if err != nil { - return err - } - m.c["vendor"] = vendor - - commentsLen, err := readInt32LittleEndian(r) - if err != nil { - return err - } - - for i := 0; i < commentsLen; i++ { - l, err := readInt32LittleEndian(r) - if err != nil { - return err - } - s, err := readString(r, l) - if err != nil { - return err - } - k, v, err := parseComment(s) - if err != nil { - return err - } - m.c[strings.ToLower(k)] = v - } - return nil -} - -func (m *metadataVorbis) readPictureBlock(r io.Reader) error { - b, err := readInt(r, 4) - if err != nil { - return err - } - pictureType, ok := pictureTypes[byte(b)] - if !ok { - return fmt.Errorf("invalid picture type: %v", b) - } - mimeLen, err := readInt(r, 4) - if err != nil { - return err - } - mime, err := readString(r, mimeLen) - if err != nil { - return err - } - - ext := "" - switch mime { - case "image/jpeg": - ext = "jpg" - case "image/png": - ext = "png" - case "image/gif": - ext = "gif" - } - - descLen, err := readInt(r, 4) - if err != nil { - return err - } - desc, err := readString(r, descLen) - if err != nil { - return err - } - - // We skip width <32>, height <32>, colorDepth <32>, coloresUsed <32> - _, err = readInt(r, 4) // width - if err != nil { - return err - } - _, err = readInt(r, 4) // height - if err != nil { - return err - } - _, err = readInt(r, 4) // color depth - if err != nil { - return err - } - _, err = readInt(r, 4) // colors used - if err != nil { - return err - } - - dataLen, err := readInt(r, 4) - if err != nil { - return err - } - data := make([]byte, dataLen) - _, err = io.ReadFull(r, data) - if err != nil { - return err - } - - m.p = &Picture{ - Ext: ext, - MIMEType: mime, - Type: pictureType, - Description: desc, - Data: data, - } - return nil -} - -func parseComment(c string) (k, v string, err error) { - kv := strings.SplitN(c, "=", 2) - if len(kv) != 2 { - err = errors.New("vorbis comment must contain '='") - return - } - k = kv[0] - v = kv[1] - return -} - -func (m *metadataVorbis) Format() Format { - return VORBIS -} - -func (m *metadataVorbis) Raw() map[string]interface{} { - raw := make(map[string]interface{}, len(m.c)) - for k, v := range m.c { - raw[k] = v - } - return raw -} - -func (m *metadataVorbis) Title() string { - return m.c["title"] -} - -func (m *metadataVorbis) Artist() string { - // PERFORMER - // The artist(s) who performed the work. In classical music this would be the - // conductor, orchestra, soloists. In an audio book it would be the actor who - // did the reading. In popular music this is typically the same as the ARTIST - // and is omitted. - if m.c["performer"] != "" { - return m.c["performer"] - } - return m.c["artist"] -} - -func (m *metadataVorbis) Album() string { - return m.c["album"] -} - -func (m *metadataVorbis) AlbumArtist() string { - // This field isn't actually included in the standard, though - // it is commonly assigned to albumartist. - return m.c["albumartist"] -} - -func (m *metadataVorbis) Composer() string { - // ARTIST - // The artist generally considered responsible for the work. In popular music - // this is usually the performing band or singer. For classical music it would - // be the composer. For an audio book it would be the author of the original text. - if m.c["composer"] != "" { - return m.c["composer"] - } - if m.c["performer"] == "" { - return "" - } - return m.c["artist"] -} - -func (m *metadataVorbis) Genre() string { - return m.c["genre"] -} - -func (m *metadataVorbis) Year() int { - var dateFormat string - - // The date need to follow the international standard https://en.wikipedia.org/wiki/ISO_8601 - // and obviously the VorbisComment standard https://wiki.xiph.org/VorbisComment#Date_and_time - switch len(m.c["date"]) { - case 0: - return 0 - case 4: - dateFormat = "2006" - case 7: - dateFormat = "2006-01" - case 10: - dateFormat = "2006-01-02" - } - - t, _ := time.Parse(dateFormat, m.c["date"]) - return t.Year() -} - -func (m *metadataVorbis) Track() (int, int) { - x, _ := strconv.Atoi(m.c["tracknumber"]) - // https://wiki.xiph.org/Field_names - n, _ := strconv.Atoi(m.c["tracktotal"]) - return x, n -} - -func (m *metadataVorbis) Disc() (int, int) { - // https://wiki.xiph.org/Field_names - x, _ := strconv.Atoi(m.c["discnumber"]) - n, _ := strconv.Atoi(m.c["disctotal"]) - return x, n -} - -func (m *metadataVorbis) Lyrics() string { - return m.c["lyrics"] -} - -func (m *metadataVorbis) Comment() string { - if m.c["comment"] != "" { - return m.c["comment"] - } - return m.c["description"] -} - -func (m *metadataVorbis) Picture() *Picture { - return m.p -} diff --git a/vendor/github.com/labstack/echo/.editorconfig b/vendor/github.com/labstack/echo/.editorconfig deleted file mode 100644 index 17ae50d..0000000 --- a/vendor/github.com/labstack/echo/.editorconfig +++ /dev/null @@ -1,25 +0,0 @@ -# EditorConfig coding styles definitions. For more information about the -# properties used in this file, please see the EditorConfig documentation: -# http://editorconfig.org/ - -# indicate this is the root of the project -root = true - -[*] -charset = utf-8 - -end_of_line = LF -insert_final_newline = true -trim_trailing_whitespace = true - -indent_style = space -indent_size = 2 - -[Makefile] -indent_style = tab - -[*.md] -trim_trailing_whitespace = false - -[*.go] -indent_style = tab diff --git a/vendor/github.com/labstack/echo/.gitattributes b/vendor/github.com/labstack/echo/.gitattributes deleted file mode 100644 index 49b63e5..0000000 --- a/vendor/github.com/labstack/echo/.gitattributes +++ /dev/null @@ -1,20 +0,0 @@ -# Automatically normalize line endings for all text-based files -# http://git-scm.com/docs/gitattributes#_end_of_line_conversion -* text=auto - -# For the following file types, normalize line endings to LF on checking and -# prevent conversion to CRLF when they are checked out (this is required in -# order to prevent newline related issues) -.* text eol=lf -*.go text eol=lf -*.yml text eol=lf -*.html text eol=lf -*.css text eol=lf -*.js text eol=lf -*.json text eol=lf -LICENSE text eol=lf - -# Exclude `website` and `cookbook` from GitHub's language statistics -# https://github.com/github/linguist#using-gitattributes -cookbook/* linguist-documentation -website/* linguist-documentation diff --git a/vendor/github.com/labstack/echo/.gitignore b/vendor/github.com/labstack/echo/.gitignore deleted file mode 100644 index dd74acc..0000000 --- a/vendor/github.com/labstack/echo/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.DS_Store -coverage.txt -_test -vendor -.idea -*.iml -*.out diff --git a/vendor/github.com/labstack/echo/.travis.yml b/vendor/github.com/labstack/echo/.travis.yml deleted file mode 100644 index 30346d7..0000000 --- a/vendor/github.com/labstack/echo/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -go: - - 1.11.x - - tip -env: - - GO111MODULE=on -install: - - go get -v golang.org/x/lint/golint -script: - - golint -set_exit_status ./... - - go test -race -coverprofile=coverage.txt -covermode=atomic ./... -after_success: - - bash <(curl -s https://codecov.io/bash) -matrix: - allow_failures: - - go: tip diff --git a/vendor/github.com/labstack/echo/LICENSE b/vendor/github.com/labstack/echo/LICENSE deleted file mode 100644 index b5b006b..0000000 --- a/vendor/github.com/labstack/echo/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 LabStack - -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/labstack/echo/Makefile b/vendor/github.com/labstack/echo/Makefile deleted file mode 100644 index dfcb6c0..0000000 --- a/vendor/github.com/labstack/echo/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -tag: - @git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'` - @git tag|grep -v ^v diff --git a/vendor/github.com/labstack/echo/README.md b/vendor/github.com/labstack/echo/README.md deleted file mode 100644 index 49e4d3b..0000000 --- a/vendor/github.com/labstack/echo/README.md +++ /dev/null @@ -1,99 +0,0 @@ - - -[![Sourcegraph](https://sourcegraph.com/github.com/labstack/echo/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/labstack/echo?badge) -[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/labstack/echo) -[![Go Report Card](https://goreportcard.com/badge/github.com/labstack/echo?style=flat-square)](https://goreportcard.com/report/github.com/labstack/echo) -[![Build Status](http://img.shields.io/travis/labstack/echo.svg?style=flat-square)](https://travis-ci.org/labstack/echo) -[![Codecov](https://img.shields.io/codecov/c/github/labstack/echo.svg?style=flat-square)](https://codecov.io/gh/labstack/echo) -[![Join the chat at https://gitter.im/labstack/echo](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg?style=flat-square)](https://gitter.im/labstack/echo) -[![Forum](https://img.shields.io/badge/community-forum-00afd1.svg?style=flat-square)](https://forum.labstack.com) -[![Twitter](https://img.shields.io/badge/twitter-@labstack-55acee.svg?style=flat-square)](https://twitter.com/labstack) -[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) - -## Feature Overview - -- Optimized HTTP router which smartly prioritize routes -- Build robust and scalable RESTful APIs -- Group APIs -- Extensible middleware framework -- Define middleware at root, group or route level -- Data binding for JSON, XML and form payload -- Handy functions to send variety of HTTP responses -- Centralized HTTP error handling -- Template rendering with any template engine -- Define your format for the logger -- Highly customizable -- Automatic TLS via Let’s Encrypt -- HTTP/2 support - -## Benchmarks - -Date: 2018/03/15
-Source: https://github.com/vishr/web-framework-benchmark
-Lower is better! - - - -## [Guide](https://echo.labstack.com/guide) - -### Example - -```go -package main - -import ( - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func main() { - // Echo instance - e := echo.New() - - // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - // Routes - e.GET("/", hello) - - // Start server - e.Logger.Fatal(e.Start(":1323")) -} - -// Handler -func hello(c echo.Context) error { - return c.String(http.StatusOK, "Hello, World!") -} -``` - -## Help - -- [Forum](https://forum.labstack.com) -- [Chat](https://gitter.im/labstack/echo) - -## Contribute - -**Use issues for everything** - -- For a small change, just send a PR. -- For bigger changes open an issue for discussion before sending a PR. -- PR should have: - - Test case - - Documentation - - Example (If it makes sense) -- You can also contribute by: - - Reporting issues - - Suggesting new features or enhancements - - Improve/fix documentation - -## Credits -- [Vishal Rana](https://github.com/vishr) - Author -- [Nitin Rana](https://github.com/nr17) - Consultant -- [Contributors](https://github.com/labstack/echo/graphs/contributors) - -## License - -[MIT](https://github.com/labstack/echo/blob/master/LICENSE) diff --git a/vendor/github.com/labstack/echo/bind.go b/vendor/github.com/labstack/echo/bind.go deleted file mode 100644 index 4998e25..0000000 --- a/vendor/github.com/labstack/echo/bind.go +++ /dev/null @@ -1,277 +0,0 @@ -package echo - -import ( - "encoding/json" - "encoding/xml" - "errors" - "fmt" - "net/http" - "reflect" - "strconv" - "strings" -) - -type ( - // Binder is the interface that wraps the Bind method. - Binder interface { - Bind(i interface{}, c Context) error - } - - // DefaultBinder is the default implementation of the Binder interface. - DefaultBinder struct{} - - // BindUnmarshaler is the interface used to wrap the UnmarshalParam method. - BindUnmarshaler interface { - // UnmarshalParam decodes and assigns a value from an form or query param. - UnmarshalParam(param string) error - } -) - -// Bind implements the `Binder#Bind` function. -func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) { - req := c.Request() - if req.ContentLength == 0 { - if req.Method == http.MethodGet || req.Method == http.MethodDelete { - if err = b.bindData(i, c.QueryParams(), "query"); err != nil { - return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) - } - return - } - return NewHTTPError(http.StatusBadRequest, "Request body can't be empty") - } - ctype := req.Header.Get(HeaderContentType) - switch { - case strings.HasPrefix(ctype, MIMEApplicationJSON): - if err = json.NewDecoder(req.Body).Decode(i); err != nil { - if ute, ok := err.(*json.UnmarshalTypeError); ok { - return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)).SetInternal(err) - } else if se, ok := err.(*json.SyntaxError); ok { - return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())).SetInternal(err) - } else { - return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) - } - return NewHTTPError(http.StatusBadRequest, err.Error()) - } - case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML): - if err = xml.NewDecoder(req.Body).Decode(i); err != nil { - if ute, ok := err.(*xml.UnsupportedTypeError); ok { - return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err) - } else if se, ok := err.(*xml.SyntaxError); ok { - return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())).SetInternal(err) - } else { - return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) - } - return NewHTTPError(http.StatusBadRequest, err.Error()) - } - case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm): - params, err := c.FormParams() - if err != nil { - return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) - } - if err = b.bindData(i, params, "form"); err != nil { - return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err) - } - default: - return ErrUnsupportedMediaType - } - return -} - -func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error { - typ := reflect.TypeOf(ptr).Elem() - val := reflect.ValueOf(ptr).Elem() - - if typ.Kind() != reflect.Struct { - return errors.New("binding element must be a struct") - } - - for i := 0; i < typ.NumField(); i++ { - typeField := typ.Field(i) - structField := val.Field(i) - if !structField.CanSet() { - continue - } - structFieldKind := structField.Kind() - inputFieldName := typeField.Tag.Get(tag) - - if inputFieldName == "" { - inputFieldName = typeField.Name - // If tag is nil, we inspect if the field is a struct. - if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct { - if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil { - return err - } - continue - } - } - - inputValue, exists := data[inputFieldName] - if !exists { - // Go json.Unmarshal supports case insensitive binding. However the - // url params are bound case sensitive which is inconsistent. To - // fix this we must check all of the map values in a - // case-insensitive search. - inputFieldName = strings.ToLower(inputFieldName) - for k, v := range data { - if strings.ToLower(k) == inputFieldName { - inputValue = v - exists = true - break - } - } - } - - if !exists { - continue - } - - // Call this first, in case we're dealing with an alias to an array type - if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok { - if err != nil { - return err - } - continue - } - - numElems := len(inputValue) - if structFieldKind == reflect.Slice && numElems > 0 { - sliceOf := structField.Type().Elem().Kind() - slice := reflect.MakeSlice(structField.Type(), numElems, numElems) - for j := 0; j < numElems; j++ { - if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil { - return err - } - } - val.Field(i).Set(slice) - } else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil { - return err - - } - } - return nil -} - -func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error { - // But also call it here, in case we're dealing with an array of BindUnmarshalers - if ok, err := unmarshalField(valueKind, val, structField); ok { - return err - } - - switch valueKind { - case reflect.Ptr: - return setWithProperType(structField.Elem().Kind(), val, structField.Elem()) - case reflect.Int: - return setIntField(val, 0, structField) - case reflect.Int8: - return setIntField(val, 8, structField) - case reflect.Int16: - return setIntField(val, 16, structField) - case reflect.Int32: - return setIntField(val, 32, structField) - case reflect.Int64: - return setIntField(val, 64, structField) - case reflect.Uint: - return setUintField(val, 0, structField) - case reflect.Uint8: - return setUintField(val, 8, structField) - case reflect.Uint16: - return setUintField(val, 16, structField) - case reflect.Uint32: - return setUintField(val, 32, structField) - case reflect.Uint64: - return setUintField(val, 64, structField) - case reflect.Bool: - return setBoolField(val, structField) - case reflect.Float32: - return setFloatField(val, 32, structField) - case reflect.Float64: - return setFloatField(val, 64, structField) - case reflect.String: - structField.SetString(val) - default: - return errors.New("unknown type") - } - return nil -} - -func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) { - switch valueKind { - case reflect.Ptr: - return unmarshalFieldPtr(val, field) - default: - return unmarshalFieldNonPtr(val, field) - } -} - -// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler -func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) { - ptr := reflect.New(field.Type()) - if ptr.CanInterface() { - iface := ptr.Interface() - if unmarshaler, ok := iface.(BindUnmarshaler); ok { - return unmarshaler, ok - } - } - return nil, false -} - -func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) { - if unmarshaler, ok := bindUnmarshaler(field); ok { - err := unmarshaler.UnmarshalParam(value) - field.Set(reflect.ValueOf(unmarshaler).Elem()) - return true, err - } - return false, nil -} - -func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) { - if field.IsNil() { - // Initialize the pointer to a nil value - field.Set(reflect.New(field.Type().Elem())) - } - return unmarshalFieldNonPtr(value, field.Elem()) -} - -func setIntField(value string, bitSize int, field reflect.Value) error { - if value == "" { - value = "0" - } - intVal, err := strconv.ParseInt(value, 10, bitSize) - if err == nil { - field.SetInt(intVal) - } - return err -} - -func setUintField(value string, bitSize int, field reflect.Value) error { - if value == "" { - value = "0" - } - uintVal, err := strconv.ParseUint(value, 10, bitSize) - if err == nil { - field.SetUint(uintVal) - } - return err -} - -func setBoolField(value string, field reflect.Value) error { - if value == "" { - value = "false" - } - boolVal, err := strconv.ParseBool(value) - if err == nil { - field.SetBool(boolVal) - } - return err -} - -func setFloatField(value string, bitSize int, field reflect.Value) error { - if value == "" { - value = "0.0" - } - floatVal, err := strconv.ParseFloat(value, bitSize) - if err == nil { - field.SetFloat(floatVal) - } - return err -} diff --git a/vendor/github.com/labstack/echo/context.go b/vendor/github.com/labstack/echo/context.go deleted file mode 100644 index d472270..0000000 --- a/vendor/github.com/labstack/echo/context.go +++ /dev/null @@ -1,600 +0,0 @@ -package echo - -import ( - "bytes" - "encoding/json" - "encoding/xml" - "fmt" - "io" - "mime/multipart" - "net" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" -) - -type ( - // Context represents the context of the current HTTP request. It holds request and - // response objects, path, path parameters, data and registered handler. - Context interface { - // Request returns `*http.Request`. - Request() *http.Request - - // SetRequest sets `*http.Request`. - SetRequest(r *http.Request) - - // Response returns `*Response`. - Response() *Response - - // IsTLS returns true if HTTP connection is TLS otherwise false. - IsTLS() bool - - // IsWebSocket returns true if HTTP connection is WebSocket otherwise false. - IsWebSocket() bool - - // Scheme returns the HTTP protocol scheme, `http` or `https`. - Scheme() string - - // RealIP returns the client's network address based on `X-Forwarded-For` - // or `X-Real-IP` request header. - RealIP() string - - // Path returns the registered path for the handler. - Path() string - - // SetPath sets the registered path for the handler. - SetPath(p string) - - // Param returns path parameter by name. - Param(name string) string - - // ParamNames returns path parameter names. - ParamNames() []string - - // SetParamNames sets path parameter names. - SetParamNames(names ...string) - - // ParamValues returns path parameter values. - ParamValues() []string - - // SetParamValues sets path parameter values. - SetParamValues(values ...string) - - // QueryParam returns the query param for the provided name. - QueryParam(name string) string - - // QueryParams returns the query parameters as `url.Values`. - QueryParams() url.Values - - // QueryString returns the URL query string. - QueryString() string - - // FormValue returns the form field value for the provided name. - FormValue(name string) string - - // FormParams returns the form parameters as `url.Values`. - FormParams() (url.Values, error) - - // FormFile returns the multipart form file for the provided name. - FormFile(name string) (*multipart.FileHeader, error) - - // MultipartForm returns the multipart form. - MultipartForm() (*multipart.Form, error) - - // Cookie returns the named cookie provided in the request. - Cookie(name string) (*http.Cookie, error) - - // SetCookie adds a `Set-Cookie` header in HTTP response. - SetCookie(cookie *http.Cookie) - - // Cookies returns the HTTP cookies sent with the request. - Cookies() []*http.Cookie - - // Get retrieves data from the context. - Get(key string) interface{} - - // 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(i interface{}) error - - // Validate validates provided `i`. It is usually called after `Context#Bind()`. - // Validator must be registered using `Echo#Validator`. - Validate(i interface{}) error - - // Render renders a template with data and sends a text/html response with status - // code. Renderer must be registered using `Echo.Renderer`. - Render(code int, name string, data interface{}) error - - // HTML sends an HTTP response with status code. - HTML(code int, html string) error - - // HTMLBlob sends an HTTP blob response with status code. - HTMLBlob(code int, b []byte) error - - // String sends a string response with status code. - String(code int, s string) error - - // JSON sends a JSON response with status code. - JSON(code int, i interface{}) error - - // JSONPretty sends a pretty-print JSON with status code. - JSONPretty(code int, i interface{}, indent string) error - - // JSONBlob sends a JSON blob response with status code. - JSONBlob(code int, b []byte) error - - // JSONP sends a JSONP response with status code. It uses `callback` to construct - // the JSONP payload. - JSONP(code int, callback string, i interface{}) error - - // JSONPBlob sends a JSONP blob response with status code. It uses `callback` - // to construct the JSONP payload. - JSONPBlob(code int, callback string, b []byte) error - - // XML sends an XML response with status code. - XML(code int, i interface{}) error - - // XMLPretty sends a pretty-print XML with status code. - XMLPretty(code int, i interface{}, indent string) error - - // XMLBlob sends an XML blob response with status code. - XMLBlob(code int, b []byte) error - - // Blob sends a blob response with status code and content type. - Blob(code int, contentType string, b []byte) error - - // Stream sends a streaming response with status code and content type. - Stream(code int, contentType string, r io.Reader) error - - // File sends a response with the content of the file. - File(file string) error - - // Attachment sends a response as attachment, prompting client to save the - // file. - Attachment(file string, name string) error - - // Inline sends a response as inline, opening the file in the browser. - Inline(file string, name string) error - - // NoContent sends a response with no body and a status code. - NoContent(code int) error - - // Redirect redirects the request to a provided URL with status code. - Redirect(code int, url string) error - - // Error invokes the registered HTTP error handler. Generally used by middleware. - Error(err error) - - // Handler returns the matched handler by router. - Handler() HandlerFunc - - // SetHandler sets the matched handler by router. - SetHandler(h HandlerFunc) - - // Logger returns the `Logger` instance. - Logger() Logger - - // Echo returns the `Echo` instance. - Echo() *Echo - - // Reset resets the context after request completes. It must be called along - // with `Echo#AcquireContext()` and `Echo#ReleaseContext()`. - // See `Echo#ServeHTTP()` - Reset(r *http.Request, w http.ResponseWriter) - } - - context struct { - request *http.Request - response *Response - path string - pnames []string - pvalues []string - query url.Values - handler HandlerFunc - store Map - echo *Echo - } -) - -const ( - defaultMemory = 32 << 20 // 32 MB - indexPage = "index.html" - defaultIndent = " " -) - -func (c *context) writeContentType(value string) { - header := c.Response().Header() - if header.Get(HeaderContentType) == "" { - header.Set(HeaderContentType, value) - } -} - -func (c *context) Request() *http.Request { - return c.request -} - -func (c *context) SetRequest(r *http.Request) { - c.request = r -} - -func (c *context) Response() *Response { - return c.response -} - -func (c *context) IsTLS() bool { - return c.request.TLS != nil -} - -func (c *context) IsWebSocket() bool { - upgrade := c.request.Header.Get(HeaderUpgrade) - return upgrade == "websocket" || upgrade == "Websocket" -} - -func (c *context) Scheme() string { - // Can't use `r.Request.URL.Scheme` - // See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0 - if c.IsTLS() { - return "https" - } - if scheme := c.request.Header.Get(HeaderXForwardedProto); scheme != "" { - return scheme - } - if scheme := c.request.Header.Get(HeaderXForwardedProtocol); scheme != "" { - return scheme - } - if ssl := c.request.Header.Get(HeaderXForwardedSsl); ssl == "on" { - return "https" - } - if scheme := c.request.Header.Get(HeaderXUrlScheme); scheme != "" { - return scheme - } - return "http" -} - -func (c *context) RealIP() string { - if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { - return strings.Split(ip, ", ")[0] - } - if ip := c.request.Header.Get(HeaderXRealIP); ip != "" { - return ip - } - ra, _, _ := net.SplitHostPort(c.request.RemoteAddr) - return ra -} - -func (c *context) Path() string { - return c.path -} - -func (c *context) SetPath(p string) { - c.path = p -} - -func (c *context) Param(name string) string { - for i, n := range c.pnames { - if i < len(c.pvalues) { - if n == name { - return c.pvalues[i] - } - } - } - return "" -} - -func (c *context) ParamNames() []string { - return c.pnames -} - -func (c *context) SetParamNames(names ...string) { - c.pnames = names -} - -func (c *context) ParamValues() []string { - return c.pvalues[:len(c.pnames)] -} - -func (c *context) SetParamValues(values ...string) { - c.pvalues = values -} - -func (c *context) QueryParam(name string) string { - if c.query == nil { - c.query = c.request.URL.Query() - } - return c.query.Get(name) -} - -func (c *context) QueryParams() url.Values { - if c.query == nil { - c.query = c.request.URL.Query() - } - return c.query -} - -func (c *context) QueryString() string { - return c.request.URL.RawQuery -} - -func (c *context) FormValue(name string) string { - return c.request.FormValue(name) -} - -func (c *context) FormParams() (url.Values, error) { - if strings.HasPrefix(c.request.Header.Get(HeaderContentType), MIMEMultipartForm) { - if err := c.request.ParseMultipartForm(defaultMemory); err != nil { - return nil, err - } - } else { - if err := c.request.ParseForm(); err != nil { - return nil, err - } - } - return c.request.Form, nil -} - -func (c *context) FormFile(name string) (*multipart.FileHeader, error) { - _, fh, err := c.request.FormFile(name) - return fh, err -} - -func (c *context) MultipartForm() (*multipart.Form, error) { - err := c.request.ParseMultipartForm(defaultMemory) - return c.request.MultipartForm, err -} - -func (c *context) Cookie(name string) (*http.Cookie, error) { - return c.request.Cookie(name) -} - -func (c *context) SetCookie(cookie *http.Cookie) { - http.SetCookie(c.Response(), cookie) -} - -func (c *context) Cookies() []*http.Cookie { - return c.request.Cookies() -} - -func (c *context) Get(key string) interface{} { - return c.store[key] -} - -func (c *context) Set(key string, val interface{}) { - if c.store == nil { - c.store = make(Map) - } - c.store[key] = val -} - -func (c *context) Bind(i interface{}) error { - return c.echo.Binder.Bind(i, c) -} - -func (c *context) Validate(i interface{}) error { - if c.echo.Validator == nil { - return ErrValidatorNotRegistered - } - return c.echo.Validator.Validate(i) -} - -func (c *context) Render(code int, name string, data interface{}) (err error) { - if c.echo.Renderer == nil { - return ErrRendererNotRegistered - } - buf := new(bytes.Buffer) - if err = c.echo.Renderer.Render(buf, name, data, c); err != nil { - return - } - return c.HTMLBlob(code, buf.Bytes()) -} - -func (c *context) HTML(code int, html string) (err error) { - return c.HTMLBlob(code, []byte(html)) -} - -func (c *context) HTMLBlob(code int, b []byte) (err error) { - return c.Blob(code, MIMETextHTMLCharsetUTF8, b) -} - -func (c *context) String(code int, s string) (err error) { - return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s)) -} - -func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error) { - enc := json.NewEncoder(c.response) - _, pretty := c.QueryParams()["pretty"] - if c.echo.Debug || pretty { - enc.SetIndent("", " ") - } - c.writeContentType(MIMEApplicationJavaScriptCharsetUTF8) - c.response.WriteHeader(code) - if _, err = c.response.Write([]byte(callback + "(")); err != nil { - return - } - if err = enc.Encode(i); err != nil { - return - } - if _, err = c.response.Write([]byte(");")); err != nil { - return - } - return -} - -func (c *context) json(code int, i interface{}, indent string) error { - enc := json.NewEncoder(c.response) - if indent != "" { - enc.SetIndent("", indent) - } - c.writeContentType(MIMEApplicationJSONCharsetUTF8) - c.response.WriteHeader(code) - return enc.Encode(i) -} - -func (c *context) JSON(code int, i interface{}) (err error) { - indent := "" - if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty { - indent = defaultIndent - } - return c.json(code, i, indent) -} - -func (c *context) JSONPretty(code int, i interface{}, indent string) (err error) { - return c.json(code, i, indent) -} - -func (c *context) JSONBlob(code int, b []byte) (err error) { - return c.Blob(code, MIMEApplicationJSONCharsetUTF8, b) -} - -func (c *context) JSONP(code int, callback string, i interface{}) (err error) { - return c.jsonPBlob(code, callback, i) -} - -func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) { - c.writeContentType(MIMEApplicationJavaScriptCharsetUTF8) - c.response.WriteHeader(code) - if _, err = c.response.Write([]byte(callback + "(")); err != nil { - return - } - if _, err = c.response.Write(b); err != nil { - return - } - _, err = c.response.Write([]byte(");")) - return -} - -func (c *context) xml(code int, i interface{}, indent string) (err error) { - c.writeContentType(MIMEApplicationXMLCharsetUTF8) - c.response.WriteHeader(code) - enc := xml.NewEncoder(c.response) - if indent != "" { - enc.Indent("", indent) - } - if _, err = c.response.Write([]byte(xml.Header)); err != nil { - return - } - return enc.Encode(i) -} - -func (c *context) XML(code int, i interface{}) (err error) { - indent := "" - if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty { - indent = defaultIndent - } - return c.xml(code, i, indent) -} - -func (c *context) XMLPretty(code int, i interface{}, indent string) (err error) { - return c.xml(code, i, indent) -} - -func (c *context) XMLBlob(code int, b []byte) (err error) { - c.writeContentType(MIMEApplicationXMLCharsetUTF8) - c.response.WriteHeader(code) - if _, err = c.response.Write([]byte(xml.Header)); err != nil { - return - } - _, err = c.response.Write(b) - return -} - -func (c *context) Blob(code int, contentType string, b []byte) (err error) { - c.writeContentType(contentType) - c.response.WriteHeader(code) - _, err = c.response.Write(b) - return -} - -func (c *context) Stream(code int, contentType string, r io.Reader) (err error) { - c.writeContentType(contentType) - c.response.WriteHeader(code) - _, err = io.Copy(c.response, r) - return -} - -func (c *context) File(file string) (err error) { - f, err := os.Open(file) - if err != nil { - return NotFoundHandler(c) - } - defer f.Close() - - fi, _ := f.Stat() - if fi.IsDir() { - file = filepath.Join(file, indexPage) - f, err = os.Open(file) - if err != nil { - return NotFoundHandler(c) - } - defer f.Close() - if fi, err = f.Stat(); err != nil { - return - } - } - http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f) - return -} - -func (c *context) Attachment(file, name string) error { - return c.contentDisposition(file, name, "attachment") -} - -func (c *context) Inline(file, name string) error { - return c.contentDisposition(file, name, "inline") -} - -func (c *context) contentDisposition(file, name, dispositionType string) error { - c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name)) - return c.File(file) -} - -func (c *context) NoContent(code int) error { - c.response.WriteHeader(code) - return nil -} - -func (c *context) Redirect(code int, url string) error { - if code < 300 || code > 308 { - return ErrInvalidRedirectCode - } - c.response.Header().Set(HeaderLocation, url) - c.response.WriteHeader(code) - return nil -} - -func (c *context) Error(err error) { - c.echo.HTTPErrorHandler(err, c) -} - -func (c *context) Echo() *Echo { - return c.echo -} - -func (c *context) Handler() HandlerFunc { - return c.handler -} - -func (c *context) SetHandler(h HandlerFunc) { - c.handler = h -} - -func (c *context) Logger() Logger { - return c.echo.Logger -} - -func (c *context) Reset(r *http.Request, w http.ResponseWriter) { - c.request = r - c.response.reset(w) - c.query = nil - c.handler = NotFoundHandler - c.store = nil - c.path = "" - c.pnames = nil - // NOTE: Don't reset because it has to have length c.echo.maxParam at all times - // c.pvalues = nil -} - diff --git a/vendor/github.com/labstack/echo/echo.go b/vendor/github.com/labstack/echo/echo.go deleted file mode 100644 index 9828651..0000000 --- a/vendor/github.com/labstack/echo/echo.go +++ /dev/null @@ -1,782 +0,0 @@ -/* -Package echo implements high performance, minimalist Go web framework. - -Example: - - package main - - import ( - "net/http" - - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" - ) - - // Handler - func hello(c echo.Context) error { - return c.String(http.StatusOK, "Hello, World!") - } - - func main() { - // Echo instance - e := echo.New() - - // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - // Routes - e.GET("/", hello) - - // Start server - e.Logger.Fatal(e.Start(":1323")) - } - -Learn more at https://echo.labstack.com -*/ -package echo - -import ( - "bytes" - stdContext "context" - "crypto/tls" - "errors" - "fmt" - "io" - stdLog "log" - "net" - "net/http" - "net/url" - "path" - "path/filepath" - "reflect" - "runtime" - "sync" - "time" - - "github.com/labstack/gommon/color" - "github.com/labstack/gommon/log" - "golang.org/x/crypto/acme/autocert" -) - -type ( - // Echo is the top-level framework instance. - Echo struct { - StdLogger *stdLog.Logger - colorer *color.Color - premiddleware []MiddlewareFunc - middleware []MiddlewareFunc - maxParam *int - router *Router - notFoundHandler HandlerFunc - pool sync.Pool - Server *http.Server - TLSServer *http.Server - Listener net.Listener - TLSListener net.Listener - AutoTLSManager autocert.Manager - DisableHTTP2 bool - Debug bool - HideBanner bool - HidePort bool - HTTPErrorHandler HTTPErrorHandler - Binder Binder - Validator Validator - Renderer Renderer - Logger Logger - } - - // Route contains a handler and information for matching against requests. - Route struct { - Method string `json:"method"` - Path string `json:"path"` - Name string `json:"name"` - } - - // HTTPError represents an error that occurred while handling a request. - HTTPError struct { - Code int - Message interface{} - Internal error // Stores the error returned by an external dependency - } - - // MiddlewareFunc defines a function to process middleware. - MiddlewareFunc func(HandlerFunc) HandlerFunc - - // HandlerFunc defines a function to serve HTTP requests. - HandlerFunc func(Context) error - - // HTTPErrorHandler is a centralized HTTP error handler. - HTTPErrorHandler func(error, Context) - - // Validator is the interface that wraps the Validate function. - Validator interface { - Validate(i interface{}) error - } - - // Renderer is the interface that wraps the Render function. - Renderer interface { - Render(io.Writer, string, interface{}, Context) error - } - - // Map defines a generic map of type `map[string]interface{}`. - Map map[string]interface{} - - // i is the interface for Echo and Group. - i interface { - GET(string, HandlerFunc, ...MiddlewareFunc) *Route - } -) - -// HTTP methods -// NOTE: Deprecated, please use the stdlib constants directly instead. -const ( - CONNECT = http.MethodConnect - DELETE = http.MethodDelete - GET = http.MethodGet - HEAD = http.MethodHead - OPTIONS = http.MethodOptions - PATCH = http.MethodPatch - POST = http.MethodPost - // PROPFIND = "PROPFIND" - PUT = http.MethodPut - TRACE = http.MethodTrace -) - -// MIME types -const ( - MIMEApplicationJSON = "application/json" - MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8 - MIMEApplicationJavaScript = "application/javascript" - MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8 - MIMEApplicationXML = "application/xml" - MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + charsetUTF8 - MIMETextXML = "text/xml" - MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + charsetUTF8 - MIMEApplicationForm = "application/x-www-form-urlencoded" - MIMEApplicationProtobuf = "application/protobuf" - MIMEApplicationMsgpack = "application/msgpack" - MIMETextHTML = "text/html" - MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + charsetUTF8 - MIMETextPlain = "text/plain" - MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + charsetUTF8 - MIMEMultipartForm = "multipart/form-data" - MIMEOctetStream = "application/octet-stream" -) - -const ( - charsetUTF8 = "charset=UTF-8" - // PROPFIND Method can be used on collection and property resources. - PROPFIND = "PROPFIND" -) - -// Headers -const ( - HeaderAccept = "Accept" - HeaderAcceptEncoding = "Accept-Encoding" - HeaderAllow = "Allow" - HeaderAuthorization = "Authorization" - HeaderContentDisposition = "Content-Disposition" - HeaderContentEncoding = "Content-Encoding" - HeaderContentLength = "Content-Length" - HeaderContentType = "Content-Type" - HeaderCookie = "Cookie" - HeaderSetCookie = "Set-Cookie" - HeaderIfModifiedSince = "If-Modified-Since" - HeaderLastModified = "Last-Modified" - HeaderLocation = "Location" - HeaderUpgrade = "Upgrade" - HeaderVary = "Vary" - HeaderWWWAuthenticate = "WWW-Authenticate" - HeaderXForwardedFor = "X-Forwarded-For" - HeaderXForwardedProto = "X-Forwarded-Proto" - HeaderXForwardedProtocol = "X-Forwarded-Protocol" - HeaderXForwardedSsl = "X-Forwarded-Ssl" - HeaderXUrlScheme = "X-Url-Scheme" - HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" - HeaderXRealIP = "X-Real-IP" - HeaderXRequestID = "X-Request-ID" - HeaderXRequestedWith = "X-Requested-With" - HeaderServer = "Server" - HeaderOrigin = "Origin" - - // Access control - HeaderAccessControlRequestMethod = "Access-Control-Request-Method" - HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers" - HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin" - HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods" - HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers" - HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" - HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers" - HeaderAccessControlMaxAge = "Access-Control-Max-Age" - - // Security - HeaderStrictTransportSecurity = "Strict-Transport-Security" - HeaderXContentTypeOptions = "X-Content-Type-Options" - HeaderXXSSProtection = "X-XSS-Protection" - HeaderXFrameOptions = "X-Frame-Options" - HeaderContentSecurityPolicy = "Content-Security-Policy" - HeaderXCSRFToken = "X-CSRF-Token" -) - -const ( - // Version of Echo - Version = "3.3.10-dev" - website = "https://echo.labstack.com" - // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo - banner = ` - ____ __ - / __/___/ / ___ - / _// __/ _ \/ _ \ -/___/\__/_//_/\___/ %s -High performance, minimalist Go web framework -%s -____________________________________O/_______ - O\ -` -) - -var ( - methods = [...]string{ - http.MethodConnect, - http.MethodDelete, - http.MethodGet, - http.MethodHead, - http.MethodOptions, - http.MethodPatch, - http.MethodPost, - PROPFIND, - http.MethodPut, - http.MethodTrace, - } -) - -// Errors -var ( - ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType) - ErrNotFound = NewHTTPError(http.StatusNotFound) - ErrUnauthorized = NewHTTPError(http.StatusUnauthorized) - ErrForbidden = NewHTTPError(http.StatusForbidden) - ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) - ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) - ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests) - ErrBadRequest = NewHTTPError(http.StatusBadRequest) - ErrBadGateway = NewHTTPError(http.StatusBadGateway) - ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) - ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout) - ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable) - ErrValidatorNotRegistered = errors.New("validator not registered") - ErrRendererNotRegistered = errors.New("renderer not registered") - ErrInvalidRedirectCode = errors.New("invalid redirect status code") - ErrCookieNotFound = errors.New("cookie not found") -) - -// Error handlers -var ( - NotFoundHandler = func(c Context) error { - return ErrNotFound - } - - MethodNotAllowedHandler = func(c Context) error { - return ErrMethodNotAllowed - } -) - -// New creates an instance of Echo. -func New() (e *Echo) { - e = &Echo{ - Server: new(http.Server), - TLSServer: new(http.Server), - AutoTLSManager: autocert.Manager{ - Prompt: autocert.AcceptTOS, - }, - Logger: log.New("echo"), - colorer: color.New(), - maxParam: new(int), - } - e.Server.Handler = e - e.TLSServer.Handler = e - e.HTTPErrorHandler = e.DefaultHTTPErrorHandler - e.Binder = &DefaultBinder{} - e.Logger.SetLevel(log.ERROR) - e.StdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0) - e.pool.New = func() interface{} { - return e.NewContext(nil, nil) - } - e.router = NewRouter(e) - return -} - -// NewContext returns a Context instance. -func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context { - return &context{ - request: r, - response: NewResponse(w, e), - store: make(Map), - echo: e, - pvalues: make([]string, *e.maxParam), - handler: NotFoundHandler, - } -} - -// Router returns router. -func (e *Echo) Router() *Router { - return e.router -} - -// DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response -// with status code. -func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) { - var ( - code = http.StatusInternalServerError - msg interface{} - ) - - if he, ok := err.(*HTTPError); ok { - code = he.Code - msg = he.Message - if he.Internal != nil { - err = fmt.Errorf("%v, %v", err, he.Internal) - } - } else if e.Debug { - msg = err.Error() - } else { - msg = http.StatusText(code) - } - if _, ok := msg.(string); ok { - msg = Map{"message": msg} - } - - // Send response - if !c.Response().Committed { - if c.Request().Method == http.MethodHead { // Issue #608 - err = c.NoContent(code) - } else { - err = c.JSON(code, msg) - } - if err != nil { - e.Logger.Error(err) - } - } -} - -// Pre adds middleware to the chain which is run before router. -func (e *Echo) Pre(middleware ...MiddlewareFunc) { - e.premiddleware = append(e.premiddleware, middleware...) -} - -// Use adds middleware to the chain which is run after router. -func (e *Echo) Use(middleware ...MiddlewareFunc) { - e.middleware = append(e.middleware, middleware...) -} - -// CONNECT registers a new CONNECT route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodConnect, path, h, m...) -} - -// DELETE registers a new DELETE route for a path with matching handler in the router -// with optional route-level middleware. -func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodDelete, path, h, m...) -} - -// GET registers a new GET route for a path with matching handler in the router -// with optional route-level middleware. -func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodGet, path, h, m...) -} - -// HEAD registers a new HEAD route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodHead, path, h, m...) -} - -// OPTIONS registers a new OPTIONS route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodOptions, path, h, m...) -} - -// PATCH registers a new PATCH route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodPatch, path, h, m...) -} - -// POST registers a new POST route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodPost, path, h, m...) -} - -// PUT registers a new PUT route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodPut, path, h, m...) -} - -// TRACE registers a new TRACE route for a path with matching handler in the -// router with optional route-level middleware. -func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return e.Add(http.MethodTrace, path, h, m...) -} - -// Any registers a new route for all HTTP methods and path with matching handler -// in the router with optional route-level middleware. -func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { - routes := make([]*Route, len(methods)) - for i, m := range methods { - routes[i] = e.Add(m, path, handler, middleware...) - } - return routes -} - -// Match registers a new route for multiple HTTP methods and path with matching -// handler in the router with optional route-level middleware. -func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { - routes := make([]*Route, len(methods)) - for i, m := range methods { - routes[i] = e.Add(m, path, handler, middleware...) - } - return routes -} - -// Static registers a new route with path prefix to serve static files from the -// provided root directory. -func (e *Echo) Static(prefix, root string) *Route { - if root == "" { - root = "." // For security we want to restrict to CWD. - } - return static(e, prefix, root) -} - -func static(i i, prefix, root string) *Route { - h := func(c Context) error { - p, err := url.PathUnescape(c.Param("*")) - if err != nil { - return err - } - name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security - return c.File(name) - } - i.GET(prefix, h) - if prefix == "/" { - return i.GET(prefix+"*", h) - } - - return i.GET(prefix+"/*", h) -} - -// File registers a new route with path to serve a static file with optional route-level middleware. -func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route { - return e.GET(path, func(c Context) error { - return c.File(file) - }, m...) -} - -// Add registers a new route for an HTTP method and path with matching handler -// in the router with optional route-level middleware. -func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { - name := handlerName(handler) - e.router.Add(method, path, func(c Context) error { - h := handler - // Chain middleware - for i := len(middleware) - 1; i >= 0; i-- { - h = middleware[i](h) - } - return h(c) - }) - r := &Route{ - Method: method, - Path: path, - Name: name, - } - e.router.routes[method+path] = r - return r -} - -// Group creates a new router group with prefix and optional group-level middleware. -func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) { - g = &Group{prefix: prefix, echo: e} - g.Use(m...) - return -} - -// URI generates a URI from handler. -func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { - name := handlerName(handler) - return e.Reverse(name, params...) -} - -// URL is an alias for `URI` function. -func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { - return e.URI(h, params...) -} - -// Reverse generates an URL from route name and provided parameters. -func (e *Echo) Reverse(name string, params ...interface{}) string { - uri := new(bytes.Buffer) - ln := len(params) - n := 0 - for _, r := range e.router.routes { - if r.Name == name { - for i, l := 0, len(r.Path); i < l; i++ { - if r.Path[i] == ':' && n < ln { - for ; i < l && r.Path[i] != '/'; i++ { - } - uri.WriteString(fmt.Sprintf("%v", params[n])) - n++ - } - if i < l { - uri.WriteByte(r.Path[i]) - } - } - break - } - } - return uri.String() -} - -// Routes returns the registered routes. -func (e *Echo) Routes() []*Route { - routes := make([]*Route, 0, len(e.router.routes)) - for _, v := range e.router.routes { - routes = append(routes, v) - } - return routes -} - -// AcquireContext returns an empty `Context` instance from the pool. -// You must return the context by calling `ReleaseContext()`. -func (e *Echo) AcquireContext() Context { - return e.pool.Get().(Context) -} - -// ReleaseContext returns the `Context` instance back to the pool. -// You must call it after `AcquireContext()`. -func (e *Echo) ReleaseContext(c Context) { - e.pool.Put(c) -} - -// ServeHTTP implements `http.Handler` interface, which serves HTTP requests. -func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Acquire context - c := e.pool.Get().(*context) - c.Reset(r, w) - - h := NotFoundHandler - - if e.premiddleware == nil { - e.router.Find(r.Method, getPath(r), c) - h = c.Handler() - for i := len(e.middleware) - 1; i >= 0; i-- { - h = e.middleware[i](h) - } - } else { - h = func(c Context) error { - e.router.Find(r.Method, getPath(r), c) - h := c.Handler() - for i := len(e.middleware) - 1; i >= 0; i-- { - h = e.middleware[i](h) - } - return h(c) - } - for i := len(e.premiddleware) - 1; i >= 0; i-- { - h = e.premiddleware[i](h) - } - } - - // Execute chain - if err := h(c); err != nil { - e.HTTPErrorHandler(err, c) - } - - // Release context - e.pool.Put(c) -} - -// Start starts an HTTP server. -func (e *Echo) Start(address string) error { - e.Server.Addr = address - return e.StartServer(e.Server) -} - -// StartTLS starts an HTTPS server. -func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) { - if certFile == "" || keyFile == "" { - return errors.New("invalid tls configuration") - } - s := e.TLSServer - s.TLSConfig = new(tls.Config) - s.TLSConfig.Certificates = make([]tls.Certificate, 1) - s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return - } - return e.startTLS(address) -} - -// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org. -func (e *Echo) StartAutoTLS(address string) error { - s := e.TLSServer - s.TLSConfig = new(tls.Config) - s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate - return e.startTLS(address) -} - -func (e *Echo) startTLS(address string) error { - s := e.TLSServer - s.Addr = address - if !e.DisableHTTP2 { - s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2") - } - return e.StartServer(e.TLSServer) -} - -// StartServer starts a custom http server. -func (e *Echo) StartServer(s *http.Server) (err error) { - // Setup - e.colorer.SetOutput(e.Logger.Output()) - s.ErrorLog = e.StdLogger - s.Handler = e - if e.Debug { - e.Logger.SetLevel(log.DEBUG) - } - - if !e.HideBanner { - e.colorer.Printf(banner, e.colorer.Red("v"+Version), e.colorer.Blue(website)) - } - - if s.TLSConfig == nil { - if e.Listener == nil { - e.Listener, err = newListener(s.Addr) - if err != nil { - return err - } - } - if !e.HidePort { - e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr())) - } - return s.Serve(e.Listener) - } - if e.TLSListener == nil { - l, err := newListener(s.Addr) - if err != nil { - return err - } - e.TLSListener = tls.NewListener(l, s.TLSConfig) - } - if !e.HidePort { - e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr())) - } - return s.Serve(e.TLSListener) -} - -// Close immediately stops the server. -// It internally calls `http.Server#Close()`. -func (e *Echo) Close() error { - if err := e.TLSServer.Close(); err != nil { - return err - } - return e.Server.Close() -} - -// Shutdown stops the server gracefully. -// It internally calls `http.Server#Shutdown()`. -func (e *Echo) Shutdown(ctx stdContext.Context) error { - if err := e.TLSServer.Shutdown(ctx); err != nil { - return err - } - return e.Server.Shutdown(ctx) -} - -// NewHTTPError creates a new HTTPError instance. -func NewHTTPError(code int, message ...interface{}) *HTTPError { - he := &HTTPError{Code: code, Message: http.StatusText(code)} - if len(message) > 0 { - he.Message = message[0] - } - return he -} - -// Error makes it compatible with `error` interface. -func (he *HTTPError) Error() string { - return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message) -} - -// SetInternal sets error to HTTPError.Internal -func (he *HTTPError) SetInternal(err error) *HTTPError { - he.Internal = err - return he -} - -// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`. -func WrapHandler(h http.Handler) HandlerFunc { - return func(c Context) error { - h.ServeHTTP(c.Response(), c.Request()) - return nil - } -} - -// WrapMiddleware wraps `func(http.Handler) http.Handler` into `echo.MiddlewareFunc` -func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc { - return func(next HandlerFunc) HandlerFunc { - return func(c Context) (err error) { - m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.SetRequest(r) - err = next(c) - })).ServeHTTP(c.Response(), c.Request()) - return - } - } -} - -func getPath(r *http.Request) string { - path := r.URL.RawPath - if path == "" { - path = r.URL.Path - } - return path -} - -func handlerName(h HandlerFunc) string { - t := reflect.ValueOf(h).Type() - if t.Kind() == reflect.Func { - return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name() - } - return t.String() -} - -// // PathUnescape is wraps `url.PathUnescape` -// func PathUnescape(s string) (string, error) { -// return url.PathUnescape(s) -// } - -// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted -// connections. It's used by ListenAndServe and ListenAndServeTLS so -// dead TCP connections (e.g. closing laptop mid-download) eventually -// go away. -type tcpKeepAliveListener struct { - *net.TCPListener -} - -func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { - tc, err := ln.AcceptTCP() - if err != nil { - return - } - tc.SetKeepAlive(true) - tc.SetKeepAlivePeriod(3 * time.Minute) - return tc, nil -} - -func newListener(address string) (*tcpKeepAliveListener, error) { - l, err := net.Listen("tcp", address) - if err != nil { - return nil, err - } - return &tcpKeepAliveListener{l.(*net.TCPListener)}, nil -} diff --git a/vendor/github.com/labstack/echo/group.go b/vendor/github.com/labstack/echo/group.go deleted file mode 100644 index 3e3732b..0000000 --- a/vendor/github.com/labstack/echo/group.go +++ /dev/null @@ -1,121 +0,0 @@ -package echo - -import ( - "net/http" - "path" -) - -type ( - // Group is a set of sub-routes for a specified route. It can be used for inner - // routes that share a common middleware or functionality that should be separate - // from the parent echo instance while still inheriting from it. - Group struct { - prefix string - middleware []MiddlewareFunc - echo *Echo - } -) - -// Use implements `Echo#Use()` for sub-routes within the Group. -func (g *Group) Use(middleware ...MiddlewareFunc) { - g.middleware = append(g.middleware, middleware...) - // 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. - for _, p := range []string{"", "/*"} { - g.echo.Any(path.Clean(g.prefix+p), func(c Context) error { - return NotFoundHandler(c) - }, g.middleware...) - } -} - -// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group. -func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodConnect, path, h, m...) -} - -// DELETE implements `Echo#DELETE()` for sub-routes within the Group. -func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodDelete, path, h, m...) -} - -// GET implements `Echo#GET()` for sub-routes within the Group. -func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodGet, path, h, m...) -} - -// HEAD implements `Echo#HEAD()` for sub-routes within the Group. -func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodHead, path, h, m...) -} - -// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group. -func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodOptions, path, h, m...) -} - -// PATCH implements `Echo#PATCH()` for sub-routes within the Group. -func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodPatch, path, h, m...) -} - -// POST implements `Echo#POST()` for sub-routes within the Group. -func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodPost, path, h, m...) -} - -// PUT implements `Echo#PUT()` for sub-routes within the Group. -func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodPut, path, h, m...) -} - -// TRACE implements `Echo#TRACE()` for sub-routes within the Group. -func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route { - return g.Add(http.MethodTrace, path, h, m...) -} - -// Any implements `Echo#Any()` for sub-routes within the Group. -func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { - routes := make([]*Route, len(methods)) - for i, m := range methods { - routes[i] = g.Add(m, path, handler, middleware...) - } - return routes -} - -// Match implements `Echo#Match()` for sub-routes within the Group. -func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route { - routes := make([]*Route, len(methods)) - for i, m := range methods { - routes[i] = g.Add(m, path, handler, middleware...) - } - return routes -} - -// Group creates a new sub-group with prefix and optional sub-group-level middleware. -func (g *Group) Group(prefix string, middleware ...MiddlewareFunc) *Group { - m := make([]MiddlewareFunc, 0, len(g.middleware)+len(middleware)) - m = append(m, g.middleware...) - m = append(m, middleware...) - return g.echo.Group(g.prefix+prefix, m...) -} - -// Static implements `Echo#Static()` for sub-routes within the Group. -func (g *Group) Static(prefix, root string) { - static(g, prefix, root) -} - -// File implements `Echo#File()` for sub-routes within the Group. -func (g *Group) File(path, file string) { - g.echo.File(g.prefix+path, file) -} - -// Add implements `Echo#Add()` for sub-routes within the Group. -func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { - // Combine into a new slice to avoid accidentally passing the same slice for - // multiple routes, which would lead to later add() calls overwriting the - // middleware from earlier calls. - m := make([]MiddlewareFunc, 0, len(g.middleware)+len(middleware)) - m = append(m, g.middleware...) - m = append(m, middleware...) - return g.echo.Add(method, g.prefix+path, handler, m...) -} diff --git a/vendor/github.com/labstack/echo/log.go b/vendor/github.com/labstack/echo/log.go deleted file mode 100644 index 3f8de59..0000000 --- a/vendor/github.com/labstack/echo/log.go +++ /dev/null @@ -1,41 +0,0 @@ -package echo - -import ( - "io" - - "github.com/labstack/gommon/log" -) - -type ( - // Logger defines the logging interface. - Logger interface { - Output() io.Writer - SetOutput(w io.Writer) - Prefix() string - SetPrefix(p string) - Level() log.Lvl - SetLevel(v log.Lvl) - SetHeader(h string) - Print(i ...interface{}) - Printf(format string, args ...interface{}) - Printj(j log.JSON) - Debug(i ...interface{}) - Debugf(format string, args ...interface{}) - Debugj(j log.JSON) - Info(i ...interface{}) - Infof(format string, args ...interface{}) - Infoj(j log.JSON) - Warn(i ...interface{}) - Warnf(format string, args ...interface{}) - Warnj(j log.JSON) - Error(i ...interface{}) - Errorf(format string, args ...interface{}) - Errorj(j log.JSON) - Fatal(i ...interface{}) - Fatalj(j log.JSON) - Fatalf(format string, args ...interface{}) - Panic(i ...interface{}) - Panicj(j log.JSON) - Panicf(format string, args ...interface{}) - } -) diff --git a/vendor/github.com/labstack/echo/middleware/basic_auth.go b/vendor/github.com/labstack/echo/middleware/basic_auth.go deleted file mode 100644 index e6c9632..0000000 --- a/vendor/github.com/labstack/echo/middleware/basic_auth.go +++ /dev/null @@ -1,106 +0,0 @@ -package middleware - -import ( - "encoding/base64" - "strconv" - "strings" - - "github.com/labstack/echo" -) - -type ( - // BasicAuthConfig defines the config for BasicAuth middleware. - BasicAuthConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Validator is a function to validate BasicAuth credentials. - // Required. - Validator BasicAuthValidator - - // Realm is a string to define realm attribute of BasicAuth. - // Default value "Restricted". - Realm string - } - - // BasicAuthValidator defines a function to validate BasicAuth credentials. - BasicAuthValidator func(string, string, echo.Context) (bool, error) -) - -const ( - basic = "basic" - defaultRealm = "Restricted" -) - -var ( - // DefaultBasicAuthConfig is the default BasicAuth middleware config. - DefaultBasicAuthConfig = BasicAuthConfig{ - Skipper: DefaultSkipper, - Realm: defaultRealm, - } -) - -// BasicAuth returns an BasicAuth middleware. -// -// For valid credentials it calls the next handler. -// For missing or invalid credentials, it sends "401 - Unauthorized" response. -func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc { - c := DefaultBasicAuthConfig - c.Validator = fn - return BasicAuthWithConfig(c) -} - -// BasicAuthWithConfig returns an BasicAuth middleware with config. -// See `BasicAuth()`. -func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc { - // Defaults - if config.Validator == nil { - panic("echo: basic-auth middleware requires a validator function") - } - if config.Skipper == nil { - config.Skipper = DefaultBasicAuthConfig.Skipper - } - if config.Realm == "" { - config.Realm = defaultRealm - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - auth := c.Request().Header.Get(echo.HeaderAuthorization) - l := len(basic) - - if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic { - b, err := base64.StdEncoding.DecodeString(auth[l+1:]) - if err != nil { - return err - } - cred := string(b) - for i := 0; i < len(cred); i++ { - if cred[i] == ':' { - // Verify credentials - valid, err := config.Validator(cred[:i], cred[i+1:], c) - if err != nil { - return err - } else if valid { - return next(c) - } - break - } - } - } - - realm := defaultRealm - if config.Realm != defaultRealm { - realm = strconv.Quote(config.Realm) - } - - // Need to return `401` for browsers to pop-up login box. - c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm) - return echo.ErrUnauthorized - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/body_dump.go b/vendor/github.com/labstack/echo/middleware/body_dump.go deleted file mode 100644 index e64e5e1..0000000 --- a/vendor/github.com/labstack/echo/middleware/body_dump.go +++ /dev/null @@ -1,111 +0,0 @@ -package middleware - -import ( - "bufio" - "bytes" - "io" - "io/ioutil" - "net" - "net/http" - - "github.com/labstack/echo" -) - -type ( - // BodyDumpConfig defines the config for BodyDump middleware. - BodyDumpConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Handler receives request and response payload. - // Required. - Handler BodyDumpHandler - } - - // BodyDumpHandler receives the request and response payload. - BodyDumpHandler func(echo.Context, []byte, []byte) - - bodyDumpResponseWriter struct { - io.Writer - http.ResponseWriter - } -) - -var ( - // DefaultBodyDumpConfig is the default BodyDump middleware config. - DefaultBodyDumpConfig = BodyDumpConfig{ - Skipper: DefaultSkipper, - } -) - -// BodyDump returns a BodyDump middleware. -// -// BodyLimit middleware captures the request and response payload and calls the -// registered handler. -func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc { - c := DefaultBodyDumpConfig - c.Handler = handler - return BodyDumpWithConfig(c) -} - -// BodyDumpWithConfig returns a BodyDump middleware with config. -// See: `BodyDump()`. -func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc { - // Defaults - if config.Handler == nil { - panic("echo: body-dump middleware requires a handler function") - } - if config.Skipper == nil { - config.Skipper = DefaultBodyDumpConfig.Skipper - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) (err error) { - if config.Skipper(c) { - return next(c) - } - - // Request - reqBody := []byte{} - if c.Request().Body != nil { // Read - reqBody, _ = ioutil.ReadAll(c.Request().Body) - } - c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset - - // Response - resBody := new(bytes.Buffer) - mw := io.MultiWriter(c.Response().Writer, resBody) - writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer} - c.Response().Writer = writer - - if err = next(c); err != nil { - c.Error(err) - } - - // Callback - config.Handler(c, reqBody, resBody.Bytes()) - - return - } - } -} - -func (w *bodyDumpResponseWriter) WriteHeader(code int) { - w.ResponseWriter.WriteHeader(code) -} - -func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) { - return w.Writer.Write(b) -} - -func (w *bodyDumpResponseWriter) Flush() { - w.ResponseWriter.(http.Flusher).Flush() -} - -func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return w.ResponseWriter.(http.Hijacker).Hijack() -} - -func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool { - return w.ResponseWriter.(http.CloseNotifier).CloseNotify() -} diff --git a/vendor/github.com/labstack/echo/middleware/body_limit.go b/vendor/github.com/labstack/echo/middleware/body_limit.go deleted file mode 100644 index c83f57e..0000000 --- a/vendor/github.com/labstack/echo/middleware/body_limit.go +++ /dev/null @@ -1,117 +0,0 @@ -package middleware - -import ( - "fmt" - "io" - "sync" - - "github.com/labstack/echo" - "github.com/labstack/gommon/bytes" -) - -type ( - // BodyLimitConfig defines the config for BodyLimit middleware. - BodyLimitConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Maximum allowed size for a request body, it can be specified - // as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P. - Limit string `yaml:"limit"` - limit int64 - } - - limitedReader struct { - BodyLimitConfig - reader io.ReadCloser - read int64 - context echo.Context - } -) - -var ( - // DefaultBodyLimitConfig is the default BodyLimit middleware config. - DefaultBodyLimitConfig = BodyLimitConfig{ - Skipper: DefaultSkipper, - } -) - -// BodyLimit returns a BodyLimit middleware. -// -// BodyLimit middleware sets the maximum allowed size for a request body, if the -// size exceeds the configured limit, it sends "413 - Request Entity Too Large" -// response. The BodyLimit is determined based on both `Content-Length` request -// header and actual content read, which makes it super secure. -// Limit can be specified as `4x` or `4xB`, where x is one of the multiple from K, M, -// G, T or P. -func BodyLimit(limit string) echo.MiddlewareFunc { - c := DefaultBodyLimitConfig - c.Limit = limit - return BodyLimitWithConfig(c) -} - -// BodyLimitWithConfig returns a BodyLimit middleware with config. -// See: `BodyLimit()`. -func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultBodyLimitConfig.Skipper - } - - limit, err := bytes.Parse(config.Limit) - if err != nil { - panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit)) - } - config.limit = limit - pool := limitedReaderPool(config) - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - - // Based on content length - if req.ContentLength > config.limit { - return echo.ErrStatusRequestEntityTooLarge - } - - // Based on content read - r := pool.Get().(*limitedReader) - r.Reset(req.Body, c) - defer pool.Put(r) - req.Body = r - - return next(c) - } - } -} - -func (r *limitedReader) Read(b []byte) (n int, err error) { - n, err = r.reader.Read(b) - r.read += int64(n) - if r.read > r.limit { - return n, echo.ErrStatusRequestEntityTooLarge - } - return -} - -func (r *limitedReader) Close() error { - return r.reader.Close() -} - -func (r *limitedReader) Reset(reader io.ReadCloser, context echo.Context) { - r.reader = reader - r.context = context - r.read = 0 -} - -func limitedReaderPool(c BodyLimitConfig) sync.Pool { - return sync.Pool{ - New: func() interface{} { - return &limitedReader{BodyLimitConfig: c} - }, - } -} diff --git a/vendor/github.com/labstack/echo/middleware/compress.go b/vendor/github.com/labstack/echo/middleware/compress.go deleted file mode 100644 index b876009..0000000 --- a/vendor/github.com/labstack/echo/middleware/compress.go +++ /dev/null @@ -1,122 +0,0 @@ -package middleware - -import ( - "bufio" - "compress/gzip" - "io" - "io/ioutil" - "net" - "net/http" - "strings" - - "github.com/labstack/echo" -) - -type ( - // GzipConfig defines the config for Gzip middleware. - GzipConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Gzip compression level. - // Optional. Default value -1. - Level int `yaml:"level"` - } - - gzipResponseWriter struct { - io.Writer - http.ResponseWriter - } -) - -const ( - gzipScheme = "gzip" -) - -var ( - // DefaultGzipConfig is the default Gzip middleware config. - DefaultGzipConfig = GzipConfig{ - Skipper: DefaultSkipper, - Level: -1, - } -) - -// Gzip returns a middleware which compresses HTTP response using gzip compression -// scheme. -func Gzip() echo.MiddlewareFunc { - return GzipWithConfig(DefaultGzipConfig) -} - -// GzipWithConfig return Gzip middleware with config. -// See: `Gzip()`. -func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultGzipConfig.Skipper - } - if config.Level == 0 { - config.Level = DefaultGzipConfig.Level - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - 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 - rw := res.Writer - w, err := gzip.NewWriterLevel(rw, config.Level) - if err != nil { - return err - } - defer func() { - if res.Size == 0 { - if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme { - res.Header().Del(echo.HeaderContentEncoding) - } - // 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(ioutil.Discard) - } - w.Close() - }() - grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw} - res.Writer = grw - } - return next(c) - } - } -} - -func (w *gzipResponseWriter) WriteHeader(code int) { - if code == http.StatusNoContent { // Issue #489 - w.ResponseWriter.Header().Del(echo.HeaderContentEncoding) - } - w.Header().Del(echo.HeaderContentLength) // Issue #444 - w.ResponseWriter.WriteHeader(code) -} - -func (w *gzipResponseWriter) Write(b []byte) (int, error) { - if w.Header().Get(echo.HeaderContentType) == "" { - w.Header().Set(echo.HeaderContentType, http.DetectContentType(b)) - } - return w.Writer.Write(b) -} - -func (w *gzipResponseWriter) Flush() { - w.Writer.(*gzip.Writer).Flush() -} - -func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return w.ResponseWriter.(http.Hijacker).Hijack() -} - -func (w *gzipResponseWriter) CloseNotify() <-chan bool { - return w.ResponseWriter.(http.CloseNotifier).CloseNotify() -} diff --git a/vendor/github.com/labstack/echo/middleware/cors.go b/vendor/github.com/labstack/echo/middleware/cors.go deleted file mode 100644 index d8b633f..0000000 --- a/vendor/github.com/labstack/echo/middleware/cors.go +++ /dev/null @@ -1,143 +0,0 @@ -package middleware - -import ( - "net/http" - "strconv" - "strings" - - "github.com/labstack/echo" -) - -type ( - // CORSConfig defines the config for CORS middleware. - CORSConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // AllowOrigin defines a list of origins that may access the resource. - // Optional. Default value []string{"*"}. - AllowOrigins []string `yaml:"allow_origins"` - - // AllowMethods defines a list methods allowed when accessing the resource. - // This is used in response to a preflight request. - // Optional. Default value DefaultCORSConfig.AllowMethods. - AllowMethods []string `yaml:"allow_methods"` - - // AllowHeaders defines a list of request headers that can be used when - // making the actual request. This is in response to a preflight request. - // Optional. Default value []string{}. - AllowHeaders []string `yaml:"allow_headers"` - - // AllowCredentials indicates whether or not the response to the request - // can be exposed when the credentials flag is true. When used as part of - // a response to a preflight request, this indicates whether or not the - // actual request can be made using credentials. - // Optional. Default value false. - AllowCredentials bool `yaml:"allow_credentials"` - - // ExposeHeaders defines a whitelist headers that clients are allowed to - // access. - // Optional. Default value []string{}. - ExposeHeaders []string `yaml:"expose_headers"` - - // MaxAge indicates how long (in seconds) the results of a preflight request - // can be cached. - // Optional. Default value 0. - MaxAge int `yaml:"max_age"` - } -) - -var ( - // DefaultCORSConfig is the default CORS middleware config. - DefaultCORSConfig = CORSConfig{ - Skipper: DefaultSkipper, - AllowOrigins: []string{"*"}, - AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete}, - } -) - -// CORS returns a Cross-Origin Resource Sharing (CORS) middleware. -// See: https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS -func CORS() echo.MiddlewareFunc { - return CORSWithConfig(DefaultCORSConfig) -} - -// CORSWithConfig returns a CORS middleware with config. -// See: `CORS()`. -func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultCORSConfig.Skipper - } - if len(config.AllowOrigins) == 0 { - config.AllowOrigins = DefaultCORSConfig.AllowOrigins - } - if len(config.AllowMethods) == 0 { - config.AllowMethods = DefaultCORSConfig.AllowMethods - } - - allowMethods := strings.Join(config.AllowMethods, ",") - allowHeaders := strings.Join(config.AllowHeaders, ",") - exposeHeaders := strings.Join(config.ExposeHeaders, ",") - maxAge := strconv.Itoa(config.MaxAge) - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - res := c.Response() - origin := req.Header.Get(echo.HeaderOrigin) - allowOrigin := "" - - // Check allowed origins - for _, o := range config.AllowOrigins { - if o == "*" && config.AllowCredentials { - allowOrigin = origin - break - } - if o == "*" || o == origin { - allowOrigin = o - break - } - } - - // Simple request - if req.Method != http.MethodOptions { - res.Header().Add(echo.HeaderVary, echo.HeaderOrigin) - res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin) - if config.AllowCredentials { - res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true") - } - if exposeHeaders != "" { - res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders) - } - return next(c) - } - - // Preflight request - res.Header().Add(echo.HeaderVary, echo.HeaderOrigin) - res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod) - res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders) - res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin) - res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods) - if config.AllowCredentials { - res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true") - } - if allowHeaders != "" { - res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders) - } else { - h := req.Header.Get(echo.HeaderAccessControlRequestHeaders) - if h != "" { - res.Header().Set(echo.HeaderAccessControlAllowHeaders, h) - } - } - if config.MaxAge > 0 { - res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge) - } - return c.NoContent(http.StatusNoContent) - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/csrf.go b/vendor/github.com/labstack/echo/middleware/csrf.go deleted file mode 100644 index 477872e..0000000 --- a/vendor/github.com/labstack/echo/middleware/csrf.go +++ /dev/null @@ -1,210 +0,0 @@ -package middleware - -import ( - "crypto/subtle" - "errors" - "net/http" - "strings" - "time" - - "github.com/labstack/echo" - "github.com/labstack/gommon/random" -) - -type ( - // CSRFConfig defines the config for CSRF middleware. - CSRFConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // TokenLength is the length of the generated token. - TokenLength uint8 `yaml:"token_length"` - // Optional. Default value 32. - - // TokenLookup is a string in the form of ":" that is used - // to extract token from the request. - // Optional. Default value "header:X-CSRF-Token". - // Possible values: - // - "header:" - // - "form:" - // - "query:" - TokenLookup string `yaml:"token_lookup"` - - // Context key to store generated CSRF token into context. - // Optional. Default value "csrf". - ContextKey string `yaml:"context_key"` - - // Name of the CSRF cookie. This cookie will store CSRF token. - // Optional. Default value "csrf". - CookieName string `yaml:"cookie_name"` - - // Domain of the CSRF cookie. - // Optional. Default value none. - CookieDomain string `yaml:"cookie_domain"` - - // Path of the CSRF cookie. - // Optional. Default value none. - CookiePath string `yaml:"cookie_path"` - - // Max age (in seconds) of the CSRF cookie. - // Optional. Default value 86400 (24hr). - CookieMaxAge int `yaml:"cookie_max_age"` - - // Indicates if CSRF cookie is secure. - // Optional. Default value false. - CookieSecure bool `yaml:"cookie_secure"` - - // Indicates if CSRF cookie is HTTP only. - // Optional. Default value false. - CookieHTTPOnly bool `yaml:"cookie_http_only"` - } - - // csrfTokenExtractor defines a function that takes `echo.Context` and returns - // either a token or an error. - csrfTokenExtractor func(echo.Context) (string, error) -) - -var ( - // DefaultCSRFConfig is the default CSRF middleware config. - DefaultCSRFConfig = CSRFConfig{ - Skipper: DefaultSkipper, - TokenLength: 32, - TokenLookup: "header:" + echo.HeaderXCSRFToken, - ContextKey: "csrf", - CookieName: "_csrf", - CookieMaxAge: 86400, - } -) - -// CSRF returns a Cross-Site Request Forgery (CSRF) middleware. -// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery -func CSRF() echo.MiddlewareFunc { - c := DefaultCSRFConfig - return CSRFWithConfig(c) -} - -// CSRFWithConfig returns a CSRF middleware with config. -// See `CSRF()`. -func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultCSRFConfig.Skipper - } - if config.TokenLength == 0 { - config.TokenLength = DefaultCSRFConfig.TokenLength - } - if config.TokenLookup == "" { - config.TokenLookup = DefaultCSRFConfig.TokenLookup - } - if config.ContextKey == "" { - config.ContextKey = DefaultCSRFConfig.ContextKey - } - if config.CookieName == "" { - config.CookieName = DefaultCSRFConfig.CookieName - } - if config.CookieMaxAge == 0 { - config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge - } - - // Initialize - parts := strings.Split(config.TokenLookup, ":") - extractor := csrfTokenFromHeader(parts[1]) - switch parts[0] { - case "form": - extractor = csrfTokenFromForm(parts[1]) - case "query": - extractor = csrfTokenFromQuery(parts[1]) - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - k, err := c.Cookie(config.CookieName) - token := "" - - // Generate token - if err != nil { - token = random.String(config.TokenLength) - } else { - // Reuse token - token = k.Value - } - - switch req.Method { - case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace: - default: - // Validate token only for requests which are not defined as 'safe' by RFC7231 - clientToken, err := extractor(c) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err.Error()) - } - if !validateCSRFToken(token, clientToken) { - return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token") - } - } - - // Set CSRF cookie - cookie := new(http.Cookie) - cookie.Name = config.CookieName - cookie.Value = token - if config.CookiePath != "" { - cookie.Path = config.CookiePath - } - if config.CookieDomain != "" { - cookie.Domain = config.CookieDomain - } - cookie.Expires = time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second) - cookie.Secure = config.CookieSecure - cookie.HttpOnly = config.CookieHTTPOnly - c.SetCookie(cookie) - - // Store token in the context - c.Set(config.ContextKey, token) - - // Protect clients from caching the response - c.Response().Header().Add(echo.HeaderVary, echo.HeaderCookie) - - return next(c) - } - } -} - -// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the -// provided request header. -func csrfTokenFromHeader(header string) csrfTokenExtractor { - return func(c echo.Context) (string, error) { - return c.Request().Header.Get(header), nil - } -} - -// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the -// provided form parameter. -func csrfTokenFromForm(param string) csrfTokenExtractor { - return func(c echo.Context) (string, error) { - token := c.FormValue(param) - if token == "" { - return "", errors.New("missing csrf token in the form parameter") - } - return token, nil - } -} - -// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the -// provided query parameter. -func csrfTokenFromQuery(param string) csrfTokenExtractor { - return func(c echo.Context) (string, error) { - token := c.QueryParam(param) - if token == "" { - return "", errors.New("missing csrf token in the query string") - } - return token, nil - } -} - -func validateCSRFToken(token, clientToken string) bool { - return subtle.ConstantTimeCompare([]byte(token), []byte(clientToken)) == 1 -} diff --git a/vendor/github.com/labstack/echo/middleware/jwt.go b/vendor/github.com/labstack/echo/middleware/jwt.go deleted file mode 100644 index 051c589..0000000 --- a/vendor/github.com/labstack/echo/middleware/jwt.go +++ /dev/null @@ -1,227 +0,0 @@ -package middleware - -import ( - "fmt" - "net/http" - "reflect" - "strings" - - "github.com/dgrijalva/jwt-go" - "github.com/labstack/echo" -) - -type ( - // JWTConfig defines the config for JWT middleware. - JWTConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // BeforeFunc defines a function which is executed just before the middleware. - BeforeFunc BeforeFunc - - // SuccessHandler defines a function which is executed for a valid token. - SuccessHandler JWTSuccessHandler - - // ErrorHandler defines a function which is executed for an invalid token. - // It may be used to define a custom JWT error. - ErrorHandler JWTErrorHandler - - // Signing key to validate token. - // Required. - SigningKey interface{} - - // Signing method, used to check token signing method. - // Optional. Default value HS256. - SigningMethod string - - // Context key to store user information from the token into context. - // Optional. Default value "user". - ContextKey string - - // Claims are extendable claims data defining token content. - // Optional. Default value jwt.MapClaims - Claims jwt.Claims - - // TokenLookup is a string in the form of ":" that is used - // to extract token from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "cookie:" - TokenLookup string - - // AuthScheme to be used in the Authorization header. - // Optional. Default value "Bearer". - AuthScheme string - - keyFunc jwt.Keyfunc - } - - // JWTSuccessHandler defines a function which is executed for a valid token. - JWTSuccessHandler func(echo.Context) - - // JWTErrorHandler defines a function which is executed for an invalid token. - JWTErrorHandler func(error) error - - jwtExtractor func(echo.Context) (string, error) -) - -// Algorithms -const ( - AlgorithmHS256 = "HS256" -) - -// Errors -var ( - ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt") -) - -var ( - // DefaultJWTConfig is the default JWT auth middleware config. - DefaultJWTConfig = JWTConfig{ - Skipper: DefaultSkipper, - SigningMethod: AlgorithmHS256, - ContextKey: "user", - TokenLookup: "header:" + echo.HeaderAuthorization, - AuthScheme: "Bearer", - Claims: jwt.MapClaims{}, - } -) - -// JWT returns a JSON Web Token (JWT) auth middleware. -// -// For valid token, it sets the user in context and calls next handler. -// For invalid token, it returns "401 - Unauthorized" error. -// For missing token, it returns "400 - Bad Request" error. -// -// See: https://jwt.io/introduction -// See `JWTConfig.TokenLookup` -func JWT(key interface{}) echo.MiddlewareFunc { - c := DefaultJWTConfig - c.SigningKey = key - return JWTWithConfig(c) -} - -// JWTWithConfig returns a JWT auth middleware with config. -// See: `JWT()`. -func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultJWTConfig.Skipper - } - if config.SigningKey == nil { - panic("echo: jwt middleware requires signing key") - } - if config.SigningMethod == "" { - config.SigningMethod = DefaultJWTConfig.SigningMethod - } - if config.ContextKey == "" { - config.ContextKey = DefaultJWTConfig.ContextKey - } - if config.Claims == nil { - config.Claims = DefaultJWTConfig.Claims - } - if config.TokenLookup == "" { - config.TokenLookup = DefaultJWTConfig.TokenLookup - } - if config.AuthScheme == "" { - config.AuthScheme = DefaultJWTConfig.AuthScheme - } - config.keyFunc = func(t *jwt.Token) (interface{}, error) { - // Check the signing method - if t.Method.Alg() != config.SigningMethod { - return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) - } - return config.SigningKey, nil - } - - // Initialize - parts := strings.Split(config.TokenLookup, ":") - extractor := jwtFromHeader(parts[1], config.AuthScheme) - switch parts[0] { - case "query": - extractor = jwtFromQuery(parts[1]) - case "cookie": - extractor = jwtFromCookie(parts[1]) - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - if config.BeforeFunc != nil { - config.BeforeFunc(c) - } - - auth, err := extractor(c) - if err != nil { - if config.ErrorHandler != nil { - return config.ErrorHandler(err) - } - return err - } - token := new(jwt.Token) - // Issue #647, #656 - if _, ok := config.Claims.(jwt.MapClaims); ok { - token, err = jwt.Parse(auth, config.keyFunc) - } else { - t := reflect.ValueOf(config.Claims).Type().Elem() - claims := reflect.New(t).Interface().(jwt.Claims) - token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc) - } - if err == nil && token.Valid { - // Store user information from token into context. - c.Set(config.ContextKey, token) - if config.SuccessHandler != nil { - config.SuccessHandler(c) - } - return next(c) - } - if config.ErrorHandler != nil { - return config.ErrorHandler(err) - } - return &echo.HTTPError{ - Code: http.StatusUnauthorized, - Message: "invalid or expired jwt", - Internal: err, - } - } - } -} - -// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header. -func jwtFromHeader(header string, authScheme string) jwtExtractor { - return func(c echo.Context) (string, error) { - auth := c.Request().Header.Get(header) - l := len(authScheme) - if len(auth) > l+1 && auth[:l] == authScheme { - return auth[l+1:], nil - } - return "", ErrJWTMissing - } -} - -// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string. -func jwtFromQuery(param string) jwtExtractor { - return func(c echo.Context) (string, error) { - token := c.QueryParam(param) - if token == "" { - return "", ErrJWTMissing - } - return token, nil - } -} - -// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie. -func jwtFromCookie(name string) jwtExtractor { - return func(c echo.Context) (string, error) { - cookie, err := c.Cookie(name) - if err != nil { - return "", ErrJWTMissing - } - return cookie.Value, nil - } -} diff --git a/vendor/github.com/labstack/echo/middleware/key_auth.go b/vendor/github.com/labstack/echo/middleware/key_auth.go deleted file mode 100644 index c12f4ca..0000000 --- a/vendor/github.com/labstack/echo/middleware/key_auth.go +++ /dev/null @@ -1,150 +0,0 @@ -package middleware - -import ( - "errors" - "net/http" - "strings" - - "github.com/labstack/echo" -) - -type ( - // KeyAuthConfig defines the config for KeyAuth middleware. - KeyAuthConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // KeyLookup is a string in the form of ":" that is used - // to extract key from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "form:" - KeyLookup string `yaml:"key_lookup"` - - // AuthScheme to be used in the Authorization header. - // Optional. Default value "Bearer". - AuthScheme string - - // Validator is a function to validate key. - // Required. - Validator KeyAuthValidator - } - - // KeyAuthValidator defines a function to validate KeyAuth credentials. - KeyAuthValidator func(string, echo.Context) (bool, error) - - keyExtractor func(echo.Context) (string, error) -) - -var ( - // DefaultKeyAuthConfig is the default KeyAuth middleware config. - DefaultKeyAuthConfig = KeyAuthConfig{ - Skipper: DefaultSkipper, - KeyLookup: "header:" + echo.HeaderAuthorization, - AuthScheme: "Bearer", - } -) - -// KeyAuth returns an KeyAuth middleware. -// -// For valid key it calls the next handler. -// For invalid key, it sends "401 - Unauthorized" response. -// For missing key, it sends "400 - Bad Request" response. -func KeyAuth(fn KeyAuthValidator) echo.MiddlewareFunc { - c := DefaultKeyAuthConfig - c.Validator = fn - return KeyAuthWithConfig(c) -} - -// KeyAuthWithConfig returns an KeyAuth middleware with config. -// See `KeyAuth()`. -func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultKeyAuthConfig.Skipper - } - // Defaults - if config.AuthScheme == "" { - config.AuthScheme = DefaultKeyAuthConfig.AuthScheme - } - if config.KeyLookup == "" { - config.KeyLookup = DefaultKeyAuthConfig.KeyLookup - } - if config.Validator == nil { - panic("echo: key-auth middleware requires a validator function") - } - - // Initialize - parts := strings.Split(config.KeyLookup, ":") - extractor := keyFromHeader(parts[1], config.AuthScheme) - switch parts[0] { - case "query": - extractor = keyFromQuery(parts[1]) - case "form": - extractor = keyFromForm(parts[1]) - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - // Extract and verify key - key, err := extractor(c) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err.Error()) - } - valid, err := config.Validator(key, c) - if err != nil { - return err - } else if valid { - return next(c) - } - - return echo.ErrUnauthorized - } - } -} - -// keyFromHeader returns a `keyExtractor` that extracts key from the request header. -func keyFromHeader(header string, authScheme string) keyExtractor { - return func(c echo.Context) (string, error) { - auth := c.Request().Header.Get(header) - if auth == "" { - return "", errors.New("missing key in request header") - } - if header == echo.HeaderAuthorization { - l := len(authScheme) - if len(auth) > l+1 && auth[:l] == authScheme { - return auth[l+1:], nil - } - return "", errors.New("invalid key in the request header") - } - return auth, nil - } -} - -// keyFromQuery returns a `keyExtractor` that extracts key from the query string. -func keyFromQuery(param string) keyExtractor { - return func(c echo.Context) (string, error) { - key := c.QueryParam(param) - if key == "" { - return "", errors.New("missing key in the query string") - } - return key, nil - } -} - -// keyFromForm returns a `keyExtractor` that extracts key from the form. -func keyFromForm(param string) keyExtractor { - return func(c echo.Context) (string, error) { - key := c.FormValue(param) - if key == "" { - return "", errors.New("missing key in the form") - } - return key, nil - } -} diff --git a/vendor/github.com/labstack/echo/middleware/logger.go b/vendor/github.com/labstack/echo/middleware/logger.go deleted file mode 100644 index dab3ef7..0000000 --- a/vendor/github.com/labstack/echo/middleware/logger.go +++ /dev/null @@ -1,217 +0,0 @@ -package middleware - -import ( - "bytes" - "io" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/labstack/echo" - "github.com/labstack/gommon/color" - "github.com/valyala/fasttemplate" -) - -type ( - // LoggerConfig defines the config for Logger middleware. - LoggerConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Tags to constructed the logger format. - // - // - time_unix - // - time_unix_nano - // - time_rfc3339 - // - time_rfc3339_nano - // - time_custom - // - id (Request ID) - // - remote_ip - // - uri - // - host - // - method - // - path - // - protocol - // - referer - // - user_agent - // - status - // - error - // - latency (In nanoseconds) - // - latency_human (Human readable) - // - bytes_in (Bytes received) - // - bytes_out (Bytes sent) - // - header: - // - query: - // - form: - // - // Example "${remote_ip} ${status}" - // - // Optional. Default value DefaultLoggerConfig.Format. - Format string `yaml:"format"` - - // Optional. Default value DefaultLoggerConfig.CustomTimeFormat. - CustomTimeFormat string `yaml:"custom_time_format"` - - // Output is a writer where logs in JSON format are written. - // Optional. Default value os.Stdout. - Output io.Writer - - template *fasttemplate.Template - colorer *color.Color - pool *sync.Pool - } -) - -var ( - // DefaultLoggerConfig is the default Logger middleware config. - DefaultLoggerConfig = LoggerConfig{ - Skipper: DefaultSkipper, - Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` + - `"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` + - `"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` + - `,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n", - CustomTimeFormat: "2006-01-02 15:04:05.00000", - Output: os.Stdout, - colorer: color.New(), - } -) - -// Logger returns a middleware that logs HTTP requests. -func Logger() echo.MiddlewareFunc { - return LoggerWithConfig(DefaultLoggerConfig) -} - -// LoggerWithConfig returns a Logger middleware with config. -// See: `Logger()`. -func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultLoggerConfig.Skipper - } - if config.Format == "" { - config.Format = DefaultLoggerConfig.Format - } - if config.Output == nil { - config.Output = DefaultLoggerConfig.Output - } - - config.template = fasttemplate.New(config.Format, "${", "}") - config.colorer = color.New() - config.colorer.SetOutput(config.Output) - config.pool = &sync.Pool{ - New: func() interface{} { - return bytes.NewBuffer(make([]byte, 256)) - }, - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) (err error) { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - res := c.Response() - start := time.Now() - if err = next(c); err != nil { - c.Error(err) - } - stop := time.Now() - buf := config.pool.Get().(*bytes.Buffer) - buf.Reset() - defer config.pool.Put(buf) - - if _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { - switch tag { - case "time_unix": - return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10)) - case "time_unix_nano": - return buf.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10)) - case "time_rfc3339": - return buf.WriteString(time.Now().Format(time.RFC3339)) - case "time_rfc3339_nano": - return buf.WriteString(time.Now().Format(time.RFC3339Nano)) - case "time_custom": - return buf.WriteString(time.Now().Format(config.CustomTimeFormat)) - case "id": - id := req.Header.Get(echo.HeaderXRequestID) - if id == "" { - id = res.Header().Get(echo.HeaderXRequestID) - } - return buf.WriteString(id) - case "remote_ip": - return buf.WriteString(c.RealIP()) - case "host": - return buf.WriteString(req.Host) - case "uri": - return buf.WriteString(req.RequestURI) - case "method": - return buf.WriteString(req.Method) - case "path": - p := req.URL.Path - if p == "" { - p = "/" - } - return buf.WriteString(p) - case "protocol": - return buf.WriteString(req.Proto) - case "referer": - return buf.WriteString(req.Referer()) - case "user_agent": - return buf.WriteString(req.UserAgent()) - case "status": - n := res.Status - s := config.colorer.Green(n) - switch { - case n >= 500: - s = config.colorer.Red(n) - case n >= 400: - s = config.colorer.Yellow(n) - case n >= 300: - s = config.colorer.Cyan(n) - } - return buf.WriteString(s) - case "error": - if err != nil { - return buf.WriteString(err.Error()) - } - case "latency": - l := stop.Sub(start) - return buf.WriteString(strconv.FormatInt(int64(l), 10)) - case "latency_human": - return buf.WriteString(stop.Sub(start).String()) - case "bytes_in": - cl := req.Header.Get(echo.HeaderContentLength) - if cl == "" { - cl = "0" - } - return buf.WriteString(cl) - case "bytes_out": - return buf.WriteString(strconv.FormatInt(res.Size, 10)) - default: - switch { - case strings.HasPrefix(tag, "header:"): - return buf.Write([]byte(c.Request().Header.Get(tag[7:]))) - case strings.HasPrefix(tag, "query:"): - return buf.Write([]byte(c.QueryParam(tag[6:]))) - case strings.HasPrefix(tag, "form:"): - return buf.Write([]byte(c.FormValue(tag[5:]))) - case strings.HasPrefix(tag, "cookie:"): - cookie, err := c.Cookie(tag[7:]) - if err == nil { - return buf.Write([]byte(cookie.Value)) - } - } - } - return 0, nil - }); err != nil { - return - } - - _, err = config.Output.Write(buf.Bytes()) - return - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/method_override.go b/vendor/github.com/labstack/echo/middleware/method_override.go deleted file mode 100644 index 0a6103b..0000000 --- a/vendor/github.com/labstack/echo/middleware/method_override.go +++ /dev/null @@ -1,92 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/labstack/echo" -) - -type ( - // MethodOverrideConfig defines the config for MethodOverride middleware. - MethodOverrideConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Getter is a function that gets overridden method from the request. - // Optional. Default values MethodFromHeader(echo.HeaderXHTTPMethodOverride). - Getter MethodOverrideGetter - } - - // MethodOverrideGetter is a function that gets overridden method from the request - MethodOverrideGetter func(echo.Context) string -) - -var ( - // DefaultMethodOverrideConfig is the default MethodOverride middleware config. - DefaultMethodOverrideConfig = MethodOverrideConfig{ - Skipper: DefaultSkipper, - Getter: MethodFromHeader(echo.HeaderXHTTPMethodOverride), - } -) - -// MethodOverride returns a MethodOverride middleware. -// MethodOverride middleware checks for the overridden method from the request and -// uses it instead of the original method. -// -// For security reasons, only `POST` method can be overridden. -func MethodOverride() echo.MiddlewareFunc { - return MethodOverrideWithConfig(DefaultMethodOverrideConfig) -} - -// MethodOverrideWithConfig returns a MethodOverride middleware with config. -// See: `MethodOverride()`. -func MethodOverrideWithConfig(config MethodOverrideConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultMethodOverrideConfig.Skipper - } - if config.Getter == nil { - config.Getter = DefaultMethodOverrideConfig.Getter - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - if req.Method == http.MethodPost { - m := config.Getter(c) - if m != "" { - req.Method = m - } - } - return next(c) - } - } -} - -// MethodFromHeader is a `MethodOverrideGetter` that gets overridden method from -// the request header. -func MethodFromHeader(header string) MethodOverrideGetter { - return func(c echo.Context) string { - return c.Request().Header.Get(header) - } -} - -// MethodFromForm is a `MethodOverrideGetter` that gets overridden method from the -// form parameter. -func MethodFromForm(param string) MethodOverrideGetter { - return func(c echo.Context) string { - return c.FormValue(param) - } -} - -// MethodFromQuery is a `MethodOverrideGetter` that gets overridden method from -// the query parameter. -func MethodFromQuery(param string) MethodOverrideGetter { - return func(c echo.Context) string { - return c.QueryParam(param) - } -} diff --git a/vendor/github.com/labstack/echo/middleware/middleware.go b/vendor/github.com/labstack/echo/middleware/middleware.go deleted file mode 100644 index febbdae..0000000 --- a/vendor/github.com/labstack/echo/middleware/middleware.go +++ /dev/null @@ -1,38 +0,0 @@ -package middleware - -import ( - "regexp" - "strconv" - "strings" - - "github.com/labstack/echo" -) - -type ( - // Skipper defines a function to skip middleware. Returning true skips processing - // the middleware. - Skipper func(echo.Context) bool - - // BeforeFunc defines a function which is executed just before the middleware. - BeforeFunc func(echo.Context) -) - -func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer { - groups := pattern.FindAllStringSubmatch(input, -1) - if groups == nil { - return nil - } - values := groups[0][1:] - replace := make([]string, 2*len(values)) - for i, v := range values { - j := 2 * i - replace[j] = "$" + strconv.Itoa(i+1) - replace[j+1] = v - } - return strings.NewReplacer(replace...) -} - -// DefaultSkipper returns false which processes the middleware. -func DefaultSkipper(echo.Context) bool { - return false -} diff --git a/vendor/github.com/labstack/echo/middleware/proxy.go b/vendor/github.com/labstack/echo/middleware/proxy.go deleted file mode 100644 index 1789c75..0000000 --- a/vendor/github.com/labstack/echo/middleware/proxy.go +++ /dev/null @@ -1,258 +0,0 @@ -package middleware - -import ( - "fmt" - "io" - "math/rand" - "net" - "net/http" - "net/url" - "regexp" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/labstack/echo" -) - -// TODO: Handle TLS proxy - -type ( - // ProxyConfig defines the config for Proxy middleware. - ProxyConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Balancer defines a load balancing technique. - // Required. - Balancer ProxyBalancer - - // Rewrite defines URL path rewrite rules. The values captured in asterisk can be - // retrieved by index e.g. $1, $2 and so on. - // Examples: - // "/old": "/new", - // "/api/*": "/$1", - // "/js/*": "/public/javascripts/$1", - // "/users/*/orders/*": "/user/$1/order/$2", - Rewrite map[string]string - - // Context key to store selected ProxyTarget into context. - // Optional. Default value "target". - ContextKey string - - // To customize the transport to remote. - // Examples: If custom TLS certificates are required. - Transport http.RoundTripper - - rewriteRegex map[*regexp.Regexp]string - } - - // ProxyTarget defines the upstream target. - ProxyTarget struct { - Name string - URL *url.URL - Meta echo.Map - } - - // ProxyBalancer defines an interface to implement a load balancing technique. - ProxyBalancer interface { - AddTarget(*ProxyTarget) bool - RemoveTarget(string) bool - Next(echo.Context) *ProxyTarget - } - - commonBalancer struct { - targets []*ProxyTarget - mutex sync.RWMutex - } - - // RandomBalancer implements a random load balancing technique. - randomBalancer struct { - *commonBalancer - random *rand.Rand - } - - // RoundRobinBalancer implements a round-robin load balancing technique. - roundRobinBalancer struct { - *commonBalancer - i uint32 - } -) - -var ( - // DefaultProxyConfig is the default Proxy middleware config. - DefaultProxyConfig = ProxyConfig{ - Skipper: DefaultSkipper, - ContextKey: "target", - } -) - -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.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err)) - return - } - defer in.Close() - - out, err := net.Dial("tcp", t.URL.Host) - if err != nil { - he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err)) - c.Error(he) - return - } - defer out.Close() - - // Write header - err = r.Write(out) - if err != nil { - he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err)) - c.Error(he) - return - } - - errCh := make(chan error, 2) - cp := func(dst io.Writer, src io.Reader) { - _, err = io.Copy(dst, src) - errCh <- err - } - - go cp(out, in) - go cp(in, out) - err = <-errCh - if err != nil && err != io.EOF { - c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err) - } - }) -} - -// NewRandomBalancer returns a random proxy balancer. -func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer { - b := &randomBalancer{commonBalancer: new(commonBalancer)} - b.targets = targets - return b -} - -// NewRoundRobinBalancer returns a round-robin proxy balancer. -func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer { - b := &roundRobinBalancer{commonBalancer: new(commonBalancer)} - b.targets = targets - return b -} - -// AddTarget adds an upstream target to the list. -func (b *commonBalancer) AddTarget(target *ProxyTarget) bool { - 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. -func (b *commonBalancer) RemoveTarget(name string) bool { - b.mutex.Lock() - defer b.mutex.Unlock() - for i, t := range b.targets { - if t.Name == name { - b.targets = append(b.targets[:i], b.targets[i+1:]...) - return true - } - } - return false -} - -// Next randomly returns an upstream target. -func (b *randomBalancer) Next(c echo.Context) *ProxyTarget { - if b.random == nil { - b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) - } - 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. -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 -} - -// Proxy returns a Proxy middleware. -// -// Proxy middleware forwards the request to upstream server using a configured load balancing technique. -func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc { - c := DefaultProxyConfig - c.Balancer = balancer - return ProxyWithConfig(c) -} - -// ProxyWithConfig returns a Proxy middleware with config. -// See: `Proxy()` -func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultLoggerConfig.Skipper - } - if config.Balancer == nil { - panic("echo: proxy middleware requires balancer") - } - config.rewriteRegex = map[*regexp.Regexp]string{} - - // Initialize - for k, v := range config.Rewrite { - k = strings.Replace(k, "*", "(\\S*)", -1) - config.rewriteRegex[regexp.MustCompile(k)] = v - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) (err error) { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - res := c.Response() - tgt := config.Balancer.Next(c) - c.Set(config.ContextKey, tgt) - - // Rewrite - for k, v := range config.rewriteRegex { - replacer := captureTokens(k, req.URL.Path) - if replacer != nil { - req.URL.Path = replacer.Replace(v) - } - } - - // Fix header - if req.Header.Get(echo.HeaderXRealIP) == "" { - req.Header.Set(echo.HeaderXRealIP, c.RealIP()) - } - if req.Header.Get(echo.HeaderXForwardedProto) == "" { - req.Header.Set(echo.HeaderXForwardedProto, c.Scheme()) - } - if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" { // For HTTP, it is automatically set by Go HTTP reverse proxy. - 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) - } - - return - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/proxy_1_11.go b/vendor/github.com/labstack/echo/middleware/proxy_1_11.go deleted file mode 100644 index 193015a..0000000 --- a/vendor/github.com/labstack/echo/middleware/proxy_1_11.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build go1.11 - -package middleware - -import ( - "fmt" - "net/http" - "net/http/httputil" - - "github.com/labstack/echo" -) - -func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { - proxy := httputil.NewSingleHostReverseProxy(tgt.URL) - proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) { - desc := tgt.URL.String() - if tgt.Name != "" { - desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String()) - } - c.Logger().Errorf("remote %s unreachable, could not forward: %v", desc, err) - c.Error(echo.NewHTTPError(http.StatusServiceUnavailable)) - } - proxy.Transport = config.Transport - return proxy -} diff --git a/vendor/github.com/labstack/echo/middleware/proxy_1_11_n.go b/vendor/github.com/labstack/echo/middleware/proxy_1_11_n.go deleted file mode 100644 index d4620e2..0000000 --- a/vendor/github.com/labstack/echo/middleware/proxy_1_11_n.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !go1.11 - -package middleware - -import ( - "github.com/labstack/echo" - "net/http" - "net/http/httputil" -) - -func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler { - return httputil.NewSingleHostReverseProxy(t.URL) -} diff --git a/vendor/github.com/labstack/echo/middleware/recover.go b/vendor/github.com/labstack/echo/middleware/recover.go deleted file mode 100644 index 2a42c5b..0000000 --- a/vendor/github.com/labstack/echo/middleware/recover.go +++ /dev/null @@ -1,81 +0,0 @@ -package middleware - -import ( - "fmt" - "runtime" - - "github.com/labstack/echo" -) - -type ( - // RecoverConfig defines the config for Recover middleware. - RecoverConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Size of the stack to be printed. - // Optional. Default value 4KB. - StackSize int `yaml:"stack_size"` - - // DisableStackAll disables formatting stack traces of all other goroutines - // into buffer after the trace for the current goroutine. - // Optional. Default value false. - DisableStackAll bool `yaml:"disable_stack_all"` - - // DisablePrintStack disables printing stack trace. - // Optional. Default value as false. - DisablePrintStack bool `yaml:"disable_print_stack"` - } -) - -var ( - // DefaultRecoverConfig is the default Recover middleware config. - DefaultRecoverConfig = RecoverConfig{ - Skipper: DefaultSkipper, - StackSize: 4 << 10, // 4 KB - DisableStackAll: false, - DisablePrintStack: false, - } -) - -// Recover returns a middleware which recovers from panics anywhere in the chain -// and handles the control to the centralized HTTPErrorHandler. -func Recover() echo.MiddlewareFunc { - return RecoverWithConfig(DefaultRecoverConfig) -} - -// RecoverWithConfig returns a Recover middleware with config. -// See: `Recover()`. -func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultRecoverConfig.Skipper - } - if config.StackSize == 0 { - config.StackSize = DefaultRecoverConfig.StackSize - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - defer func() { - if r := recover(); r != nil { - err, ok := r.(error) - if !ok { - err = fmt.Errorf("%v", r) - } - stack := make([]byte, config.StackSize) - length := runtime.Stack(stack, !config.DisableStackAll) - if !config.DisablePrintStack { - c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length]) - } - c.Error(err) - } - }() - return next(c) - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/redirect.go b/vendor/github.com/labstack/echo/middleware/redirect.go deleted file mode 100644 index 422263d..0000000 --- a/vendor/github.com/labstack/echo/middleware/redirect.go +++ /dev/null @@ -1,153 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/labstack/echo" -) - -// RedirectConfig defines the config for Redirect middleware. -type RedirectConfig struct { - // Skipper defines a function to skip middleware. - Skipper - - // Status code to be used when redirecting the request. - // Optional. Default value http.StatusMovedPermanently. - Code int `yaml:"code"` -} - -// redirectLogic represents a function that given a scheme, host and uri -// can both: 1) determine if redirect is needed (will set ok accordingly) and -// 2) return the appropriate redirect url. -type redirectLogic func(scheme, host, uri string) (ok bool, url string) - -const www = "www" - -// DefaultRedirectConfig is the default Redirect middleware config. -var DefaultRedirectConfig = RedirectConfig{ - Skipper: DefaultSkipper, - Code: http.StatusMovedPermanently, -} - -// HTTPSRedirect redirects http requests to https. -// For example, http://labstack.com will be redirect to https://labstack.com. -// -// Usage `Echo#Pre(HTTPSRedirect())` -func HTTPSRedirect() echo.MiddlewareFunc { - return HTTPSRedirectWithConfig(DefaultRedirectConfig) -} - -// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config. -// See `HTTPSRedirect()`. -func HTTPSRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc { - return redirect(config, func(scheme, host, uri string) (ok bool, url string) { - if ok = scheme != "https"; ok { - url = "https://" + host + uri - } - return - }) -} - -// HTTPSWWWRedirect redirects http requests to https www. -// For example, http://labstack.com will be redirect to https://www.labstack.com. -// -// Usage `Echo#Pre(HTTPSWWWRedirect())` -func HTTPSWWWRedirect() echo.MiddlewareFunc { - return HTTPSWWWRedirectWithConfig(DefaultRedirectConfig) -} - -// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config. -// See `HTTPSWWWRedirect()`. -func HTTPSWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc { - return redirect(config, func(scheme, host, uri string) (ok bool, url string) { - if ok = scheme != "https" && host[:3] != www; ok { - url = "https://www." + host + uri - } - return - }) -} - -// HTTPSNonWWWRedirect redirects http requests to https non www. -// For example, http://www.labstack.com will be redirect to https://labstack.com. -// -// Usage `Echo#Pre(HTTPSNonWWWRedirect())` -func HTTPSNonWWWRedirect() echo.MiddlewareFunc { - return HTTPSNonWWWRedirectWithConfig(DefaultRedirectConfig) -} - -// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config. -// See `HTTPSNonWWWRedirect()`. -func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc { - return redirect(config, func(scheme, host, uri string) (ok bool, url string) { - if ok = scheme != "https"; ok { - if host[:3] == www { - host = host[4:] - } - url = "https://" + host + uri - } - return - }) -} - -// WWWRedirect redirects non www requests to www. -// For example, http://labstack.com will be redirect to http://www.labstack.com. -// -// Usage `Echo#Pre(WWWRedirect())` -func WWWRedirect() echo.MiddlewareFunc { - return WWWRedirectWithConfig(DefaultRedirectConfig) -} - -// WWWRedirectWithConfig returns an HTTPSRedirect middleware with config. -// See `WWWRedirect()`. -func WWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc { - return redirect(config, func(scheme, host, uri string) (ok bool, url string) { - if ok = host[:3] != www; ok { - url = scheme + "://www." + host + uri - } - return - }) -} - -// NonWWWRedirect redirects www requests to non www. -// For example, http://www.labstack.com will be redirect to http://labstack.com. -// -// Usage `Echo#Pre(NonWWWRedirect())` -func NonWWWRedirect() echo.MiddlewareFunc { - return NonWWWRedirectWithConfig(DefaultRedirectConfig) -} - -// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config. -// See `NonWWWRedirect()`. -func NonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc { - return redirect(config, func(scheme, host, uri string) (ok bool, url string) { - if ok = host[:3] == www; ok { - url = scheme + "://" + host[4:] + uri - } - return - }) -} - -func redirect(config RedirectConfig, cb redirectLogic) echo.MiddlewareFunc { - if config.Skipper == nil { - config.Skipper = DefaultTrailingSlashConfig.Skipper - } - if config.Code == 0 { - config.Code = DefaultRedirectConfig.Code - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req, scheme := c.Request(), c.Scheme() - host := req.Host - if ok, url := cb(scheme, host, req.RequestURI); ok { - return c.Redirect(config.Code, url) - } - - return next(c) - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/request_id.go b/vendor/github.com/labstack/echo/middleware/request_id.go deleted file mode 100644 index f376c29..0000000 --- a/vendor/github.com/labstack/echo/middleware/request_id.go +++ /dev/null @@ -1,64 +0,0 @@ -package middleware - -import ( - "github.com/labstack/echo" - "github.com/labstack/gommon/random" -) - -type ( - // RequestIDConfig defines the config for RequestID middleware. - RequestIDConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Generator defines a function to generate an ID. - // Optional. Default value random.String(32). - Generator func() string - } -) - -var ( - // DefaultRequestIDConfig is the default RequestID middleware config. - DefaultRequestIDConfig = RequestIDConfig{ - Skipper: DefaultSkipper, - Generator: generator, - } -) - -// RequestID returns a X-Request-ID middleware. -func RequestID() echo.MiddlewareFunc { - return RequestIDWithConfig(DefaultRequestIDConfig) -} - -// RequestIDWithConfig returns a X-Request-ID middleware with config. -func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultRequestIDConfig.Skipper - } - if config.Generator == nil { - config.Generator = generator - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - res := c.Response() - rid := req.Header.Get(echo.HeaderXRequestID) - if rid == "" { - rid = config.Generator() - } - res.Header().Set(echo.HeaderXRequestID, rid) - - return next(c) - } - } -} - -func generator() string { - return random.String(32) -} diff --git a/vendor/github.com/labstack/echo/middleware/rewrite.go b/vendor/github.com/labstack/echo/middleware/rewrite.go deleted file mode 100644 index 1b32212..0000000 --- a/vendor/github.com/labstack/echo/middleware/rewrite.go +++ /dev/null @@ -1,84 +0,0 @@ -package middleware - -import ( - "regexp" - "strings" - - "github.com/labstack/echo" -) - -type ( - // RewriteConfig defines the config for Rewrite middleware. - RewriteConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // Rules defines the URL path rewrite rules. The values captured in asterisk can be - // retrieved by index e.g. $1, $2 and so on. - // Example: - // "/old": "/new", - // "/api/*": "/$1", - // "/js/*": "/public/javascripts/$1", - // "/users/*/orders/*": "/user/$1/order/$2", - // Required. - Rules map[string]string `yaml:"rules"` - - rulesRegex map[*regexp.Regexp]string - } -) - -var ( - // DefaultRewriteConfig is the default Rewrite middleware config. - DefaultRewriteConfig = RewriteConfig{ - Skipper: DefaultSkipper, - } -) - -// Rewrite returns a Rewrite middleware. -// -// Rewrite middleware rewrites the URL path based on the provided rules. -func Rewrite(rules map[string]string) echo.MiddlewareFunc { - c := DefaultRewriteConfig - c.Rules = rules - return RewriteWithConfig(c) -} - -// RewriteWithConfig returns a Rewrite middleware with config. -// See: `Rewrite()`. -func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc { - // Defaults - if config.Rules == nil { - panic("echo: rewrite middleware requires url path rewrite rules") - } - if config.Skipper == nil { - config.Skipper = DefaultBodyDumpConfig.Skipper - } - config.rulesRegex = map[*regexp.Regexp]string{} - - // Initialize - for k, v := range config.Rules { - k = strings.Replace(k, "*", "(.*)", -1) - k = k + "$" - config.rulesRegex[regexp.MustCompile(k)] = v - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) (err error) { - if config.Skipper(c) { - return next(c) - } - - req := c.Request() - - // Rewrite - for k, v := range config.rulesRegex { - replacer := captureTokens(k, req.URL.Path) - if replacer != nil { - req.URL.Path = replacer.Replace(v) - break - } - } - return next(c) - } - } -} diff --git a/vendor/github.com/labstack/echo/middleware/secure.go b/vendor/github.com/labstack/echo/middleware/secure.go deleted file mode 100644 index 188c0c4..0000000 --- a/vendor/github.com/labstack/echo/middleware/secure.go +++ /dev/null @@ -1,116 +0,0 @@ -package middleware - -import ( - "fmt" - - "github.com/labstack/echo" -) - -type ( - // SecureConfig defines the config for Secure middleware. - SecureConfig struct { - // Skipper defines a function to skip middleware. - Skipper Skipper - - // XSSProtection provides protection against cross-site scripting attack (XSS) - // by setting the `X-XSS-Protection` header. - // Optional. Default value "1; mode=block". - XSSProtection string `yaml:"xss_protection"` - - // ContentTypeNosniff provides protection against overriding Content-Type - // header by setting the `X-Content-Type-Options` header. - // Optional. Default value "nosniff". - ContentTypeNosniff string `yaml:"content_type_nosniff"` - - // XFrameOptions can be used to indicate whether or not a browser should - // be allowed to render a page in a ,