diff options
author | Tamal Saha <tamal@appscode.com> | 2019-08-23 09:40:30 -0700 |
---|---|---|
committer | techknowlogick <techknowlogick@gitea.io> | 2019-08-23 12:40:29 -0400 |
commit | 171b3598778a1ecd0a921c71ed6755bfef68f7f0 (patch) | |
tree | 02857629ef9e8e26ee0ee559153f803f77b588b7 /vendor/gitea.com/macaron | |
parent | ca6fb004ac50fc924861112403895d637c6a2d1d (diff) | |
download | gitea-171b3598778a1ecd0a921c71ed6755bfef68f7f0.tar.gz gitea-171b3598778a1ecd0a921c71ed6755bfef68f7f0.zip |
Use gitea forked macaron (#7933)
Signed-off-by: Tamal Saha <tamal@appscode.com>
Diffstat (limited to 'vendor/gitea.com/macaron')
107 files changed, 12861 insertions, 0 deletions
diff --git a/vendor/gitea.com/macaron/binding/.drone.yml b/vendor/gitea.com/macaron/binding/.drone.yml new file mode 100644 index 0000000000..9baf4f1cf7 --- /dev/null +++ b/vendor/gitea.com/macaron/binding/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/binding/.gitignore b/vendor/gitea.com/macaron/binding/.gitignore new file mode 100644 index 0000000000..485dee64bc --- /dev/null +++ b/vendor/gitea.com/macaron/binding/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/gitea.com/macaron/binding/LICENSE b/vendor/gitea.com/macaron/binding/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/binding/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/binding/README.md b/vendor/gitea.com/macaron/binding/README.md new file mode 100644 index 0000000000..aeaa571939 --- /dev/null +++ b/vendor/gitea.com/macaron/binding/README.md @@ -0,0 +1,20 @@ +# binding [![Build Status](https://travis-ci.org/go-macaron/binding.svg?branch=master)](https://travis-ci.org/go-macaron/binding) [![Sourcegraph](https://sourcegraph.com/github.com/go-macaron/binding/-/badge.svg)](https://sourcegraph.com/github.com/go-macaron/binding?badge) + +Middleware binding provides request data binding and validation for [Macaron](https://github.com/go-macaron/macaron). + +### Installation + + go get github.com/go-macaron/binding + +## Getting Help + +- [API Reference](https://gowalker.org/github.com/go-macaron/binding) +- [Documentation](http://go-macaron.com/docs/middlewares/binding) + +## Credits + +This package is a modified version of [martini-contrib/binding](https://github.com/martini-contrib/binding). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/binding/binding.go b/vendor/gitea.com/macaron/binding/binding.go new file mode 100644 index 0000000000..633d836b64 --- /dev/null +++ b/vendor/gitea.com/macaron/binding/binding.go @@ -0,0 +1,761 @@ +// Copyright 2014 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package binding is a middleware that provides request data binding and validation for Macaron. +package binding + +import ( + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + "gitea.com/macaron/macaron" + "github.com/unknwon/com" +) + +const _VERSION = "0.6.0" + +func Version() string { + return _VERSION +} + +func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) { + contentType := ctx.Req.Header.Get("Content-Type") + if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || len(contentType) > 0 { + switch { + case strings.Contains(contentType, "form-urlencoded"): + ctx.Invoke(Form(obj, ifacePtr...)) + case strings.Contains(contentType, "multipart/form-data"): + ctx.Invoke(MultipartForm(obj, ifacePtr...)) + case strings.Contains(contentType, "json"): + ctx.Invoke(Json(obj, ifacePtr...)) + default: + var errors Errors + if contentType == "" { + errors.Add([]string{}, ERR_CONTENT_TYPE, "Empty Content-Type") + } else { + errors.Add([]string{}, ERR_CONTENT_TYPE, "Unsupported Content-Type") + } + ctx.Map(errors) + ctx.Map(obj) // Map a fake struct so handler won't panic. + } + } else { + ctx.Invoke(Form(obj, ifacePtr...)) + } +} + +const ( + _JSON_CONTENT_TYPE = "application/json; charset=utf-8" + STATUS_UNPROCESSABLE_ENTITY = 422 +) + +// errorHandler simply counts the number of errors in the +// context and, if more than 0, writes a response with an +// error code and a JSON payload describing the errors. +// The response will have a JSON content-type. +// Middleware remaining on the stack will not even see the request +// if, by this point, there are any errors. +// This is a "default" handler, of sorts, and you are +// welcome to use your own instead. The Bind middleware +// invokes this automatically for convenience. +func errorHandler(errs Errors, rw http.ResponseWriter) { + if len(errs) > 0 { + rw.Header().Set("Content-Type", _JSON_CONTENT_TYPE) + if errs.Has(ERR_DESERIALIZATION) { + rw.WriteHeader(http.StatusBadRequest) + } else if errs.Has(ERR_CONTENT_TYPE) { + rw.WriteHeader(http.StatusUnsupportedMediaType) + } else { + rw.WriteHeader(STATUS_UNPROCESSABLE_ENTITY) + } + errOutput, _ := json.Marshal(errs) + rw.Write(errOutput) + return + } +} + +// Bind wraps up the functionality of the Form and Json middleware +// according to the Content-Type and verb of the request. +// A Content-Type is required for POST and PUT requests. +// Bind invokes the ErrorHandler middleware to bail out if errors +// occurred. If you want to perform your own error handling, use +// Form or Json middleware directly. An interface pointer can +// be added as a second argument in order to map the struct to +// a specific interface. +func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + bind(ctx, obj, ifacePtr...) + if handler, ok := obj.(ErrorHandler); ok { + ctx.Invoke(handler.Error) + } else { + ctx.Invoke(errorHandler) + } + } +} + +// BindIgnErr will do the exactly same thing as Bind but without any +// error handling, which user has freedom to deal with them. +// This allows user take advantages of validation. +func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + bind(ctx, obj, ifacePtr...) + } +} + +// Form is middleware to deserialize form-urlencoded data from the request. +// It gets data from the form-urlencoded body, if present, or from the +// query string. It uses the http.Request.ParseForm() method +// to perform deserialization, then reflection is used to map each field +// into the struct with the proper type. Structs with primitive slice types +// (bool, float, int, string) can support deserialization of repeated form +// keys, for example: key=val1&key=val2&key=val3 +// An interface pointer can be added as a second argument in order +// to map the struct to a specific interface. +func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + var errors Errors + + ensureNotPointer(formStruct) + formStruct := reflect.New(reflect.TypeOf(formStruct)) + parseErr := ctx.Req.ParseForm() + + // Format validation of the request body or the URL would add considerable overhead, + // and ParseForm does not complain when URL encoding is off. + // Because an empty request body or url can also mean absence of all needed values, + // it is not in all cases a bad request, so let's return 422. + if parseErr != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) + } + errors = mapForm(formStruct, ctx.Req.Form, nil, errors) + validateAndMap(formStruct, ctx, errors, ifacePtr...) + } +} + +// Maximum amount of memory to use when parsing a multipart form. +// Set this to whatever value you prefer; default is 10 MB. +var MaxMemory = int64(1024 * 1024 * 10) + +// MultipartForm works much like Form, except it can parse multipart forms +// and handle file uploads. Like the other deserialization middleware handlers, +// you can pass in an interface to make the interface available for injection +// into other handlers later. +func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + var errors Errors + ensureNotPointer(formStruct) + formStruct := reflect.New(reflect.TypeOf(formStruct)) + // This if check is necessary due to https://github.com/martini-contrib/csrf/issues/6 + if ctx.Req.MultipartForm == nil { + // Workaround for multipart forms returning nil instead of an error + // when content is not multipart; see https://code.google.com/p/go/issues/detail?id=6334 + if multipartReader, err := ctx.Req.MultipartReader(); err != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, err.Error()) + } else { + form, parseErr := multipartReader.ReadForm(MaxMemory) + if parseErr != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) + } + + if ctx.Req.Form == nil { + ctx.Req.ParseForm() + } + for k, v := range form.Value { + ctx.Req.Form[k] = append(ctx.Req.Form[k], v...) + } + + ctx.Req.MultipartForm = form + } + } + errors = mapForm(formStruct, ctx.Req.MultipartForm.Value, ctx.Req.MultipartForm.File, errors) + validateAndMap(formStruct, ctx, errors, ifacePtr...) + } +} + +// Json is middleware to deserialize a JSON payload from the request +// into the struct that is passed in. The resulting struct is then +// validated, but no error handling is actually performed here. +// An interface pointer can be added as a second argument in order +// to map the struct to a specific interface. +func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + var errors Errors + ensureNotPointer(jsonStruct) + jsonStruct := reflect.New(reflect.TypeOf(jsonStruct)) + if ctx.Req.Request.Body != nil { + defer ctx.Req.Request.Body.Close() + err := json.NewDecoder(ctx.Req.Request.Body).Decode(jsonStruct.Interface()) + if err != nil && err != io.EOF { + errors.Add([]string{}, ERR_DESERIALIZATION, err.Error()) + } + } + validateAndMap(jsonStruct, ctx, errors, ifacePtr...) + } +} + +// RawValidate is same as Validate but does not require a HTTP context, +// and can be used independently just for validation. +// This function does not support Validator interface. +func RawValidate(obj interface{}) Errors { + var errs Errors + v := reflect.ValueOf(obj) + k := v.Kind() + if k == reflect.Interface || k == reflect.Ptr { + v = v.Elem() + k = v.Kind() + } + if k == reflect.Slice || k == reflect.Array { + for i := 0; i < v.Len(); i++ { + e := v.Index(i).Interface() + errs = validateStruct(errs, e) + } + } else { + errs = validateStruct(errs, obj) + } + return errs +} + +// Validate is middleware to enforce required fields. If the struct +// passed in implements Validator, then the user-defined Validate method +// is executed, and its errors are mapped to the context. This middleware +// performs no error handling: it merely detects errors and maps them. +func Validate(obj interface{}) macaron.Handler { + return func(ctx *macaron.Context) { + var errs Errors + v := reflect.ValueOf(obj) + k := v.Kind() + if k == reflect.Interface || k == reflect.Ptr { + v = v.Elem() + k = v.Kind() + } + if k == reflect.Slice || k == reflect.Array { + for i := 0; i < v.Len(); i++ { + e := v.Index(i).Interface() + errs = validateStruct(errs, e) + if validator, ok := e.(Validator); ok { + errs = validator.Validate(ctx, errs) + } + } + } else { + errs = validateStruct(errs, obj) + if validator, ok := obj.(Validator); ok { + errs = validator.Validate(ctx, errs) + } + } + ctx.Map(errs) + } +} + +var ( + AlphaDashPattern = regexp.MustCompile("[^\\d\\w-_]") + AlphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]") + EmailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") +) + +// Copied from github.com/asaskevich/govalidator. +const _MAX_URL_RUNE_COUNT = 2083 +const _MIN_URL_RUNE_COUNT = 3 + +var ( + urlSchemaRx = `((ftp|tcp|udp|wss?|https?):\/\/)` + urlUsernameRx = `(\S+(:\S*)?@)` + urlIPRx = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))` + ipRx = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + urlSubdomainRx = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))` + urlPortRx = `(:(\d{1,5}))` + urlPathRx = `((\/|\?|#)[^\s]*)` + URLPattern = regexp.MustCompile(`^` + urlSchemaRx + `?` + urlUsernameRx + `?` + `((` + urlIPRx + `|(\[` + ipRx + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + urlSubdomainRx + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + urlPortRx + `?` + urlPathRx + `?$`) +) + +// IsURL check if the string is an URL. +func isURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= _MAX_URL_RUNE_COUNT || len(str) <= _MIN_URL_RUNE_COUNT || strings.HasPrefix(str, ".") { + return false + } + u, err := url.Parse(str) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return URLPattern.MatchString(str) + +} + +type ( + // Rule represents a validation rule. + Rule struct { + // IsMatch checks if rule matches. + IsMatch func(string) bool + // IsValid applies validation rule to condition. + IsValid func(Errors, string, interface{}) (bool, Errors) + } + + // ParamRule does same thing as Rule but passes rule itself to IsValid method. + ParamRule struct { + // IsMatch checks if rule matches. + IsMatch func(string) bool + // IsValid applies validation rule to condition. + IsValid func(Errors, string, string, interface{}) (bool, Errors) + } + + // RuleMapper and ParamRuleMapper represent validation rule mappers, + // it allwos users to add custom validation rules. + RuleMapper []*Rule + ParamRuleMapper []*ParamRule +) + +var ruleMapper RuleMapper +var paramRuleMapper ParamRuleMapper + +// AddRule adds new validation rule. +func AddRule(r *Rule) { + ruleMapper = append(ruleMapper, r) +} + +// AddParamRule adds new validation rule. +func AddParamRule(r *ParamRule) { + paramRuleMapper = append(paramRuleMapper, r) +} + +func in(fieldValue interface{}, arr string) bool { + val := fmt.Sprintf("%v", fieldValue) + vals := strings.Split(arr, ",") + isIn := false + for _, v := range vals { + if v == val { + isIn = true + break + } + } + return isIn +} + +func parseFormName(raw, actual string) string { + if len(actual) > 0 { + return actual + } + return nameMapper(raw) +} + +// Performs required field checking on a struct +func validateStruct(errors Errors, obj interface{}) Errors { + typ := reflect.TypeOf(obj) + val := reflect.ValueOf(obj) + + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + val = val.Elem() + } + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + + // Allow ignored fields in the struct + if field.Tag.Get("form") == "-" || !val.Field(i).CanInterface() { + continue + } + + fieldVal := val.Field(i) + fieldValue := fieldVal.Interface() + zero := reflect.Zero(field.Type).Interface() + + // Validate nested and embedded structs (if pointer, only do so if not nil) + if field.Type.Kind() == reflect.Struct || + (field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue) && + field.Type.Elem().Kind() == reflect.Struct) { + errors = validateStruct(errors, fieldValue) + } + errors = validateField(errors, zero, field, fieldVal, fieldValue) + } + return errors +} + +func validateField(errors Errors, zero interface{}, field reflect.StructField, fieldVal reflect.Value, fieldValue interface{}) Errors { + if fieldVal.Kind() == reflect.Slice { + for i := 0; i < fieldVal.Len(); i++ { + sliceVal := fieldVal.Index(i) + if sliceVal.Kind() == reflect.Ptr { + sliceVal = sliceVal.Elem() + } + + sliceValue := sliceVal.Interface() + zero := reflect.Zero(sliceVal.Type()).Interface() + if sliceVal.Kind() == reflect.Struct || + (sliceVal.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, sliceValue) && + sliceVal.Elem().Kind() == reflect.Struct) { + errors = validateStruct(errors, sliceValue) + } + /* Apply validation rules to each item in a slice. ISSUE #3 + else { + errors = validateField(errors, zero, field, sliceVal, sliceValue) + }*/ + } + } + +VALIDATE_RULES: + for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { + if len(rule) == 0 { + continue + } + + switch { + case rule == "OmitEmpty": + if reflect.DeepEqual(zero, fieldValue) { + break VALIDATE_RULES + } + case rule == "Required": + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice { + if v.Len() == 0 { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + + continue + } + + if reflect.DeepEqual(zero, fieldValue) { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + case rule == "AlphaDash": + if AlphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash") + break VALIDATE_RULES + } + case rule == "AlphaDashDot": + if AlphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Size("): + size, _ := strconv.Atoi(rule[5 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MinSize("): + min, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MaxSize("): + max, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Range("): + nums := strings.Split(rule[6:len(rule)-1], ",") + if len(nums) != 2 { + break VALIDATE_RULES + } + val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt() + if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() { + errors.Add([]string{field.Name}, ERR_RANGE, "Range") + break VALIDATE_RULES + } + case rule == "Email": + if !EmailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_EMAIL, "Email") + break VALIDATE_RULES + } + case rule == "Url": + str := fmt.Sprintf("%v", fieldValue) + if len(str) == 0 { + continue + } else if !isURL(str) { + errors.Add([]string{field.Name}, ERR_URL, "Url") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "In("): + if !in(fieldValue, rule[3:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_IN, "In") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "NotIn("): + if in(fieldValue, rule[6:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Include("): + if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_INCLUDE, "Include") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Exclude("): + if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Default("): + if reflect.DeepEqual(zero, fieldValue) { + if fieldVal.CanAddr() { + errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) + } else { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default") + break VALIDATE_RULES + } + } + default: + // Apply custom validation rules + var isValid bool + for i := range ruleMapper { + if ruleMapper[i].IsMatch(rule) { + isValid, errors = ruleMapper[i].IsValid(errors, field.Name, fieldValue) + if !isValid { + break VALIDATE_RULES + } + } + } + for i := range paramRuleMapper { + if paramRuleMapper[i].IsMatch(rule) { + isValid, errors = paramRuleMapper[i].IsValid(errors, rule, field.Name, fieldValue) + if !isValid { + break VALIDATE_RULES + } + } + } + } + } + return errors +} + +// NameMapper represents a form tag name mapper. +type NameMapper func(string) string + +var ( + nameMapper = func(field string) string { + newstr := make([]rune, 0, len(field)) + for i, chr := range field { + if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { + if i > 0 { + newstr = append(newstr, '_') + } + chr -= ('A' - 'a') + } + newstr = append(newstr, chr) + } + return string(newstr) + } +) + +// SetNameMapper sets name mapper. +func SetNameMapper(nm NameMapper) { + nameMapper = nm +} + +// Takes values from the form data and puts them into a struct +func mapForm(formStruct reflect.Value, form map[string][]string, + formfile map[string][]*multipart.FileHeader, errors Errors) Errors { + + if formStruct.Kind() == reflect.Ptr { + formStruct = formStruct.Elem() + } + typ := formStruct.Type() + + for i := 0; i < typ.NumField(); i++ { + typeField := typ.Field(i) + structField := formStruct.Field(i) + + if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous { + structField.Set(reflect.New(typeField.Type.Elem())) + errors = mapForm(structField.Elem(), form, formfile, errors) + if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) { + structField.Set(reflect.Zero(structField.Type())) + } + } else if typeField.Type.Kind() == reflect.Struct { + errors = mapForm(structField, form, formfile, errors) + } + + inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form")) + if len(inputFieldName) == 0 || !structField.CanSet() { + continue + } + + inputValue, exists := form[inputFieldName] + if exists { + numElems := len(inputValue) + if structField.Kind() == reflect.Slice && numElems > 0 { + sliceOf := structField.Type().Elem().Kind() + slice := reflect.MakeSlice(structField.Type(), numElems, numElems) + for i := 0; i < numElems; i++ { + errors = setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) + } + formStruct.Field(i).Set(slice) + } else { + errors = setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) + } + continue + } + + inputFile, exists := formfile[inputFieldName] + if !exists { + continue + } + fhType := reflect.TypeOf((*multipart.FileHeader)(nil)) + numElems := len(inputFile) + if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType { + slice := reflect.MakeSlice(structField.Type(), numElems, numElems) + for i := 0; i < numElems; i++ { + slice.Index(i).Set(reflect.ValueOf(inputFile[i])) + } + structField.Set(slice) + } else if structField.Type() == fhType { + structField.Set(reflect.ValueOf(inputFile[0])) + } + } + return errors +} + +// This sets the value in a struct of an indeterminate type to the +// matching value from the request (via Form middleware) in the +// same type, so that not all deserialized values have to be strings. +// Supported types are string, int, float, and bool. +func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors Errors) Errors { + switch valueKind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if val == "" { + val = "0" + } + intVal, err := strconv.ParseInt(val, 10, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as integer") + } else { + structField.SetInt(intVal) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if val == "" { + val = "0" + } + uintVal, err := strconv.ParseUint(val, 10, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as unsigned integer") + } else { + structField.SetUint(uintVal) + } + case reflect.Bool: + if val == "on" { + structField.SetBool(true) + break + } + + if val == "" { + val = "false" + } + boolVal, err := strconv.ParseBool(val) + if err != nil { + errors.Add([]string{nameInTag}, ERR_BOOLEAN_TYPE, "Value could not be parsed as boolean") + } else if boolVal { + structField.SetBool(true) + } + case reflect.Float32: + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, 32) + if err != nil { + errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 32-bit float") + } else { + structField.SetFloat(floatVal) + } + case reflect.Float64: + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 64-bit float") + } else { + structField.SetFloat(floatVal) + } + case reflect.String: + structField.SetString(val) + } + return errors +} + +// Don't pass in pointers to bind to. Can lead to bugs. +func ensureNotPointer(obj interface{}) { + if reflect.TypeOf(obj).Kind() == reflect.Ptr { + panic("Pointers are not accepted as binding models") + } +} + +// Performs validation and combines errors from validation +// with errors from deserialization, then maps both the +// resulting struct and the errors to the context. +func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors Errors, ifacePtr ...interface{}) { + ctx.Invoke(Validate(obj.Interface())) + errors = append(errors, getErrors(ctx)...) + ctx.Map(errors) + ctx.Map(obj.Elem().Interface()) + if len(ifacePtr) > 0 { + ctx.MapTo(obj.Elem().Interface(), ifacePtr[0]) + } +} + +// getErrors simply gets the errors from the context (it's kind of a chore) +func getErrors(ctx *macaron.Context) Errors { + return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors) +} + +type ( + // ErrorHandler is the interface that has custom error handling process. + ErrorHandler interface { + // Error handles validation errors with custom process. + Error(*macaron.Context, Errors) + } + + // Validator is the interface that handles some rudimentary + // request validation logic so your application doesn't have to. + Validator interface { + // Validate validates that the request is OK. It is recommended + // that validation be limited to checking values for syntax and + // semantics, enough to know that you can make sense of the request + // in your application. For example, you might verify that a credit + // card number matches a valid pattern, but you probably wouldn't + // perform an actual credit card authorization here. + Validate(*macaron.Context, Errors) Errors + } +) diff --git a/vendor/gitea.com/macaron/binding/errors.go b/vendor/gitea.com/macaron/binding/errors.go new file mode 100644 index 0000000000..8cbe44a9d1 --- /dev/null +++ b/vendor/gitea.com/macaron/binding/errors.go @@ -0,0 +1,159 @@ +// Copyright 2014 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package binding + +const ( + // Type mismatch errors. + ERR_CONTENT_TYPE = "ContentTypeError" + ERR_DESERIALIZATION = "DeserializationError" + ERR_INTERGER_TYPE = "IntegerTypeError" + ERR_BOOLEAN_TYPE = "BooleanTypeError" + ERR_FLOAT_TYPE = "FloatTypeError" + + // Validation errors. + ERR_REQUIRED = "RequiredError" + ERR_ALPHA_DASH = "AlphaDashError" + ERR_ALPHA_DASH_DOT = "AlphaDashDotError" + ERR_SIZE = "SizeError" + ERR_MIN_SIZE = "MinSizeError" + ERR_MAX_SIZE = "MaxSizeError" + ERR_RANGE = "RangeError" + ERR_EMAIL = "EmailError" + ERR_URL = "UrlError" + ERR_IN = "InError" + ERR_NOT_INT = "NotInError" + ERR_INCLUDE = "IncludeError" + ERR_EXCLUDE = "ExcludeError" + ERR_DEFAULT = "DefaultError" +) + +type ( + // Errors may be generated during deserialization, binding, + // or validation. This type is mapped to the context so you + // can inject it into your own handlers and use it in your + // application if you want all your errors to look the same. + Errors []Error + + Error struct { + // An error supports zero or more field names, because an + // error can morph three ways: (1) it can indicate something + // wrong with the request as a whole, (2) it can point to a + // specific problem with a particular input field, or (3) it + // can span multiple related input fields. + FieldNames []string `json:"fieldNames,omitempty"` + + // The classification is like an error code, convenient to + // use when processing or categorizing an error programmatically. + // It may also be called the "kind" of error. + Classification string `json:"classification,omitempty"` + + // Message should be human-readable and detailed enough to + // pinpoint and resolve the problem, but it should be brief. For + // example, a payload of 100 objects in a JSON array might have + // an error in the 41st object. The message should help the + // end user find and fix the error with their request. + Message string `json:"message,omitempty"` + } +) + +// Add adds an error associated with the fields indicated +// by fieldNames, with the given classification and message. +func (e *Errors) Add(fieldNames []string, classification, message string) { + *e = append(*e, Error{ + FieldNames: fieldNames, + Classification: classification, + Message: message, + }) +} + +// Len returns the number of errors. +func (e *Errors) Len() int { + return len(*e) +} + +// Has determines whether an Errors slice has an Error with +// a given classification in it; it does not search on messages +// or field names. +func (e *Errors) Has(class string) bool { + for _, err := range *e { + if err.Kind() == class { + return true + } + } + return false +} + +/* +// WithClass gets a copy of errors that are classified by the +// the given classification. +func (e *Errors) WithClass(classification string) Errors { + var errs Errors + for _, err := range *e { + if err.Kind() == classification { + errs = append(errs, err) + } + } + return errs +} + +// ForField gets a copy of errors that are associated with the +// field by the given name. +func (e *Errors) ForField(name string) Errors { + var errs Errors + for _, err := range *e { + for _, fieldName := range err.Fields() { + if fieldName == name { + errs = append(errs, err) + break + } + } + } + return errs +} + +// Get gets errors of a particular class for the specified +// field name. +func (e *Errors) Get(class, fieldName string) Errors { + var errs Errors + for _, err := range *e { + if err.Kind() == class { + for _, nameOfField := range err.Fields() { + if nameOfField == fieldName { + errs = append(errs, err) + break + } + } + } + } + return errs +} +*/ + +// Fields returns the list of field names this error is +// associated with. +func (e Error) Fields() []string { + return e.FieldNames +} + +// Kind returns this error's classification. +func (e Error) Kind() string { + return e.Classification +} + +// Error returns this error's message. +func (e Error) Error() string { + return e.Message +} diff --git a/vendor/gitea.com/macaron/binding/go.mod b/vendor/gitea.com/macaron/binding/go.mod new file mode 100644 index 0000000000..8609495d49 --- /dev/null +++ b/vendor/gitea.com/macaron/binding/go.mod @@ -0,0 +1,9 @@ +module gitea.com/macaron/binding + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e +) diff --git a/vendor/gitea.com/macaron/binding/go.sum b/vendor/gitea.com/macaron/binding/go.sum new file mode 100644 index 0000000000..56302b6a5f --- /dev/null +++ b/vendor/gitea.com/macaron/binding/go.sum @@ -0,0 +1,30 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gitea.com/macaron/cache/.drone.yml b/vendor/gitea.com/macaron/cache/.drone.yml new file mode 100644 index 0000000000..9baf4f1cf7 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/cache/.gitignore b/vendor/gitea.com/macaron/cache/.gitignore new file mode 100644 index 0000000000..5c84e85ffc --- /dev/null +++ b/vendor/gitea.com/macaron/cache/.gitignore @@ -0,0 +1,5 @@ +nodb/cache +ledis/tmp.db/ +nodb/tmp.db/ +/vendor +/.idea diff --git a/vendor/gitea.com/macaron/cache/LICENSE b/vendor/gitea.com/macaron/cache/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/cache/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/cache/README.md b/vendor/gitea.com/macaron/cache/README.md new file mode 100644 index 0000000000..e9eac96115 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/README.md @@ -0,0 +1,20 @@ +# cache + +Middleware cache provides cache management for [Macaron](https://github.com/go-macaron/macaron). It can use many cache adapters, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Ledis and Nodb. + +### Installation + + go get gitea.com/macaron/cache + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/cache) +- [Documentation](http://go-macaron.com/docs/middlewares/cache) + +## Credits + +This package is a modified version of [go-macaron/cache](github.com/go-macaron/cache). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/cache/cache.go b/vendor/gitea.com/macaron/cache/cache.go new file mode 100644 index 0000000000..0893bca6c0 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/cache.go @@ -0,0 +1,122 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package cache is a middleware that provides the cache management of Macaron. +package cache + +import ( + "fmt" + + "gitea.com/macaron/macaron" +) + +const _VERSION = "0.3.0" + +func Version() string { + return _VERSION +} + +// Cache is the interface that operates the cache data. +type Cache interface { + // Put puts value into cache with key and expire time. + Put(key string, val interface{}, timeout int64) error + // Get gets cached value by given key. + Get(key string) interface{} + // Delete deletes cached value by given key. + Delete(key string) error + // Incr increases cached int-type value by given key as a counter. + Incr(key string) error + // Decr decreases cached int-type value by given key as a counter. + Decr(key string) error + // IsExist returns true if cached value exists. + IsExist(key string) bool + // Flush deletes all cached data. + Flush() error + // StartAndGC starts GC routine based on config string settings. + StartAndGC(opt Options) error +} + +// Options represents a struct for specifying configuration options for the cache middleware. +type Options struct { + // Name of adapter. Default is "memory". + Adapter string + // Adapter configuration, it's corresponding to adapter. + AdapterConfig string + // GC interval time in seconds. Default is 60. + Interval int + // Occupy entire database. Default is false. + OccupyMode bool + // Configuration section name. Default is "cache". + Section string +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + if len(opt.Section) == 0 { + opt.Section = "cache" + } + sec := macaron.Config().Section(opt.Section) + + if len(opt.Adapter) == 0 { + opt.Adapter = sec.Key("ADAPTER").MustString("memory") + } + if opt.Interval == 0 { + opt.Interval = sec.Key("INTERVAL").MustInt(60) + } + if len(opt.AdapterConfig) == 0 { + opt.AdapterConfig = sec.Key("ADAPTER_CONFIG").MustString("data/caches") + } + + return opt +} + +// NewCacher creates and returns a new cacher by given adapter name and configuration. +// It panics when given adapter isn't registered and starts GC automatically. +func NewCacher(name string, opt Options) (Cache, error) { + adapter, ok := adapters[name] + if !ok { + return nil, fmt.Errorf("cache: unknown adapter '%s'(forgot to import?)", name) + } + return adapter, adapter.StartAndGC(opt) +} + +// Cacher is a middleware that maps a cache.Cache service into the Macaron handler chain. +// An single variadic cache.Options struct can be optionally provided to configure. +func Cacher(options ...Options) macaron.Handler { + opt := prepareOptions(options) + cache, err := NewCacher(opt.Adapter, opt) + if err != nil { + panic(err) + } + return func(ctx *macaron.Context) { + ctx.Map(cache) + } +} + +var adapters = make(map[string]Cache) + +// Register registers a adapter. +func Register(name string, adapter Cache) { + if adapter == nil { + panic("cache: cannot register adapter with nil value") + } + if _, dup := adapters[name]; dup { + panic(fmt.Errorf("cache: cannot register adapter '%s' twice", name)) + } + adapters[name] = adapter +} diff --git a/vendor/gitea.com/macaron/cache/file.go b/vendor/gitea.com/macaron/cache/file.go new file mode 100644 index 0000000000..3a51892b41 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/file.go @@ -0,0 +1,208 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cache + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "sync" + "time" + + "gitea.com/macaron/macaron" + "github.com/unknwon/com" +) + +// Item represents a cache item. +type Item struct { + Val interface{} + Created int64 + Expire int64 +} + +func (item *Item) hasExpired() bool { + return item.Expire > 0 && + (time.Now().Unix()-item.Created) >= item.Expire +} + +// FileCacher represents a file cache adapter implementation. +type FileCacher struct { + lock sync.Mutex + rootPath string + interval int // GC interval. +} + +// NewFileCacher creates and returns a new file cacher. +func NewFileCacher() *FileCacher { + return &FileCacher{} +} + +func (c *FileCacher) filepath(key string) string { + m := md5.Sum([]byte(key)) + hash := hex.EncodeToString(m[:]) + return filepath.Join(c.rootPath, string(hash[0]), string(hash[1]), hash) +} + +// Put puts value into cache with key and expire time. +// If expired is 0, it will be deleted by next GC operation. +func (c *FileCacher) Put(key string, val interface{}, expire int64) error { + filename := c.filepath(key) + item := &Item{val, time.Now().Unix(), expire} + data, err := EncodeGob(item) + if err != nil { + return err + } + + os.MkdirAll(filepath.Dir(filename), os.ModePerm) + return ioutil.WriteFile(filename, data, os.ModePerm) +} + +func (c *FileCacher) read(key string) (*Item, error) { + filename := c.filepath(key) + + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + item := new(Item) + return item, DecodeGob(data, item) +} + +// Get gets cached value by given key. +func (c *FileCacher) Get(key string) interface{} { + item, err := c.read(key) + if err != nil { + return nil + } + + if item.hasExpired() { + os.Remove(c.filepath(key)) + return nil + } + return item.Val +} + +// Delete deletes cached value by given key. +func (c *FileCacher) Delete(key string) error { + return os.Remove(c.filepath(key)) +} + +// Incr increases cached int-type value by given key as a counter. +func (c *FileCacher) Incr(key string) error { + item, err := c.read(key) + if err != nil { + return err + } + + item.Val, err = Incr(item.Val) + if err != nil { + return err + } + + return c.Put(key, item.Val, item.Expire) +} + +// Decrease cached int value. +func (c *FileCacher) Decr(key string) error { + item, err := c.read(key) + if err != nil { + return err + } + + item.Val, err = Decr(item.Val) + if err != nil { + return err + } + + return c.Put(key, item.Val, item.Expire) +} + +// IsExist returns true if cached value exists. +func (c *FileCacher) IsExist(key string) bool { + return com.IsExist(c.filepath(key)) +} + +// Flush deletes all cached data. +func (c *FileCacher) Flush() error { + return os.RemoveAll(c.rootPath) +} + +func (c *FileCacher) startGC() { + c.lock.Lock() + defer c.lock.Unlock() + + if c.interval < 1 { + return + } + + if err := filepath.Walk(c.rootPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("Walk: %v", err) + } + + if fi.IsDir() { + return nil + } + + data, err := ioutil.ReadFile(path) + if err != nil && !os.IsNotExist(err) { + fmt.Errorf("ReadFile: %v", err) + } + + item := new(Item) + if err = DecodeGob(data, item); err != nil { + return err + } + if item.hasExpired() { + if err = os.Remove(path); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Remove: %v", err) + } + } + return nil + }); err != nil { + log.Printf("error garbage collecting cache files: %v", err) + } + + time.AfterFunc(time.Duration(c.interval)*time.Second, func() { c.startGC() }) +} + +// StartAndGC starts GC routine based on config string settings. +func (c *FileCacher) StartAndGC(opt Options) error { + c.lock.Lock() + c.rootPath = opt.AdapterConfig + c.interval = opt.Interval + + if !filepath.IsAbs(c.rootPath) { + c.rootPath = filepath.Join(macaron.Root, c.rootPath) + } + c.lock.Unlock() + + if err := os.MkdirAll(c.rootPath, os.ModePerm); err != nil { + return err + } + + go c.startGC() + return nil +} + +func init() { + Register("file", NewFileCacher()) +} diff --git a/vendor/gitea.com/macaron/cache/go.mod b/vendor/gitea.com/macaron/cache/go.mod new file mode 100644 index 0000000000..b00811dc2a --- /dev/null +++ b/vendor/gitea.com/macaron/cache/go.mod @@ -0,0 +1,30 @@ +module gitea.com/macaron/cache + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 + github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 // indirect + github.com/edsrzf/mmap-go v1.0.0 // indirect + github.com/go-redis/redis v6.15.2+incompatible + github.com/go-sql-driver/mysql v1.4.1 + github.com/lib/pq v1.2.0 + github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de // indirect + github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af + github.com/mattn/go-sqlite3 v1.11.0 // indirect + github.com/onsi/ginkgo v1.8.0 // indirect + github.com/onsi/gomega v1.5.0 // indirect + github.com/pelletier/go-toml v1.4.0 // indirect + github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect + github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect + github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 + github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect + golang.org/x/sys v0.0.0-20190730183949-1393eb018365 // indirect + google.golang.org/appengine v1.6.1 // indirect + gopkg.in/ini.v1 v1.44.2 +) diff --git a/vendor/gitea.com/macaron/cache/go.sum b/vendor/gitea.com/macaron/cache/go.sum new file mode 100644 index 0000000000..3f311ad14b --- /dev/null +++ b/vendor/gitea.com/macaron/cache/go.sum @@ -0,0 +1,106 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= +github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af h1:UaWHNBdukWrSG3DRvHFR/hyfg681fceqQDYVTBncKfQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 h1:qvsJwGToa8rxb42cDRhkbKeX2H5N8BH+s2aUikGt8mI= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190730183949-1393eb018365 h1:SaXEMXhWzMJThc05vu6uh61Q245r4KaWMrsTedk0FDc= +golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.2 h1:N6kNUPqiIyxP+s/aINPzRvNpcTVV30qLC0t6ZjZFlUU= +gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gitea.com/macaron/cache/memcache/memcache.go b/vendor/gitea.com/macaron/cache/memcache/memcache.go new file mode 100644 index 0000000000..79f44bfccc --- /dev/null +++ b/vendor/gitea.com/macaron/cache/memcache/memcache.go @@ -0,0 +1,92 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cache + +import ( + "strings" + + "github.com/bradfitz/gomemcache/memcache" + "github.com/unknwon/com" + + "gitea.com/macaron/cache" +) + +// MemcacheCacher represents a memcache cache adapter implementation. +type MemcacheCacher struct { + c *memcache.Client +} + +func NewItem(key string, data []byte, expire int32) *memcache.Item { + return &memcache.Item{ + Key: key, + Value: data, + Expiration: expire, + } +} + +// Put puts value into cache with key and expire time. +// If expired is 0, it lives forever. +func (c *MemcacheCacher) Put(key string, val interface{}, expire int64) error { + return c.c.Set(NewItem(key, []byte(com.ToStr(val)), int32(expire))) +} + +// Get gets cached value by given key. +func (c *MemcacheCacher) Get(key string) interface{} { + item, err := c.c.Get(key) + if err != nil { + return nil + } + return string(item.Value) +} + +// Delete deletes cached value by given key. +func (c *MemcacheCacher) Delete(key string) error { + return c.c.Delete(key) +} + +// Incr increases cached int-type value by given key as a counter. +func (c *MemcacheCacher) Incr(key string) error { + _, err := c.c.Increment(key, 1) + return err +} + +// Decr decreases cached int-type value by given key as a counter. +func (c *MemcacheCacher) Decr(key string) error { + _, err := c.c.Decrement(key, 1) + return err +} + +// IsExist returns true if cached value exists. +func (c *MemcacheCacher) IsExist(key string) bool { + _, err := c.c.Get(key) + return err == nil +} + +// Flush deletes all cached data. +func (c *MemcacheCacher) Flush() error { + return c.c.FlushAll() +} + +// StartAndGC starts GC routine based on config string settings. +// AdapterConfig: 127.0.0.1:9090;127.0.0.1:9091 +func (c *MemcacheCacher) StartAndGC(opt cache.Options) error { + c.c = memcache.New(strings.Split(opt.AdapterConfig, ";")...) + return nil +} + +func init() { + cache.Register("memcache", &MemcacheCacher{}) +} diff --git a/vendor/gitea.com/macaron/cache/memcache/memcache.goconvey b/vendor/gitea.com/macaron/cache/memcache/memcache.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/memcache/memcache.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/cache/memory.go b/vendor/gitea.com/macaron/cache/memory.go new file mode 100644 index 0000000000..2d5a8fac4d --- /dev/null +++ b/vendor/gitea.com/macaron/cache/memory.go @@ -0,0 +1,179 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cache + +import ( + "errors" + "sync" + "time" +) + +// MemoryItem represents a memory cache item. +type MemoryItem struct { + val interface{} + created int64 + expire int64 +} + +func (item *MemoryItem) hasExpired() bool { + return item.expire > 0 && + (time.Now().Unix()-item.created) >= item.expire +} + +// MemoryCacher represents a memory cache adapter implementation. +type MemoryCacher struct { + lock sync.RWMutex + items map[string]*MemoryItem + interval int // GC interval. +} + +// NewMemoryCacher creates and returns a new memory cacher. +func NewMemoryCacher() *MemoryCacher { + return &MemoryCacher{items: make(map[string]*MemoryItem)} +} + +// Put puts value into cache with key and expire time. +// If expired is 0, it will be deleted by next GC operation. +func (c *MemoryCacher) Put(key string, val interface{}, expire int64) error { + c.lock.Lock() + defer c.lock.Unlock() + + c.items[key] = &MemoryItem{ + val: val, + created: time.Now().Unix(), + expire: expire, + } + return nil +} + +// Get gets cached value by given key. +func (c *MemoryCacher) Get(key string) interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + + item, ok := c.items[key] + if !ok { + return nil + } + if item.hasExpired() { + go c.Delete(key) + return nil + } + return item.val +} + +// Delete deletes cached value by given key. +func (c *MemoryCacher) Delete(key string) error { + c.lock.Lock() + defer c.lock.Unlock() + + delete(c.items, key) + return nil +} + +// Incr increases cached int-type value by given key as a counter. +func (c *MemoryCacher) Incr(key string) (err error) { + c.lock.RLock() + defer c.lock.RUnlock() + + item, ok := c.items[key] + if !ok { + return errors.New("key not exist") + } + item.val, err = Incr(item.val) + return err +} + +// Decr decreases cached int-type value by given key as a counter. +func (c *MemoryCacher) Decr(key string) (err error) { + c.lock.RLock() + defer c.lock.RUnlock() + + item, ok := c.items[key] + if !ok { + return errors.New("key not exist") + } + + item.val, err = Decr(item.val) + return err +} + +// IsExist returns true if cached value exists. +func (c *MemoryCacher) IsExist(key string) bool { + c.lock.RLock() + defer c.lock.RUnlock() + + _, ok := c.items[key] + return ok +} + +// Flush deletes all cached data. +func (c *MemoryCacher) Flush() error { + c.lock.Lock() + defer c.lock.Unlock() + + c.items = make(map[string]*MemoryItem) + return nil +} + +func (c *MemoryCacher) checkRawExpiration(key string) { + item, ok := c.items[key] + if !ok { + return + } + + if item.hasExpired() { + delete(c.items, key) + } +} + +func (c *MemoryCacher) checkExpiration(key string) { + c.lock.Lock() + defer c.lock.Unlock() + + c.checkRawExpiration(key) +} + +func (c *MemoryCacher) startGC() { + c.lock.Lock() + defer c.lock.Unlock() + + if c.interval < 1 { + return + } + + if c.items != nil { + for key, _ := range c.items { + c.checkRawExpiration(key) + } + } + + time.AfterFunc(time.Duration(c.interval)*time.Second, func() { c.startGC() }) +} + +// StartAndGC starts GC routine based on config string settings. +func (c *MemoryCacher) StartAndGC(opt Options) error { + c.lock.Lock() + c.interval = opt.Interval + c.lock.Unlock() + + go c.startGC() + return nil +} + +func init() { + Register("memory", NewMemoryCacher()) +} diff --git a/vendor/gitea.com/macaron/cache/redis/redis.go b/vendor/gitea.com/macaron/cache/redis/redis.go new file mode 100644 index 0000000000..892ee28bdc --- /dev/null +++ b/vendor/gitea.com/macaron/cache/redis/redis.go @@ -0,0 +1,178 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cache + +import ( + "fmt" + "strings" + "time" + + "github.com/go-redis/redis" + "github.com/unknwon/com" + "gopkg.in/ini.v1" + + "gitea.com/macaron/cache" +) + +// RedisCacher represents a redis cache adapter implementation. +type RedisCacher struct { + c *redis.Client + prefix string + hsetName string + occupyMode bool +} + +// Put puts value into cache with key and expire time. +// If expired is 0, it lives forever. +func (c *RedisCacher) Put(key string, val interface{}, expire int64) error { + key = c.prefix + key + if expire == 0 { + if err := c.c.Set(key, com.ToStr(val), 0).Err(); err != nil { + return err + } + } else { + dur, err := time.ParseDuration(com.ToStr(expire) + "s") + if err != nil { + return err + } + if err = c.c.Set(key, com.ToStr(val), dur).Err(); err != nil { + return err + } + } + + if c.occupyMode { + return nil + } + return c.c.HSet(c.hsetName, key, "0").Err() +} + +// Get gets cached value by given key. +func (c *RedisCacher) Get(key string) interface{} { + val, err := c.c.Get(c.prefix + key).Result() + if err != nil { + return nil + } + return val +} + +// Delete deletes cached value by given key. +func (c *RedisCacher) Delete(key string) error { + key = c.prefix + key + if err := c.c.Del(key).Err(); err != nil { + return err + } + + if c.occupyMode { + return nil + } + return c.c.HDel(c.hsetName, key).Err() +} + +// Incr increases cached int-type value by given key as a counter. +func (c *RedisCacher) Incr(key string) error { + if !c.IsExist(key) { + return fmt.Errorf("key '%s' not exist", key) + } + return c.c.Incr(c.prefix + key).Err() +} + +// Decr decreases cached int-type value by given key as a counter. +func (c *RedisCacher) Decr(key string) error { + if !c.IsExist(key) { + return fmt.Errorf("key '%s' not exist", key) + } + return c.c.Decr(c.prefix + key).Err() +} + +// IsExist returns true if cached value exists. +func (c *RedisCacher) IsExist(key string) bool { + if c.c.Exists(c.prefix+key).Val() == 1 { + return true + } + + if !c.occupyMode { + c.c.HDel(c.hsetName, c.prefix+key) + } + return false +} + +// Flush deletes all cached data. +func (c *RedisCacher) Flush() error { + if c.occupyMode { + return c.c.FlushDb().Err() + } + + keys, err := c.c.HKeys(c.hsetName).Result() + if err != nil { + return err + } + if err = c.c.Del(keys...).Err(); err != nil { + return err + } + return c.c.Del(c.hsetName).Err() +} + +// StartAndGC starts GC routine based on config string settings. +// AdapterConfig: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache: +func (c *RedisCacher) StartAndGC(opts cache.Options) error { + c.hsetName = "MacaronCache" + c.occupyMode = opts.OccupyMode + + cfg, err := ini.Load([]byte(strings.Replace(opts.AdapterConfig, ",", "\n", -1))) + if err != nil { + return err + } + + opt := &redis.Options{ + Network: "tcp", + } + for k, v := range cfg.Section("").KeysHash() { + switch k { + case "network": + opt.Network = v + case "addr": + opt.Addr = v + case "password": + opt.Password = v + case "db": + opt.DB = com.StrTo(v).MustInt() + case "pool_size": + opt.PoolSize = com.StrTo(v).MustInt() + case "idle_timeout": + opt.IdleTimeout, err = time.ParseDuration(v + "s") + if err != nil { + return fmt.Errorf("error parsing idle timeout: %v", err) + } + case "hset_name": + c.hsetName = v + case "prefix": + c.prefix = v + default: + return fmt.Errorf("session/redis: unsupported option '%s'", k) + } + } + + c.c = redis.NewClient(opt) + if err = c.c.Ping().Err(); err != nil { + return err + } + + return nil +} + +func init() { + cache.Register("redis", &RedisCacher{}) +} diff --git a/vendor/gitea.com/macaron/cache/redis/redis.goconvey b/vendor/gitea.com/macaron/cache/redis/redis.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/cache/redis/redis.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/cache/utils.go b/vendor/gitea.com/macaron/cache/utils.go new file mode 100644 index 0000000000..734fb6f40e --- /dev/null +++ b/vendor/gitea.com/macaron/cache/utils.go @@ -0,0 +1,84 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package cache + +import ( + "bytes" + "encoding/gob" + "errors" +) + +func EncodeGob(item *Item) ([]byte, error) { + buf := bytes.NewBuffer(nil) + err := gob.NewEncoder(buf).Encode(item) + return buf.Bytes(), err +} + +func DecodeGob(data []byte, out *Item) error { + buf := bytes.NewBuffer(data) + return gob.NewDecoder(buf).Decode(&out) +} + +func Incr(val interface{}) (interface{}, error) { + switch val.(type) { + case int: + val = val.(int) + 1 + case int32: + val = val.(int32) + 1 + case int64: + val = val.(int64) + 1 + case uint: + val = val.(uint) + 1 + case uint32: + val = val.(uint32) + 1 + case uint64: + val = val.(uint64) + 1 + default: + return val, errors.New("item value is not int-type") + } + return val, nil +} + +func Decr(val interface{}) (interface{}, error) { + switch val.(type) { + case int: + val = val.(int) - 1 + case int32: + val = val.(int32) - 1 + case int64: + val = val.(int64) - 1 + case uint: + if val.(uint) > 0 { + val = val.(uint) - 1 + } else { + return val, errors.New("item value is less than 0") + } + case uint32: + if val.(uint32) > 0 { + val = val.(uint32) - 1 + } else { + return val, errors.New("item value is less than 0") + } + case uint64: + if val.(uint64) > 0 { + val = val.(uint64) - 1 + } else { + return val, errors.New("item value is less than 0") + } + default: + return val, errors.New("item value is not int-type") + } + return val, nil +} diff --git a/vendor/gitea.com/macaron/captcha/.drone.yml b/vendor/gitea.com/macaron/captcha/.drone.yml new file mode 100644 index 0000000000..1db7ea30e3 --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/captcha/LICENSE b/vendor/gitea.com/macaron/captcha/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/captcha/README.md b/vendor/gitea.com/macaron/captcha/README.md new file mode 100644 index 0000000000..f55b2e925e --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/README.md @@ -0,0 +1,16 @@ +# captcha [![Build Status](https://drone.gitea.com/api/badges/macaron/captcha/status.svg)](https://drone.gitea.com/macaron/captcha) + +Middleware captcha provides captcha service for [Macaron](https://gitea.com/macaron/macaron). + +### Installation + + go get gitea.com/macaron/captcha + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/captcha) +- [Documentation](http://go-macaron.com/docs/middlewares/captcha) + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/captcha/captcha.go b/vendor/gitea.com/macaron/captcha/captcha.go new file mode 100644 index 0000000000..b6e51f2a26 --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/captcha.go @@ -0,0 +1,253 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package captcha a middleware that provides captcha service for Macaron. +package captcha + +import ( + "fmt" + "html/template" + "image/color" + "path" + "strings" + + "gitea.com/macaron/cache" + "gitea.com/macaron/macaron" + "github.com/unknwon/com" +) + +const _VERSION = "0.1.0" + +func Version() string { + return _VERSION +} + +var ( + defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} +) + +// Captcha represents a captcha service. +type Captcha struct { + store cache.Cache + SubURL string + URLPrefix string + FieldIdName string + FieldCaptchaName string + StdWidth int + StdHeight int + ChallengeNums int + Expiration int64 + CachePrefix string + ColorPalette color.Palette +} + +// generate key string +func (c *Captcha) key(id string) string { + return c.CachePrefix + id +} + +// generate rand chars with default chars +func (c *Captcha) genRandChars() string { + return string(com.RandomCreateBytes(c.ChallengeNums, defaultChars...)) +} + +// CreateHTML outputs HTML for display and fetch new captcha images. +func (c *Captcha) CreateHTML() template.HTML { + value, err := c.CreateCaptcha() + if err != nil { + panic(fmt.Errorf("fail to create captcha: %v", err)) + } + return template.HTML(fmt.Sprintf(`<input type="hidden" name="%[1]s" value="%[2]s"> + <a class="captcha" href="javascript:" tabindex="-1"> + <img onclick="this.src=('%[3]s%[4]s%[2]s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%[3]s%[4]s%[2]s.png"> + </a>`, c.FieldIdName, value, c.SubURL, c.URLPrefix)) +} + +// DEPRECATED +func (c *Captcha) CreateHtml() template.HTML { + return c.CreateHTML() +} + +// create a new captcha id +func (c *Captcha) CreateCaptcha() (string, error) { + id := string(com.RandomCreateBytes(15)) + if err := c.store.Put(c.key(id), c.genRandChars(), c.Expiration); err != nil { + return "", err + } + return id, nil +} + +// verify from a request +func (c *Captcha) VerifyReq(req macaron.Request) bool { + req.ParseForm() + return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName)) +} + +// direct verify id and challenge string +func (c *Captcha) Verify(id string, challenge string) bool { + if len(challenge) == 0 || len(id) == 0 { + return false + } + + var chars string + + key := c.key(id) + + if v, ok := c.store.Get(key).(string); ok { + chars = v + } else { + return false + } + + defer c.store.Delete(key) + + if len(chars) != len(challenge) { + return false + } + + // verify challenge + for i, c := range []byte(chars) { + if c != challenge[i]-48 { + return false + } + } + + return true +} + +type Options struct { + // Suburl path. Default is empty. + SubURL string + // URL prefix of getting captcha pictures. Default is "/captcha/". + URLPrefix string + // Hidden input element ID. Default is "captcha_id". + FieldIdName string + // User input value element name in request form. Default is "captcha". + FieldCaptchaName string + // Challenge number. Default is 6. + ChallengeNums int + // Captcha image width. Default is 240. + Width int + // Captcha image height. Default is 80. + Height int + // Captcha expiration time in seconds. Default is 600. + Expiration int64 + // Cache key prefix captcha characters. Default is "captcha_". + CachePrefix string + // ColorPalette holds a collection of primary colors used for + // the captcha's text. If not defined, a random color will be generated. + ColorPalette color.Palette +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + opt.SubURL = strings.TrimSuffix(opt.SubURL, "/") + + // Defaults. + if len(opt.URLPrefix) == 0 { + opt.URLPrefix = "/captcha/" + } else if opt.URLPrefix[len(opt.URLPrefix)-1] != '/' { + opt.URLPrefix += "/" + } + if len(opt.FieldIdName) == 0 { + opt.FieldIdName = "captcha_id" + } + if len(opt.FieldCaptchaName) == 0 { + opt.FieldCaptchaName = "captcha" + } + if opt.ChallengeNums == 0 { + opt.ChallengeNums = 6 + } + if opt.Width == 0 { + opt.Width = stdWidth + } + if opt.Height == 0 { + opt.Height = stdHeight + } + if opt.Expiration == 0 { + opt.Expiration = 600 + } + if len(opt.CachePrefix) == 0 { + opt.CachePrefix = "captcha_" + } + + return opt +} + +// NewCaptcha initializes and returns a captcha with given options. +func NewCaptcha(opt Options) *Captcha { + return &Captcha{ + SubURL: opt.SubURL, + URLPrefix: opt.URLPrefix, + FieldIdName: opt.FieldIdName, + FieldCaptchaName: opt.FieldCaptchaName, + StdWidth: opt.Width, + StdHeight: opt.Height, + ChallengeNums: opt.ChallengeNums, + Expiration: opt.Expiration, + CachePrefix: opt.CachePrefix, + ColorPalette: opt.ColorPalette, + } +} + +// Captchaer is a middleware that maps a captcha.Captcha service into the Macaron handler chain. +// An single variadic captcha.Options struct can be optionally provided to configure. +// This should be register after cache.Cacher. +func Captchaer(options ...Options) macaron.Handler { + return func(ctx *macaron.Context, cache cache.Cache) { + cpt := NewCaptcha(prepareOptions(options)) + cpt.store = cache + + if strings.HasPrefix(ctx.Req.URL.Path, cpt.URLPrefix) { + var chars string + id := path.Base(ctx.Req.URL.Path) + if i := strings.Index(id, "."); i > -1 { + id = id[:i] + } + key := cpt.key(id) + + // Reload captcha. + if len(ctx.Query("reload")) > 0 { + chars = cpt.genRandChars() + if err := cpt.store.Put(key, chars, cpt.Expiration); err != nil { + ctx.Status(500) + ctx.Write([]byte("captcha reload error")) + panic(fmt.Errorf("reload captcha: %v", err)) + } + } else { + if v, ok := cpt.store.Get(key).(string); ok { + chars = v + } else { + ctx.Status(404) + ctx.Write([]byte("captcha not found")) + return + } + } + + if _, err := NewImage([]byte(chars), cpt.StdWidth, cpt.StdHeight, cpt.ColorPalette).WriteTo(ctx.Resp); err != nil { + panic(fmt.Errorf("write captcha: %v", err)) + } + ctx.Status(200) + return + } + + ctx.Data["Captcha"] = cpt + ctx.Map(cpt) + } +} diff --git a/vendor/gitea.com/macaron/captcha/go.mod b/vendor/gitea.com/macaron/captcha/go.mod new file mode 100644 index 0000000000..6980787b99 --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/go.mod @@ -0,0 +1,10 @@ +module gitea.com/macaron/captcha + +go 1.11 + +require ( + gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76 + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e +) diff --git a/vendor/gitea.com/macaron/captcha/go.sum b/vendor/gitea.com/macaron/captcha/go.sum new file mode 100644 index 0000000000..6c6f0e4b66 --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/go.sum @@ -0,0 +1,78 @@ +gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76 h1:mMsMEg90c5KXQgRWsH8D6GHXfZIW1RAe5S9VYIb12lM= +gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76/go.mod h1:NFHb9Of+LUnU86bU20CiXXg6ZlgCJ4XytP14UsHOXFs= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.2 h1:N6kNUPqiIyxP+s/aINPzRvNpcTVV30qLC0t6ZjZFlUU= +gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gitea.com/macaron/captcha/image.go b/vendor/gitea.com/macaron/captcha/image.go new file mode 100644 index 0000000000..abe087437a --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/image.go @@ -0,0 +1,505 @@ +// Copyright 2013 Beego Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package captcha + +import ( + "bytes" + "image" + "image/color" + "image/png" + "io" + "math" +) + +const ( + fontWidth = 11 + fontHeight = 18 + blackChar = 1 + + // Standard width and height of a captcha image. + stdWidth = 240 + stdHeight = 80 + + // Maximum absolute skew factor of a single digit. + maxSkew = 0.7 + // Number of background circles. + circleCount = 20 +) + +var font = [][]byte{ + { // 0 + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + }, + { // 1 + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { // 2 + 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }, + { // 3 + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + }, + { // 4 + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + }, + { // 5 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, + }, + { // 6 + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + }, + { // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, + }, + { // 8 + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + }, + { // 9 + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + }, +} + +type Image struct { + *image.Paletted + numWidth int + numHeight int + dotSize int +} + +var prng = &siprng{} + +// randIntn returns a pseudorandom non-negative int in range [0, n). +func randIntn(n int) int { + return prng.Intn(n) +} + +// randInt returns a pseudorandom int in range [from, to]. +func randInt(from, to int) int { + return prng.Intn(to+1-from) + from +} + +// randFloat returns a pseudorandom float64 in range [from, to]. +func randFloat(from, to float64) float64 { + return (to-from)*prng.Float64() + from +} + +func randomPalette(primary color.Palette) color.Palette { + p := make([]color.Color, circleCount+1) + // Transparent color. + p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} + // Primary color. + var prim color.RGBA + if len(primary) == 0 { + prim = color.RGBA{ + uint8(randIntn(129)), + uint8(randIntn(129)), + uint8(randIntn(129)), + 0xFF, + } + } else { + r, g, b, a := primary[randIntn(len(primary)-1)].RGBA() + prim = color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)} + } + p[1] = prim + // Circle colors. + for i := 2; i <= circleCount; i++ { + p[i] = randomBrightness(prim, 255) + } + return p +} + +// NewImage returns a new captcha image of the given width and height with the +// given digits, where each digit must be in range 0-9. The digit's color is +// chosen by random from the colorPalette. +func NewImage(digits []byte, width, height int, colorPalette color.Palette) *Image { + m := new(Image) + m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette(colorPalette)) + m.calculateSizes(width, height, len(digits)) + // Randomly position captcha inside the image. + maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize + maxy := height - m.numHeight - m.dotSize*2 + var border int + if width > height { + border = height / 5 + } else { + border = width / 5 + } + x := randInt(border, maxx-border) + y := randInt(border, maxy-border) + // Draw digits. + for _, n := range digits { + m.drawDigit(font[n], x, y) + x += m.numWidth + m.dotSize + } + // Draw strike-through line. + m.strikeThrough() + // Apply wave distortion. + m.distort(randFloat(5, 10), randFloat(100, 200)) + // Fill image with random circles. + m.fillWithCircles(circleCount, m.dotSize) + return m +} + +// encodedPNG encodes an image to PNG and returns +// the result as a byte slice. +func (m *Image) encodedPNG() []byte { + var buf bytes.Buffer + if err := png.Encode(&buf, m.Paletted); err != nil { + panic(err.Error()) + } + return buf.Bytes() +} + +// WriteTo writes captcha image in PNG format into the given writer. +func (m *Image) WriteTo(w io.Writer) (int64, error) { + n, err := w.Write(m.encodedPNG()) + return int64(n), err +} + +func (m *Image) calculateSizes(width, height, ncount int) { + // Goal: fit all digits inside the image. + var border int + if width > height { + border = height / 4 + } else { + border = width / 4 + } + // Convert everything to floats for calculations. + w := float64(width - border*2) + h := float64(height - border*2) + // fw takes into account 1-dot spacing between digits. + fw := float64(fontWidth + 1) + fh := float64(fontHeight) + nc := float64(ncount) + // Calculate the width of a single digit taking into account only the + // width of the image. + nw := w / nc + // Calculate the height of a digit from this width. + nh := nw * fh / fw + // Digit too high? + if nh > h { + // Fit digits based on height. + nh = h + nw = fw / fh * nh + } + // Calculate dot size. + m.dotSize = int(nh / fh) + // Save everything, making the actual width smaller by 1 dot to account + // for spacing between digits. + m.numWidth = int(nw) - m.dotSize + m.numHeight = int(nh) +} + +func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { + for x := fromX; x <= toX; x++ { + m.SetColorIndex(x, y, colorIdx) + } +} + +func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { + f := 1 - radius + dfx := 1 + dfy := -2 * radius + xo := 0 + yo := radius + + m.SetColorIndex(x, y+radius, colorIdx) + m.SetColorIndex(x, y-radius, colorIdx) + m.drawHorizLine(x-radius, x+radius, y, colorIdx) + + for xo < yo { + if f >= 0 { + yo-- + dfy += 2 + f += dfy + } + xo++ + dfx += 2 + f += dfx + m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) + m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) + m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) + m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) + } +} + +func (m *Image) fillWithCircles(n, maxradius int) { + maxx := m.Bounds().Max.X + maxy := m.Bounds().Max.Y + for i := 0; i < n; i++ { + colorIdx := uint8(randInt(1, circleCount-1)) + r := randInt(1, maxradius) + m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx) + } +} + +func (m *Image) strikeThrough() { + maxx := m.Bounds().Max.X + maxy := m.Bounds().Max.Y + y := randInt(maxy/3, maxy-maxy/3) + amplitude := randFloat(5, 20) + period := randFloat(80, 180) + dx := 2.0 * math.Pi / period + for x := 0; x < maxx; x++ { + xo := amplitude * math.Cos(float64(y)*dx) + yo := amplitude * math.Sin(float64(x)*dx) + for yn := 0; yn < m.dotSize; yn++ { + r := randInt(0, m.dotSize) + m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) + } + } +} + +func (m *Image) drawDigit(digit []byte, x, y int) { + skf := randFloat(-maxSkew, maxSkew) + xs := float64(x) + r := m.dotSize / 2 + y += randInt(-r, r) + for yo := 0; yo < fontHeight; yo++ { + for xo := 0; xo < fontWidth; xo++ { + if digit[yo*fontWidth+xo] != blackChar { + continue + } + m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) + } + xs += skf + x = int(xs) + } +} + +func (m *Image) distort(amplude float64, period float64) { + w := m.Bounds().Max.X + h := m.Bounds().Max.Y + + oldm := m.Paletted + newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) + + dx := 2.0 * math.Pi / period + for x := 0; x < w; x++ { + for y := 0; y < h; y++ { + xo := amplude * math.Sin(float64(y)*dx) + yo := amplude * math.Cos(float64(x)*dx) + newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) + } + } + m.Paletted = newm +} + +func randomBrightness(c color.RGBA, max uint8) color.RGBA { + minc := min3(c.R, c.G, c.B) + maxc := max3(c.R, c.G, c.B) + if maxc > max { + return c + } + n := randIntn(int(max-maxc)) - int(minc) + return color.RGBA{ + uint8(int(c.R) + n), + uint8(int(c.G) + n), + uint8(int(c.B) + n), + uint8(c.A), + } +} + +func min3(x, y, z uint8) (m uint8) { + m = x + if y < m { + m = y + } + if z < m { + m = z + } + return +} + +func max3(x, y, z uint8) (m uint8) { + m = x + if y > m { + m = y + } + if z > m { + m = z + } + return +} diff --git a/vendor/gitea.com/macaron/captcha/siprng.go b/vendor/gitea.com/macaron/captcha/siprng.go new file mode 100644 index 0000000000..53a06d6841 --- /dev/null +++ b/vendor/gitea.com/macaron/captcha/siprng.go @@ -0,0 +1,277 @@ +// Copyright 2013 Beego Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package captcha + +import ( + "crypto/rand" + "encoding/binary" + "io" + "sync" +) + +// siprng is PRNG based on SipHash-2-4. +type siprng struct { + mu sync.Mutex + k0, k1, ctr uint64 +} + +// siphash implements SipHash-2-4, accepting a uint64 as a message. +func siphash(k0, k1, m uint64) uint64 { + // Initialization. + v0 := k0 ^ 0x736f6d6570736575 + v1 := k1 ^ 0x646f72616e646f6d + v2 := k0 ^ 0x6c7967656e657261 + v3 := k1 ^ 0x7465646279746573 + t := uint64(8) << 56 + + // Compression. + v3 ^= m + + // Round 1. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + // Round 2. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + v0 ^= m + + // Compress last block. + v3 ^= t + + // Round 1. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + // Round 2. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + v0 ^= t + + // Finalization. + v2 ^= 0xff + + // Round 1. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + // Round 2. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + // Round 3. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + // Round 4. + v0 += v1 + v1 = v1<<13 | v1>>(64-13) + v1 ^= v0 + v0 = v0<<32 | v0>>(64-32) + + v2 += v3 + v3 = v3<<16 | v3>>(64-16) + v3 ^= v2 + + v0 += v3 + v3 = v3<<21 | v3>>(64-21) + v3 ^= v0 + + v2 += v1 + v1 = v1<<17 | v1>>(64-17) + v1 ^= v2 + v2 = v2<<32 | v2>>(64-32) + + return v0 ^ v1 ^ v2 ^ v3 +} + +// rekey sets a new PRNG key, which is read from crypto/rand. +func (p *siprng) rekey() { + var k [16]byte + if _, err := io.ReadFull(rand.Reader, k[:]); err != nil { + panic(err.Error()) + } + p.k0 = binary.LittleEndian.Uint64(k[0:8]) + p.k1 = binary.LittleEndian.Uint64(k[8:16]) + p.ctr = 1 +} + +// Uint64 returns a new pseudorandom uint64. +// It rekeys PRNG on the first call and every 64 MB of generated data. +func (p *siprng) Uint64() uint64 { + p.mu.Lock() + if p.ctr == 0 || p.ctr > 8*1024*1024 { + p.rekey() + } + v := siphash(p.k0, p.k1, p.ctr) + p.ctr++ + p.mu.Unlock() + return v +} + +func (p *siprng) Int63() int64 { + return int64(p.Uint64() & 0x7fffffffffffffff) +} + +func (p *siprng) Uint32() uint32 { + return uint32(p.Uint64()) +} + +func (p *siprng) Int31() int32 { + return int32(p.Uint32() & 0x7fffffff) +} + +func (p *siprng) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } + if n <= 1<<31-1 { + return int(p.Int31n(int32(n))) + } + return int(p.Int63n(int64(n))) +} + +func (p *siprng) Int63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int63n") + } + max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) + v := p.Int63() + for v > max { + v = p.Int63() + } + return v % n +} + +func (p *siprng) Int31n(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int31n") + } + max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) + v := p.Int31() + for v > max { + v = p.Int31() + } + return v % n +} + +func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) } diff --git a/vendor/gitea.com/macaron/cors/.drone.yml b/vendor/gitea.com/macaron/cors/.drone.yml new file mode 100644 index 0000000000..39499f444a --- /dev/null +++ b/vendor/gitea.com/macaron/cors/.drone.yml @@ -0,0 +1,9 @@ +kind: pipeline +name: default + +steps: +- name: test + image: golang:1.11 + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic diff --git a/vendor/gitea.com/macaron/cors/.gitignore b/vendor/gitea.com/macaron/cors/.gitignore new file mode 100644 index 0000000000..f1c181ec9c --- /dev/null +++ b/vendor/gitea.com/macaron/cors/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/gitea.com/macaron/cors/LICENSE b/vendor/gitea.com/macaron/cors/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/gitea.com/macaron/cors/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gitea.com/macaron/cors/README.md b/vendor/gitea.com/macaron/cors/README.md new file mode 100644 index 0000000000..5ef70e3579 --- /dev/null +++ b/vendor/gitea.com/macaron/cors/README.md @@ -0,0 +1,5 @@ +# cors + +[![Build Status](https://drone.gitea.com/api/badges/macaron/cors/status.svg)](https://drone.gitea.com/macaron/cors) + +Package cors is a middleware that handles CORS requests & headers for Macaron. diff --git a/vendor/gitea.com/macaron/cors/cors.go b/vendor/gitea.com/macaron/cors/cors.go new file mode 100644 index 0000000000..ed00df1e7b --- /dev/null +++ b/vendor/gitea.com/macaron/cors/cors.go @@ -0,0 +1,168 @@ +package cors + +import ( + "fmt" + "log" + "net/http" + "net/url" + "strconv" + "strings" + + macaron "gitea.com/macaron/macaron" +) + +const version = "0.1.1" + +const anyDomain = "!*" + +// Version returns the version of this module +func Version() string { + return version +} + +/* +Options to configure the CORS middleware read from the [cors] section of the ini configuration file. + +SCHEME may be http or https as accepted schemes or the '*' wildcard to accept any scheme. + +ALLOW_DOMAIN may be a comma separated list of domains that are allowed to run CORS requests +Special values are the a single '*' wildcard that will allow any domain to send requests without +credentials and the special '!*' wildcard which will reply with requesting domain in the 'access-control-allow-origin' +header and hence allow requess from any domain *with* credentials. + +ALLOW_SUBDOMAIN set to true accepts requests from any subdomain of ALLOW_DOMAIN. + +METHODS may be a comma separated list of HTTP-methods to be accepted. + +MAX_AGE_SECONDS may be the duration in secs for which the response is cached (default 600). +ref: https://stackoverflow.com/questions/54300997/is-it-possible-to-cache-http-options-response?noredirect=1#comment95790277_54300997 +ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age + +ALLOW_CREDENTIALS set to false rejects any request with credentials. +*/ +type Options struct { + Section string + Scheme string + AllowDomain []string + AllowSubdomain bool + Methods []string + MaxAgeSeconds int + AllowCredentials bool +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + if len(opt.Section) == 0 { + opt.Section = "cors" + } + sec := macaron.Config().Section(opt.Section) + + if len(opt.Scheme) == 0 { + opt.Scheme = sec.Key("SCHEME").MustString("http") + } + if len(opt.AllowDomain) == 0 { + opt.AllowDomain = sec.Key("ALLOW_DOMAIN").Strings(",") + if len(opt.AllowDomain) == 0 { + opt.AllowDomain = []string{"*"} + } + } + if !opt.AllowSubdomain { + opt.AllowSubdomain = sec.Key("ALLOW_SUBDOMAIN").MustBool(false) + } + if len(opt.Methods) == 0 { + opt.Methods = sec.Key("METHODS").Strings(",") + if len(opt.Methods) == 0 { + opt.Methods = []string{ + http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodOptions, + } + } + } + if opt.MaxAgeSeconds <= 0 { + opt.MaxAgeSeconds = sec.Key("MAX_AGE_SECONDS").MustInt(600) + } + if !opt.AllowCredentials { + opt.AllowCredentials = sec.Key("ALLOW_CREDENTIALS").MustBool(true) + } + + return opt +} + +// CORS responds to preflight requests with adequat access-control-* respond headers +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin +// https://fetch.spec.whatwg.org/#cors-protocol-and-credentials +func CORS(options ...Options) macaron.Handler { + opt := prepareOptions(options) + return func(ctx *macaron.Context, log *log.Logger) { + reqOptions := ctx.Req.Method == http.MethodOptions + + headers := map[string]string{ + "access-control-allow-methods": strings.Join(opt.Methods, ","), + "access-control-allow-headers": ctx.Req.Header.Get("access-control-request-headers"), + "access-control-max-age": strconv.Itoa(opt.MaxAgeSeconds), + } + if opt.AllowDomain[0] == "*" { + headers["access-control-allow-origin"] = "*" + } else { + origin := ctx.Req.Header.Get("Origin") + if reqOptions && origin == "" { + respErrorf(ctx, log, http.StatusBadRequest, "missing origin header in CORS request") + return + } + + u, err := url.Parse(origin) + if err != nil { + respErrorf(ctx, log, http.StatusBadRequest, "Failed to parse CORS origin header. Reason: %v", err) + return + } + + ok := false + for _, d := range opt.AllowDomain { + if u.Hostname() == d || (opt.AllowSubdomain && strings.HasSuffix(u.Hostname(), "."+d)) || d == anyDomain { + ok = true + break + } + } + if ok { + if opt.Scheme != "*" { + u.Scheme = opt.Scheme + } + headers["access-control-allow-origin"] = u.String() + headers["access-control-allow-credentials"] = strconv.FormatBool(opt.AllowCredentials) + headers["vary"] = "Origin" + } + if reqOptions && !ok { + respErrorf(ctx, log, http.StatusBadRequest, "CORS request from prohibited domain %v", origin) + return + } + } + ctx.Resp.Before(func(w macaron.ResponseWriter) { + for k, v := range headers { + w.Header().Set(k, v) + } + }) + if reqOptions { + ctx.Status(200) // return response + } + } +} + +func respErrorf(ctx *macaron.Context, log *log.Logger, statusCode int, format string, a ...interface{}) { + msg := fmt.Sprintf(format, a...) + log.Println(msg) + ctx.WriteHeader(statusCode) + _, err := ctx.Write([]byte(msg)) + if err != nil { + panic(err) + } + return +} diff --git a/vendor/gitea.com/macaron/cors/go.mod b/vendor/gitea.com/macaron/cors/go.mod new file mode 100644 index 0000000000..418aab88de --- /dev/null +++ b/vendor/gitea.com/macaron/cors/go.mod @@ -0,0 +1,5 @@ +module gitea.com/macaron/cors + +go 1.11 + +require gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827 diff --git a/vendor/gitea.com/macaron/cors/go.sum b/vendor/gitea.com/macaron/cors/go.sum new file mode 100644 index 0000000000..e3bcd933dc --- /dev/null +++ b/vendor/gitea.com/macaron/cors/go.sum @@ -0,0 +1,31 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827 h1:/rT4MEFjhdViy2BFWKUwbC0JSNSziEbBCM7q4/B9qgo= +gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw= +github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg= +github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gitea.com/macaron/csrf/.drone.yml b/vendor/gitea.com/macaron/csrf/.drone.yml new file mode 100644 index 0000000000..1db7ea30e3 --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/csrf/LICENSE b/vendor/gitea.com/macaron/csrf/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/csrf/README.md b/vendor/gitea.com/macaron/csrf/README.md new file mode 100644 index 0000000000..3295bd5f9a --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/README.md @@ -0,0 +1,18 @@ +# csrf [![Build Status](https://drone.gitea.com/api/badges/macaron/csrf/status.svg)](https://drone.gitea.com/macaron/csrf) + +Middleware csrf generates and validates CSRF tokens for [Macaron](https://gitea.com/macaron/macaron). + +[API Reference](https://gowalker.org/gitea.com/macaron/csrf) + +### Installation + + go get gitea.com/macaron/csrf + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/csrf) +- [Documentation](http://go-macaron.com/docs/middlewares/csrf) + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/csrf/csrf.go b/vendor/gitea.com/macaron/csrf/csrf.go new file mode 100644 index 0000000000..66f5b40a8e --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/csrf.go @@ -0,0 +1,267 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package csrf is a middleware that generates and validates CSRF tokens for Macaron. +package csrf + +import ( + "net/http" + "time" + + "gitea.com/macaron/macaron" + "gitea.com/macaron/session" + "github.com/unknwon/com" +) + +const _VERSION = "0.1.1" + +func Version() string { + return _VERSION +} + +// CSRF represents a CSRF service and is used to get the current token and validate a suspect token. +type CSRF interface { + // Return HTTP header to search for token. + GetHeaderName() string + // Return form value to search for token. + GetFormName() string + // Return cookie name to search for token. + GetCookieName() string + // Return cookie path + GetCookiePath() string + // Return the flag value used for the csrf token. + GetCookieHttpOnly() bool + // Return the token. + GetToken() string + // Validate by token. + ValidToken(t string) bool + // Error replies to the request with a custom function when ValidToken fails. + Error(w http.ResponseWriter) +} + +type csrf struct { + // Header name value for setting and getting csrf token. + Header string + // Form name value for setting and getting csrf token. + Form string + // Cookie name value for setting and getting csrf token. + Cookie string + //Cookie domain + CookieDomain string + //Cookie path + CookiePath string + // Cookie HttpOnly flag value used for the csrf token. + CookieHttpOnly bool + // Token generated to pass via header, cookie, or hidden form value. + Token string + // This value must be unique per user. + ID string + // Secret used along with the unique id above to generate the Token. + Secret string + // ErrorFunc is the custom function that replies to the request when ValidToken fails. + ErrorFunc func(w http.ResponseWriter) +} + +// GetHeaderName returns the name of the HTTP header for csrf token. +func (c *csrf) GetHeaderName() string { + return c.Header +} + +// GetFormName returns the name of the form value for csrf token. +func (c *csrf) GetFormName() string { + return c.Form +} + +// GetCookieName returns the name of the cookie for csrf token. +func (c *csrf) GetCookieName() string { + return c.Cookie +} + +// GetCookiePath returns the path of the cookie for csrf token. +func (c *csrf) GetCookiePath() string { + return c.CookiePath +} + +// GetCookieHttpOnly returns the flag value used for the csrf token. +func (c *csrf) GetCookieHttpOnly() bool { + return c.CookieHttpOnly +} + +// GetToken returns the current token. This is typically used +// to populate a hidden form in an HTML template. +func (c *csrf) GetToken() string { + return c.Token +} + +// ValidToken validates the passed token against the existing Secret and ID. +func (c *csrf) ValidToken(t string) bool { + return ValidToken(t, c.Secret, c.ID, "POST") +} + +// Error replies to the request when ValidToken fails. +func (c *csrf) Error(w http.ResponseWriter) { + c.ErrorFunc(w) +} + +// Options maintains options to manage behavior of Generate. +type Options struct { + // The global secret value used to generate Tokens. + Secret string + // HTTP header used to set and get token. + Header string + // Form value used to set and get token. + Form string + // Cookie value used to set and get token. + Cookie string + // Cookie domain. + CookieDomain string + // Cookie path. + CookiePath string + CookieHttpOnly bool + // Key used for getting the unique ID per user. + SessionKey string + // oldSeesionKey saves old value corresponding to SessionKey. + oldSeesionKey string + // If true, send token via X-CSRFToken header. + SetHeader bool + // If true, send token via _csrf cookie. + SetCookie bool + // Set the Secure flag to true on the cookie. + Secure bool + // Disallow Origin appear in request header. + Origin bool + // The function called when Validate fails. + ErrorFunc func(w http.ResponseWriter) +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + // Defaults. + if len(opt.Secret) == 0 { + opt.Secret = string(com.RandomCreateBytes(10)) + } + if len(opt.Header) == 0 { + opt.Header = "X-CSRFToken" + } + if len(opt.Form) == 0 { + opt.Form = "_csrf" + } + if len(opt.Cookie) == 0 { + opt.Cookie = "_csrf" + } + if len(opt.CookiePath) == 0 { + opt.CookiePath = "/" + } + if len(opt.SessionKey) == 0 { + opt.SessionKey = "uid" + } + opt.oldSeesionKey = "_old_" + opt.SessionKey + if opt.ErrorFunc == nil { + opt.ErrorFunc = func(w http.ResponseWriter) { + http.Error(w, "Invalid csrf token.", http.StatusBadRequest) + } + } + + return opt +} + +// Generate maps CSRF to each request. If this request is a Get request, it will generate a new token. +// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie. +func Generate(options ...Options) macaron.Handler { + opt := prepareOptions(options) + return func(ctx *macaron.Context, sess session.Store) { + x := &csrf{ + Secret: opt.Secret, + Header: opt.Header, + Form: opt.Form, + Cookie: opt.Cookie, + CookieDomain: opt.CookieDomain, + CookiePath: opt.CookiePath, + CookieHttpOnly: opt.CookieHttpOnly, + ErrorFunc: opt.ErrorFunc, + } + ctx.MapTo(x, (*CSRF)(nil)) + + if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 { + return + } + + x.ID = "0" + uid := sess.Get(opt.SessionKey) + if uid != nil { + x.ID = com.ToStr(uid) + } + + needsNew := false + oldUid := sess.Get(opt.oldSeesionKey) + if oldUid == nil || oldUid.(string) != x.ID { + needsNew = true + sess.Set(opt.oldSeesionKey, x.ID) + } else { + // If cookie present, map existing token, else generate a new one. + if val := ctx.GetCookie(opt.Cookie); len(val) > 0 { + // FIXME: test coverage. + x.Token = val + } else { + needsNew = true + } + } + + if needsNew { + // FIXME: actionId. + x.Token = GenerateToken(x.Secret, x.ID, "POST") + if opt.SetCookie { + ctx.SetCookie(opt.Cookie, x.Token, 0, opt.CookiePath, opt.CookieDomain, opt.Secure, opt.CookieHttpOnly, time.Now().AddDate(0, 0, 1)) + } + } + + if opt.SetHeader { + ctx.Resp.Header().Add(opt.Header, x.Token) + } + } +} + +// Csrfer maps CSRF to each request. If this request is a Get request, it will generate a new token. +// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie. +func Csrfer(options ...Options) macaron.Handler { + return Generate(options...) +} + +// Validate should be used as a per route middleware. It attempts to get a token from a "X-CSRFToken" +// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated +// using ValidToken. If this validation fails, custom Error is sent in the reply. +// If neither a header or form value is found, http.StatusBadRequest is sent. +func Validate(ctx *macaron.Context, x CSRF) { + if token := ctx.Req.Header.Get(x.GetHeaderName()); len(token) > 0 { + if !x.ValidToken(token) { + ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath()) + x.Error(ctx.Resp) + } + return + } + if token := ctx.Req.FormValue(x.GetFormName()); len(token) > 0 { + if !x.ValidToken(token) { + ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath()) + x.Error(ctx.Resp) + } + return + } + + http.Error(ctx.Resp, "Bad Request: no CSRF token present", http.StatusBadRequest) +} diff --git a/vendor/gitea.com/macaron/csrf/go.mod b/vendor/gitea.com/macaron/csrf/go.mod new file mode 100644 index 0000000000..946cb6afff --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/go.mod @@ -0,0 +1,12 @@ +module gitea.com/macaron/csrf + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 + github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect + github.com/smartystreets/assertions v1.0.1 // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e +) diff --git a/vendor/gitea.com/macaron/csrf/go.sum b/vendor/gitea.com/macaron/csrf/go.sum new file mode 100644 index 0000000000..7919c9f8c3 --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/go.sum @@ -0,0 +1,83 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0= +gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= +github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= +github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gitea.com/macaron/csrf/xsrf.go b/vendor/gitea.com/macaron/csrf/xsrf.go new file mode 100644 index 0000000000..7f31894f95 --- /dev/null +++ b/vendor/gitea.com/macaron/csrf/xsrf.go @@ -0,0 +1,97 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package csrf + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "crypto/subtle" + "encoding/base64" + "fmt" + "strconv" + "strings" + "time" +) + +// The duration that XSRF tokens are valid. +// It is exported so clients may set cookie timeouts that match generated tokens. +const TIMEOUT = 24 * time.Hour + +// clean sanitizes a string for inclusion in a token by replacing all ":"s. +func clean(s string) string { + return strings.Replace(s, ":", "_", -1) +} + +// GenerateToken returns a URL-safe secure XSRF token that expires in 24 hours. +// +// key is a secret key for your application. +// userID is a unique identifier for the user. +// actionID is the action the user is taking (e.g. POSTing to a particular path). +func GenerateToken(key, userID, actionID string) string { + return generateTokenAtTime(key, userID, actionID, time.Now()) +} + +// generateTokenAtTime is like Generate, but returns a token that expires 24 hours from now. +func generateTokenAtTime(key, userID, actionID string, now time.Time) string { + h := hmac.New(sha1.New, []byte(key)) + fmt.Fprintf(h, "%s:%s:%d", clean(userID), clean(actionID), now.UnixNano()) + tok := fmt.Sprintf("%s:%d", h.Sum(nil), now.UnixNano()) + return base64.RawURLEncoding.EncodeToString([]byte(tok)) +} + +// Valid returns true if token is a valid, unexpired token returned by Generate. +func ValidToken(token, key, userID, actionID string) bool { + return validTokenAtTime(token, key, userID, actionID, time.Now()) +} + +// validTokenAtTime is like Valid, but it uses now to check if the token is expired. +func validTokenAtTime(token, key, userID, actionID string, now time.Time) bool { + // Decode the token. + data, err := base64.RawURLEncoding.DecodeString(token) + if err != nil { + return false + } + + // Extract the issue time of the token. + sep := bytes.LastIndex(data, []byte{':'}) + if sep < 0 { + return false + } + nanos, err := strconv.ParseInt(string(data[sep+1:]), 10, 64) + if err != nil { + return false + } + issueTime := time.Unix(0, nanos) + + // Check that the token is not expired. + if now.Sub(issueTime) >= TIMEOUT { + return false + } + + // Check that the token is not from the future. + // Allow 1 minute grace period in case the token is being verified on a + // machine whose clock is behind the machine that issued the token. + if issueTime.After(now.Add(1 * time.Minute)) { + return false + } + + expected := generateTokenAtTime(key, userID, actionID, issueTime) + + // Check that the token matches the expected value. + // Use constant time comparison to avoid timing attacks. + return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1 +} diff --git a/vendor/gitea.com/macaron/i18n/.drone.yml b/vendor/gitea.com/macaron/i18n/.drone.yml new file mode 100644 index 0000000000..3f2a434892 --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: golang-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: golang-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/i18n/LICENSE b/vendor/gitea.com/macaron/i18n/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/i18n/README.md b/vendor/gitea.com/macaron/i18n/README.md new file mode 100644 index 0000000000..bff89d9b6c --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/README.md @@ -0,0 +1,16 @@ +# i18n [![Build Status](https://drone.gitea.com/api/badges/macaron/i18n/status.svg)](https://drone.gitea.com/macaron/i18n) [![](http://gocover.io/_badge/github.com/go-macaron/i18n)](http://gocover.io/github.com/go-macaron/i18n) + +Middleware i18n provides app Internationalization and Localization for [Macaron](https://gitea.com/macaron/macaron). + +### Installation + + go get gitea.com/macaron/i18n + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/i18n) +- [Documentation](http://go-macaron.com/docs/middlewares/i18n) + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/i18n/go.mod b/vendor/gitea.com/macaron/i18n/go.mod new file mode 100644 index 0000000000..03577c6e88 --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/go.mod @@ -0,0 +1,11 @@ +module gitea.com/macaron/i18n + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e + github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 + golang.org/x/text v0.3.2 +) diff --git a/vendor/gitea.com/macaron/i18n/go.sum b/vendor/gitea.com/macaron/i18n/go.sum new file mode 100644 index 0000000000..c96916d448 --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/go.sum @@ -0,0 +1,35 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 h1:sRrkJEHtNoaSvyXMbRgofEOX4/3gMiraevQKJdIBhYE= +github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag= +gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gitea.com/macaron/i18n/i18n.go b/vendor/gitea.com/macaron/i18n/i18n.go new file mode 100644 index 0000000000..fd114a5bf7 --- /dev/null +++ b/vendor/gitea.com/macaron/i18n/i18n.go @@ -0,0 +1,227 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package i18n is a middleware that provides app Internationalization and Localization of Macaron. +package i18n + +import ( + "fmt" + "path" + "strings" + + "gitea.com/macaron/macaron" + "github.com/unknwon/com" + "github.com/unknwon/i18n" + "golang.org/x/text/language" +) + +const _VERSION = "0.4.0" + +func Version() string { + return _VERSION +} + +// initLocales initializes language type list and Accept-Language header matcher. +func initLocales(opt Options) language.Matcher { + tags := make([]language.Tag, len(opt.Langs)) + for i, lang := range opt.Langs { + tags[i] = language.Raw.Make(lang) + fname := fmt.Sprintf(opt.Format, lang) + // Append custom locale file. + custom := []interface{}{} + customPath := path.Join(opt.CustomDirectory, fname) + if com.IsFile(customPath) { + custom = append(custom, customPath) + } + + var locale interface{} + if data, ok := opt.Files[fname]; ok { + locale = data + } else { + locale = path.Join(opt.Directory, fname) + } + + err := i18n.SetMessageWithDesc(lang, opt.Names[i], locale, custom...) + if err != nil && err != i18n.ErrLangAlreadyExist { + panic(fmt.Errorf("fail to set message file(%s): %v", lang, err)) + } + } + return language.NewMatcher(tags) +} + +// A Locale describles the information of localization. +type Locale struct { + i18n.Locale +} + +// Language returns language current locale represents. +func (l Locale) Language() string { + return l.Lang +} + +// Options represents a struct for specifying configuration options for the i18n middleware. +type Options struct { + // Suburl of path. Default is empty. + SubURL string + // Directory to load locale files. Default is "conf/locale" + Directory string + // File stores actual data of locale files. Used for in-memory purpose. + Files map[string][]byte + // Custom directory to overload locale files. Default is "custom/conf/locale" + CustomDirectory string + // Langauges that will be supported, order is meaningful. + Langs []string + // Human friendly names corresponding to Langs list. + Names []string + // Default language locale, leave empty to remain unset. + DefaultLang string + // Locale file naming style. Default is "locale_%s.ini". + Format string + // Name of language parameter name in URL. Default is "lang". + Parameter string + // Redirect when user uses get parameter to specify language. + Redirect bool + // Name that maps into template variable. Default is "i18n". + TmplName string + // Configuration section name. Default is "i18n". + Section string + // Domain used for `lang` cookie. Default is "" + CookieDomain string +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + if len(opt.Section) == 0 { + opt.Section = "i18n" + } + sec := macaron.Config().Section(opt.Section) + + opt.SubURL = strings.TrimSuffix(opt.SubURL, "/") + + if len(opt.Langs) == 0 { + opt.Langs = sec.Key("LANGS").Strings(",") + } + if len(opt.Names) == 0 { + opt.Names = sec.Key("NAMES").Strings(",") + } + if len(opt.Langs) == 0 { + panic("no language is specified") + } else if len(opt.Langs) != len(opt.Names) { + panic("length of langs is not same as length of names") + } + i18n.SetDefaultLang(opt.DefaultLang) + + if len(opt.Directory) == 0 { + opt.Directory = sec.Key("DIRECTORY").MustString("conf/locale") + } + if len(opt.CustomDirectory) == 0 { + opt.CustomDirectory = sec.Key("CUSTOM_DIRECTORY").MustString("custom/conf/locale") + } + if len(opt.Format) == 0 { + opt.Format = sec.Key("FORMAT").MustString("locale_%s.ini") + } + if len(opt.Parameter) == 0 { + opt.Parameter = sec.Key("PARAMETER").MustString("lang") + } + if !opt.Redirect { + opt.Redirect = sec.Key("REDIRECT").MustBool() + } + if len(opt.TmplName) == 0 { + opt.TmplName = sec.Key("TMPL_NAME").MustString("i18n") + } + + return opt +} + +type LangType struct { + Lang, Name string +} + +// I18n is a middleware provides localization layer for your application. +// Paramenter langs must be in the form of "en-US", "zh-CN", etc. +// Otherwise it may not recognize browser input. +func I18n(options ...Options) macaron.Handler { + opt := prepareOptions(options) + m := initLocales(opt) + return func(ctx *macaron.Context) { + isNeedRedir := false + hasCookie := false + + // 1. Check URL arguments. + lang := ctx.Query(opt.Parameter) + + // 2. Get language information from cookies. + if len(lang) == 0 { + lang = ctx.GetCookie("lang") + hasCookie = true + } else { + isNeedRedir = true + } + + // Check again in case someone modify by purpose. + if !i18n.IsExist(lang) { + lang = "" + isNeedRedir = false + hasCookie = false + } + + // 3. Get language information from 'Accept-Language'. + // The first element in the list is chosen to be the default language automatically. + if len(lang) == 0 { + tags, _, _ := language.ParseAcceptLanguage(ctx.Req.Header.Get("Accept-Language")) + tag, _, _ := m.Match(tags...) + lang = tag.String() + isNeedRedir = false + } + + curLang := LangType{ + Lang: lang, + } + + // Save language information in cookies. + if !hasCookie { + ctx.SetCookie("lang", curLang.Lang, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"), opt.CookieDomain) + } + + restLangs := make([]LangType, 0, i18n.Count()-1) + langs := i18n.ListLangs() + names := i18n.ListLangDescs() + for i, v := range langs { + if lang != v { + restLangs = append(restLangs, LangType{v, names[i]}) + } else { + curLang.Name = names[i] + } + } + + // Set language properties. + locale := Locale{i18n.Locale{lang}} + ctx.Map(locale) + ctx.Locale = locale + ctx.Data[opt.TmplName] = locale + ctx.Data["Tr"] = i18n.Tr + ctx.Data["Lang"] = locale.Lang + ctx.Data["LangName"] = curLang.Name + ctx.Data["AllLangs"] = append([]LangType{curLang}, restLangs...) + ctx.Data["RestLangs"] = restLangs + + if opt.Redirect && isNeedRedir { + ctx.Redirect(opt.SubURL + ctx.Req.RequestURI[:strings.Index(ctx.Req.RequestURI, "?")]) + } + } +} diff --git a/vendor/gitea.com/macaron/inject/.drone.yml b/vendor/gitea.com/macaron/inject/.drone.yml new file mode 100644 index 0000000000..4db3bab535 --- /dev/null +++ b/vendor/gitea.com/macaron/inject/.drone.yml @@ -0,0 +1,20 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic diff --git a/vendor/gitea.com/macaron/inject/LICENSE b/vendor/gitea.com/macaron/inject/LICENSE new file mode 100644 index 0000000000..37ec93a14f --- /dev/null +++ b/vendor/gitea.com/macaron/inject/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gitea.com/macaron/inject/README.md b/vendor/gitea.com/macaron/inject/README.md new file mode 100644 index 0000000000..5ed58bae52 --- /dev/null +++ b/vendor/gitea.com/macaron/inject/README.md @@ -0,0 +1,11 @@ +# inject [![Build Status](https://drone.gitea.com/api/badges/macaron/inject/status.svg)](https://drone.gitea.com/macaron/inject) [![](http://gocover.io/_badge/gitea.com/macaron/inject)](http://gocover.io/gitea.com/macaron/inject) + +Package inject provides utilities for mapping and injecting dependencies in various ways. + +**This a modified version of [codegangsta/inject](https://github.com/codegangsta/inject) for special purpose of Macaron** + +**Please use the original version if you need dependency injection feature** + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/inject/go.mod b/vendor/gitea.com/macaron/inject/go.mod new file mode 100644 index 0000000000..7d885b7008 --- /dev/null +++ b/vendor/gitea.com/macaron/inject/go.mod @@ -0,0 +1,5 @@ +module gitea.com/macaron/inject + +go 1.11 + +require github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 diff --git a/vendor/gitea.com/macaron/inject/go.sum b/vendor/gitea.com/macaron/inject/go.sum new file mode 100644 index 0000000000..6a9ccb166c --- /dev/null +++ b/vendor/gitea.com/macaron/inject/go.sum @@ -0,0 +1,13 @@ +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/vendor/gitea.com/macaron/inject/inject.go b/vendor/gitea.com/macaron/inject/inject.go new file mode 100644 index 0000000000..1c1f98eaaa --- /dev/null +++ b/vendor/gitea.com/macaron/inject/inject.go @@ -0,0 +1,262 @@ +// Copyright 2013 Jeremy Saenz +// Copyright 2015 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package inject provides utilities for mapping and injecting dependencies in various ways. +package inject + +import ( + "fmt" + "reflect" +) + +// Injector represents an interface for mapping and injecting dependencies into structs +// and function arguments. +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} + +// Applicator represents an interface for mapping dependencies to a struct. +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} + +// Invoker represents an interface for calling functions via reflection. +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} + +// FastInvoker represents an interface in order to avoid the calling function via reflection. +// +// example: +// type handlerFuncHandler func(http.ResponseWriter, *http.Request) error +// func (f handlerFuncHandler)Invoke([]interface{}) ([]reflect.Value, error){ +// ret := f(p[0].(http.ResponseWriter), p[1].(*http.Request)) +// return []reflect.Value{reflect.ValueOf(ret)}, nil +// } +// +// type funcHandler func(int, string) +// func (f funcHandler)Invoke([]interface{}) ([]reflect.Value, error){ +// f(p[0].(int), p[1].(string)) +// return nil, nil +// } +type FastInvoker interface { + // Invoke attempts to call the ordinary functions. If f is a function + // with the appropriate signature, f.Invoke([]interface{}) is a Call that calls f. + // Returns a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke([]interface{}) ([]reflect.Value, error) +} + +// IsFastInvoker check interface is FastInvoker +func IsFastInvoker(h interface{}) bool { + _, ok := h.(FastInvoker) + return ok +} + +// TypeMapper represents an interface for mapping interface{} values based on type. +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + GetVal(reflect.Type) reflect.Value +} + +type injector struct { + values map[reflect.Type]reflect.Value + parent Injector +} + +// InterfaceOf dereferences a pointer to an Interface type. +// It panics if value is not an pointer to an interface. +func InterfaceOf(value interface{}) reflect.Type { + t := reflect.TypeOf(value) + + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + if t.Kind() != reflect.Interface { + panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") + } + + return t +} + +// New returns a new Injector. +func New() Injector { + return &injector{ + values: make(map[reflect.Type]reflect.Value), + } +} + +// Invoke attempts to call the interface{} provided as a function, +// providing dependencies for function arguments based on Type. +// Returns a slice of reflect.Value representing the returned values of the function. +// Returns an error if the injection fails. +// It panics if f is not a function +func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { + t := reflect.TypeOf(f) + switch v := f.(type) { + case FastInvoker: + return inj.fastInvoke(v, t, t.NumIn()) + default: + return inj.callInvoke(f, t, t.NumIn()) + } +} + +func (inj *injector) fastInvoke(f FastInvoker, t reflect.Type, numIn int) ([]reflect.Value, error) { + var in []interface{} + if numIn > 0 { + in = make([]interface{}, numIn) // Panic if t is not kind of Func + var argType reflect.Type + var val reflect.Value + for i := 0; i < numIn; i++ { + argType = t.In(i) + val = inj.GetVal(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val.Interface() + } + } + return f.Invoke(in) +} + +// callInvoke reflect.Value.Call +func (inj *injector) callInvoke(f interface{}, t reflect.Type, numIn int) ([]reflect.Value, error) { + var in []reflect.Value + if numIn > 0 { + in = make([]reflect.Value, numIn) + var argType reflect.Type + var val reflect.Value + for i := 0; i < numIn; i++ { + argType = t.In(i) + val = inj.GetVal(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val + } + } + return reflect.ValueOf(f).Call(in), nil +} + +// Maps dependencies in the Type map to each field in the struct +// that is tagged with 'inject'. +// Returns an error if the injection fails. +func (inj *injector) Apply(val interface{}) error { + v := reflect.ValueOf(val) + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil // Should not panic here ? + } + + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + structField := t.Field(i) + if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { + ft := f.Type() + v := inj.GetVal(ft) + if !v.IsValid() { + return fmt.Errorf("Value not found for type %v", ft) + } + + f.Set(v) + } + + } + + return nil +} + +// Maps the concrete value of val to its dynamic type using reflect.TypeOf, +// It returns the TypeMapper registered in. +func (i *injector) Map(val interface{}) TypeMapper { + i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) + return i +} + +func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { + i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) + return i +} + +// Maps the given reflect.Type to the given reflect.Value and returns +// the Typemapper the mapping has been registered in. +func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { + i.values[typ] = val + return i +} + +func (i *injector) GetVal(t reflect.Type) reflect.Value { + val := i.values[t] + + if val.IsValid() { + return val + } + + // no concrete types found, try to find implementors + // if t is an interface + if t.Kind() == reflect.Interface { + for k, v := range i.values { + if k.Implements(t) { + val = v + break + } + } + } + + // Still no type found, try to look it up on the parent + if !val.IsValid() && i.parent != nil { + val = i.parent.GetVal(t) + } + + return val + +} + +func (i *injector) SetParent(parent Injector) { + i.parent = parent +} diff --git a/vendor/gitea.com/macaron/macaron/.drone.yml b/vendor/gitea.com/macaron/macaron/.drone.yml new file mode 100644 index 0000000000..06ecc018e3 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/.drone.yml @@ -0,0 +1,10 @@ +kind: pipeline +name: default + +steps: +- name: test + image: golang:1.12 + commands: + - go get -u + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic diff --git a/vendor/gitea.com/macaron/macaron/.gitignore b/vendor/gitea.com/macaron/macaron/.gitignore new file mode 100644 index 0000000000..fc5aca3e47 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/.gitignore @@ -0,0 +1,3 @@ +macaron.sublime-project +macaron.sublime-workspace +.idea diff --git a/vendor/gitea.com/macaron/macaron/LICENSE b/vendor/gitea.com/macaron/macaron/LICENSE new file mode 100644 index 0000000000..c8a16eb2eb --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2014 The Macaron Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gitea.com/macaron/macaron/README.md b/vendor/gitea.com/macaron/macaron/README.md new file mode 100644 index 0000000000..97b782ed87 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/README.md @@ -0,0 +1,93 @@ +Macaron [![Build Status](https://travis-ci.org/go-macaron/macaron.svg?branch=v1)](https://travis-ci.org/go-macaron/macaron) +======================= + +![Macaron Logo](https://raw.githubusercontent.com/go-macaron/macaron/v1/macaronlogo.png) + +Package macaron is a high productive and modular web framework in Go. + +## Getting Started + +The minimum requirement of Go is **1.6**. + +To install Macaron: + + go get gitea.com/macaron/macaron + +The very basic usage of Macaron: + +```go +package main + +import "gitea.com/macaron/macaron" + +func main() { + m := macaron.Classic() + m.Get("/", func() string { + return "Hello world!" + }) + m.Run() +} +``` + +## Features + +- Powerful routing with suburl. +- Flexible routes combinations. +- Unlimited nested group routers. +- Directly integrate with existing services. +- Dynamically change template files at runtime. +- Allow to use in-memory template and static files. +- Easy to plugin/unplugin features with modular design. +- Handy dependency injection powered by [inject](https://github.com/codegangsta/inject). +- Better router layer and less reflection make faster speed. + +## Middlewares + +Middlewares allow you easily plugin/unplugin features for your Macaron applications. + +There are already many [middlewares](https://github.com/go-macaron) to simplify your work: + +- render - Go template engine +- static - Serves static files +- [gzip](https://github.com/go-macaron/gzip) - Gzip compression to all responses +- [binding](https://github.com/go-macaron/binding) - Request data binding and validation +- [i18n](https://github.com/go-macaron/i18n) - Internationalization and Localization +- [cache](https://github.com/go-macaron/cache) - Cache manager +- [session](https://github.com/go-macaron/session) - Session manager +- [csrf](https://github.com/go-macaron/csrf) - Generates and validates csrf tokens +- [captcha](https://github.com/go-macaron/captcha) - Captcha service +- [pongo2](https://github.com/go-macaron/pongo2) - Pongo2 template engine support +- [sockets](https://github.com/go-macaron/sockets) - WebSockets channels binding +- [bindata](https://github.com/go-macaron/bindata) - Embed binary data as static and template files +- [toolbox](https://github.com/go-macaron/toolbox) - Health check, pprof, profile and statistic services +- [oauth2](https://github.com/go-macaron/oauth2) - OAuth 2.0 backend +- [authz](https://github.com/go-macaron/authz) - ACL/RBAC/ABAC authorization based on Casbin +- [switcher](https://github.com/go-macaron/switcher) - Multiple-site support +- [method](https://github.com/go-macaron/method) - HTTP method override +- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions +- [renders](https://github.com/go-macaron/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option) +- [piwik](https://github.com/veecue/piwik-middleware) - Server-side piwik analytics + +## Use Cases + +- [Gogs](https://gogs.io): A painless self-hosted Git Service +- [Grafana](http://grafana.org/): The open platform for beautiful analytics and monitoring +- [Peach](https://peachdocs.org): A modern web documentation server +- [Go Walker](https://gowalker.org): Go online API documentation +- [Switch](https://gopm.io): Gopm registry +- [Critical Stack Intel](https://intel.criticalstack.com/): A 100% free intel marketplace from Critical Stack, Inc. + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/macaron) +- [Documentation](https://go-macaron.com) +- [FAQs](https://go-macaron.com/docs/faqs) + +## Credits + +- Basic design of [Martini](https://github.com/go-martini/martini). +- Logo is modified by [@insionng](https://github.com/insionng) based on [Tribal Dragon](http://xtremeyamazaki.deviantart.com/art/Tribal-Dragon-27005087). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. diff --git a/vendor/gitea.com/macaron/macaron/context.go b/vendor/gitea.com/macaron/macaron/context.go new file mode 100644 index 0000000000..a3f2c69e03 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/context.go @@ -0,0 +1,532 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "crypto/sha256" + "encoding/hex" + "html/template" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "reflect" + "strconv" + "strings" + "time" + + "gitea.com/macaron/inject" + "github.com/unknwon/com" + "golang.org/x/crypto/pbkdf2" +) + +// Locale reprents a localization interface. +type Locale interface { + Language() string + Tr(string, ...interface{}) string +} + +// RequestBody represents a request body. +type RequestBody struct { + reader io.ReadCloser +} + +// Bytes reads and returns content of request body in bytes. +func (rb *RequestBody) Bytes() ([]byte, error) { + return ioutil.ReadAll(rb.reader) +} + +// String reads and returns content of request body in string. +func (rb *RequestBody) String() (string, error) { + data, err := rb.Bytes() + return string(data), err +} + +// ReadCloser returns a ReadCloser for request body. +func (rb *RequestBody) ReadCloser() io.ReadCloser { + return rb.reader +} + +// Request represents an HTTP request received by a server or to be sent by a client. +type Request struct { + *http.Request +} + +func (r *Request) Body() *RequestBody { + return &RequestBody{r.Request.Body} +} + +// ContextInvoker is an inject.FastInvoker wrapper of func(ctx *Context). +type ContextInvoker func(ctx *Context) + +func (invoke ContextInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(*Context)) + return nil, nil +} + +// Context represents the runtime context of current request of Macaron instance. +// It is the integration of most frequently used middlewares and helper methods. +type Context struct { + inject.Injector + handlers []Handler + action Handler + index int + + *Router + Req Request + Resp ResponseWriter + params Params + Render + Locale + Data map[string]interface{} +} + +func (c *Context) handler() Handler { + if c.index < len(c.handlers) { + return c.handlers[c.index] + } + if c.index == len(c.handlers) { + return c.action + } + panic("invalid index for context handler") +} + +func (c *Context) Next() { + c.index += 1 + c.run() +} + +func (c *Context) Written() bool { + return c.Resp.Written() +} + +func (c *Context) run() { + for c.index <= len(c.handlers) { + vals, err := c.Invoke(c.handler()) + if err != nil { + panic(err) + } + c.index += 1 + + // if the handler returned something, write it to the http response + if len(vals) > 0 { + ev := c.GetVal(reflect.TypeOf(ReturnHandler(nil))) + handleReturn := ev.Interface().(ReturnHandler) + handleReturn(c, vals) + } + + if c.Written() { + return + } + } +} + +// RemoteAddr returns more real IP address. +func (ctx *Context) RemoteAddr() string { + addr := ctx.Req.Header.Get("X-Real-IP") + if len(addr) == 0 { + addr = ctx.Req.Header.Get("X-Forwarded-For") + if addr == "" { + addr = ctx.Req.RemoteAddr + if i := strings.LastIndex(addr, ":"); i > -1 { + addr = addr[:i] + } + } + } + return addr +} + +func (ctx *Context) renderHTML(status int, setName, tplName string, data ...interface{}) { + if len(data) <= 0 { + ctx.Render.HTMLSet(status, setName, tplName, ctx.Data) + } else if len(data) == 1 { + ctx.Render.HTMLSet(status, setName, tplName, data[0]) + } else { + ctx.Render.HTMLSet(status, setName, tplName, data[0], data[1].(HTMLOptions)) + } +} + +// HTML renders the HTML with default template set. +func (ctx *Context) HTML(status int, name string, data ...interface{}) { + ctx.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data...) +} + +// HTMLSet renders the HTML with given template set name. +func (ctx *Context) HTMLSet(status int, setName, tplName string, data ...interface{}) { + ctx.renderHTML(status, setName, tplName, data...) +} + +func (ctx *Context) Redirect(location string, status ...int) { + code := http.StatusFound + if len(status) == 1 { + code = status[0] + } + + http.Redirect(ctx.Resp, ctx.Req.Request, location, code) +} + +// Maximum amount of memory to use when parsing a multipart form. +// Set this to whatever value you prefer; default is 10 MB. +var MaxMemory = int64(1024 * 1024 * 10) + +func (ctx *Context) parseForm() { + if ctx.Req.Form != nil { + return + } + + contentType := ctx.Req.Header.Get(_CONTENT_TYPE) + if (ctx.Req.Method == "POST" || ctx.Req.Method == "PUT") && + len(contentType) > 0 && strings.Contains(contentType, "multipart/form-data") { + ctx.Req.ParseMultipartForm(MaxMemory) + } else { + ctx.Req.ParseForm() + } +} + +// Query querys form parameter. +func (ctx *Context) Query(name string) string { + ctx.parseForm() + return ctx.Req.Form.Get(name) +} + +// QueryTrim querys and trims spaces form parameter. +func (ctx *Context) QueryTrim(name string) string { + return strings.TrimSpace(ctx.Query(name)) +} + +// QueryStrings returns a list of results by given query name. +func (ctx *Context) QueryStrings(name string) []string { + ctx.parseForm() + + vals, ok := ctx.Req.Form[name] + if !ok { + return []string{} + } + return vals +} + +// QueryEscape returns escapred query result. +func (ctx *Context) QueryEscape(name string) string { + return template.HTMLEscapeString(ctx.Query(name)) +} + +// QueryBool returns query result in bool type. +func (ctx *Context) QueryBool(name string) bool { + v, _ := strconv.ParseBool(ctx.Query(name)) + return v +} + +// QueryInt returns query result in int type. +func (ctx *Context) QueryInt(name string) int { + return com.StrTo(ctx.Query(name)).MustInt() +} + +// QueryInt64 returns query result in int64 type. +func (ctx *Context) QueryInt64(name string) int64 { + return com.StrTo(ctx.Query(name)).MustInt64() +} + +// QueryFloat64 returns query result in float64 type. +func (ctx *Context) QueryFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.Query(name), 64) + return v +} + +// Params returns value of given param name. +// e.g. ctx.Params(":uid") or ctx.Params("uid") +func (ctx *Context) Params(name string) string { + if len(name) == 0 { + return "" + } + if len(name) > 1 && name[0] != ':' { + name = ":" + name + } + return ctx.params[name] +} + +// SetParams sets value of param with given name. +func (ctx *Context) SetParams(name, val string) { + if name != "*" && !strings.HasPrefix(name, ":") { + name = ":" + name + } + ctx.params[name] = val +} + +// ReplaceAllParams replace all current params with given params +func (ctx *Context) ReplaceAllParams(params Params) { + ctx.params = params +} + +// ParamsEscape returns escapred params result. +// e.g. ctx.ParamsEscape(":uname") +func (ctx *Context) ParamsEscape(name string) string { + return template.HTMLEscapeString(ctx.Params(name)) +} + +// ParamsInt returns params result in int type. +// e.g. ctx.ParamsInt(":uid") +func (ctx *Context) ParamsInt(name string) int { + return com.StrTo(ctx.Params(name)).MustInt() +} + +// ParamsInt64 returns params result in int64 type. +// e.g. ctx.ParamsInt64(":uid") +func (ctx *Context) ParamsInt64(name string) int64 { + return com.StrTo(ctx.Params(name)).MustInt64() +} + +// ParamsFloat64 returns params result in int64 type. +// e.g. ctx.ParamsFloat64(":uid") +func (ctx *Context) ParamsFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.Params(name), 64) + return v +} + +// GetFile returns information about user upload file by given form field name. +func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error) { + return ctx.Req.FormFile(name) +} + +// SaveToFile reads a file from request by field name and saves to given path. +func (ctx *Context) SaveToFile(name, savePath string) error { + fr, _, err := ctx.GetFile(name) + if err != nil { + return err + } + defer fr.Close() + + fw, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer fw.Close() + + _, err = io.Copy(fw, fr) + return err +} + +// SetCookie sets given cookie value to response header. +// FIXME: IE support? http://golanghome.com/post/620#reply2 +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { + cookie := http.Cookie{} + cookie.Name = name + cookie.Value = url.QueryEscape(value) + + if len(others) > 0 { + switch v := others[0].(type) { + case int: + cookie.MaxAge = v + case int64: + cookie.MaxAge = int(v) + case int32: + cookie.MaxAge = int(v) + } + } + + cookie.Path = "/" + if len(others) > 1 { + if v, ok := others[1].(string); ok && len(v) > 0 { + cookie.Path = v + } + } + + if len(others) > 2 { + if v, ok := others[2].(string); ok && len(v) > 0 { + cookie.Domain = v + } + } + + if len(others) > 3 { + switch v := others[3].(type) { + case bool: + cookie.Secure = v + default: + if others[3] != nil { + cookie.Secure = true + } + } + } + + if len(others) > 4 { + if v, ok := others[4].(bool); ok && v { + cookie.HttpOnly = true + } + } + + if len(others) > 5 { + if v, ok := others[5].(time.Time); ok { + cookie.Expires = v + cookie.RawExpires = v.Format(time.UnixDate) + } + } + + ctx.Resp.Header().Add("Set-Cookie", cookie.String()) +} + +// GetCookie returns given cookie value from request header. +func (ctx *Context) GetCookie(name string) string { + cookie, err := ctx.Req.Cookie(name) + if err != nil { + return "" + } + val, _ := url.QueryUnescape(cookie.Value) + return val +} + +// GetCookieInt returns cookie result in int type. +func (ctx *Context) GetCookieInt(name string) int { + return com.StrTo(ctx.GetCookie(name)).MustInt() +} + +// GetCookieInt64 returns cookie result in int64 type. +func (ctx *Context) GetCookieInt64(name string) int64 { + return com.StrTo(ctx.GetCookie(name)).MustInt64() +} + +// GetCookieFloat64 returns cookie result in float64 type. +func (ctx *Context) GetCookieFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64) + return v +} + +var defaultCookieSecret string + +// SetDefaultCookieSecret sets global default secure cookie secret. +func (m *Macaron) SetDefaultCookieSecret(secret string) { + defaultCookieSecret = secret +} + +// SetSecureCookie sets given cookie value to response header with default secret string. +func (ctx *Context) SetSecureCookie(name, value string, others ...interface{}) { + ctx.SetSuperSecureCookie(defaultCookieSecret, name, value, others...) +} + +// GetSecureCookie returns given cookie value from request header with default secret string. +func (ctx *Context) GetSecureCookie(key string) (string, bool) { + return ctx.GetSuperSecureCookie(defaultCookieSecret, key) +} + +// SetSuperSecureCookie sets given cookie value to response header with secret string. +func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) { + key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) + text, err := com.AESGCMEncrypt(key, []byte(value)) + if err != nil { + panic("error encrypting cookie: " + err.Error()) + } + + ctx.SetCookie(name, hex.EncodeToString(text), others...) +} + +// GetSuperSecureCookie returns given cookie value from request header with secret string. +func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) { + val := ctx.GetCookie(name) + if val == "" { + return "", false + } + + text, err := hex.DecodeString(val) + if err != nil { + return "", false + } + + key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) + text, err = com.AESGCMDecrypt(key, text) + return string(text), err == nil +} + +func (ctx *Context) setRawContentHeader() { + ctx.Resp.Header().Set("Content-Description", "Raw content") + ctx.Resp.Header().Set("Content-Type", "text/plain") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") +} + +// ServeContent serves given content to response. +func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { + modtime := time.Now() + for _, p := range params { + switch v := p.(type) { + case time.Time: + modtime = v + } + } + + ctx.setRawContentHeader() + http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r) +} + +// ServeFileContent serves given file as content to response. +func (ctx *Context) ServeFileContent(file string, names ...string) { + var name string + if len(names) > 0 { + name = names[0] + } else { + name = path.Base(file) + } + + f, err := os.Open(file) + if err != nil { + if Env == PROD { + http.Error(ctx.Resp, "Internal Server Error", 500) + } else { + http.Error(ctx.Resp, err.Error(), 500) + } + return + } + defer f.Close() + + ctx.setRawContentHeader() + http.ServeContent(ctx.Resp, ctx.Req.Request, name, time.Now(), f) +} + +// ServeFile serves given file to response. +func (ctx *Context) ServeFile(file string, names ...string) { + var name string + if len(names) > 0 { + name = names[0] + } else { + name = path.Base(file) + } + ctx.Resp.Header().Set("Content-Description", "File Transfer") + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) + ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") + http.ServeFile(ctx.Resp, ctx.Req.Request, file) +} + +// ChangeStaticPath changes static path from old to new one. +func (ctx *Context) ChangeStaticPath(oldPath, newPath string) { + if !filepath.IsAbs(oldPath) { + oldPath = filepath.Join(Root, oldPath) + } + dir := statics.Get(oldPath) + if dir != nil { + statics.Delete(oldPath) + + if !filepath.IsAbs(newPath) { + newPath = filepath.Join(Root, newPath) + } + *dir = http.Dir(newPath) + statics.Set(dir) + } +} diff --git a/vendor/gitea.com/macaron/macaron/go.mod b/vendor/gitea.com/macaron/macaron/go.mod new file mode 100644 index 0000000000..e256ae8f63 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/go.mod @@ -0,0 +1,11 @@ +module gitea.com/macaron/macaron + +go 1.11 + +require ( + gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + gopkg.in/ini.v1 v1.44.0 +) diff --git a/vendor/gitea.com/macaron/macaron/go.sum b/vendor/gitea.com/macaron/macaron/go.sum new file mode 100644 index 0000000000..7dfc6ea74e --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/go.sum @@ -0,0 +1,27 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gitea.com/macaron/macaron/logger.go b/vendor/gitea.com/macaron/macaron/logger.go new file mode 100644 index 0000000000..34178d78a6 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/logger.go @@ -0,0 +1,73 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "fmt" + "log" + "net/http" + "reflect" + "runtime" + "time" +) + +var ( + ColorLog = true + LogTimeFormat = "2006-01-02 15:04:05" +) + +func init() { + ColorLog = runtime.GOOS != "windows" +} + +// LoggerInvoker is an inject.FastInvoker wrapper of func(ctx *Context, log *log.Logger). +type LoggerInvoker func(ctx *Context, log *log.Logger) + +func (invoke LoggerInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(*Context), params[1].(*log.Logger)) + return nil, nil +} + +// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. +func Logger() Handler { + return func(ctx *Context, log *log.Logger) { + start := time.Now() + + log.Printf("%s: Started %s %s for %s", time.Now().Format(LogTimeFormat), ctx.Req.Method, ctx.Req.RequestURI, ctx.RemoteAddr()) + + rw := ctx.Resp.(ResponseWriter) + ctx.Next() + + content := fmt.Sprintf("%s: Completed %s %s %v %s in %v", time.Now().Format(LogTimeFormat), ctx.Req.Method, ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start)) + if ColorLog { + switch rw.Status() { + case 200, 201, 202: + content = fmt.Sprintf("\033[1;32m%s\033[0m", content) + case 301, 302: + content = fmt.Sprintf("\033[1;37m%s\033[0m", content) + case 304: + content = fmt.Sprintf("\033[1;33m%s\033[0m", content) + case 401, 403: + content = fmt.Sprintf("\033[4;31m%s\033[0m", content) + case 404: + content = fmt.Sprintf("\033[1;31m%s\033[0m", content) + case 500: + content = fmt.Sprintf("\033[1;36m%s\033[0m", content) + } + } + log.Println(content) + } +} diff --git a/vendor/gitea.com/macaron/macaron/macaron.go b/vendor/gitea.com/macaron/macaron/macaron.go new file mode 100644 index 0000000000..17b5783702 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/macaron.go @@ -0,0 +1,334 @@ +// +build go1.3 + +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package macaron is a high productive and modular web framework in Go. +package macaron + +import ( + "io" + "log" + "net/http" + "os" + "reflect" + "strings" + "sync" + + "github.com/unknwon/com" + "gopkg.in/ini.v1" + + "gitea.com/macaron/inject" +) + +const _VERSION = "1.3.2.1216" + +func Version() string { + return _VERSION +} + +// Handler can be any callable function. +// Macaron attempts to inject services into the handler's argument list, +// and panics if an argument could not be fullfilled via dependency injection. +type Handler interface{} + +// handlerFuncInvoker is an inject.FastInvoker wrapper of func(http.ResponseWriter, *http.Request). +type handlerFuncInvoker func(http.ResponseWriter, *http.Request) + +func (invoke handlerFuncInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(http.ResponseWriter), params[1].(*http.Request)) + return nil, nil +} + +// internalServerErrorInvoker is an inject.FastInvoker wrapper of func(rw http.ResponseWriter, err error). +type internalServerErrorInvoker func(rw http.ResponseWriter, err error) + +func (invoke internalServerErrorInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(http.ResponseWriter), params[1].(error)) + return nil, nil +} + +// validateAndWrapHandler makes sure a handler is a callable function, it panics if not. +// When the handler is also potential to be any built-in inject.FastInvoker, +// it wraps the handler automatically to have some performance gain. +func validateAndWrapHandler(h Handler) Handler { + if reflect.TypeOf(h).Kind() != reflect.Func { + panic("Macaron handler must be a callable function") + } + + if !inject.IsFastInvoker(h) { + switch v := h.(type) { + case func(*Context): + return ContextInvoker(v) + case func(*Context, *log.Logger): + return LoggerInvoker(v) + case func(http.ResponseWriter, *http.Request): + return handlerFuncInvoker(v) + case func(http.ResponseWriter, error): + return internalServerErrorInvoker(v) + } + } + return h +} + +// validateAndWrapHandlers preforms validation and wrapping for each input handler. +// It accepts an optional wrapper function to perform custom wrapping on handlers. +func validateAndWrapHandlers(handlers []Handler, wrappers ...func(Handler) Handler) []Handler { + var wrapper func(Handler) Handler + if len(wrappers) > 0 { + wrapper = wrappers[0] + } + + wrappedHandlers := make([]Handler, len(handlers)) + for i, h := range handlers { + h = validateAndWrapHandler(h) + if wrapper != nil && !inject.IsFastInvoker(h) { + h = wrapper(h) + } + wrappedHandlers[i] = h + } + + return wrappedHandlers +} + +// Macaron represents the top level web application. +// inject.Injector methods can be invoked to map services on a global level. +type Macaron struct { + inject.Injector + befores []BeforeHandler + handlers []Handler + action Handler + + hasURLPrefix bool + urlPrefix string // For suburl support. + *Router + + logger *log.Logger +} + +// NewWithLogger creates a bare bones Macaron instance. +// Use this method if you want to have full control over the middleware that is used. +// You can specify logger output writer with this function. +func NewWithLogger(out io.Writer) *Macaron { + m := &Macaron{ + Injector: inject.New(), + action: func() {}, + Router: NewRouter(), + logger: log.New(out, "[Macaron] ", 0), + } + m.Router.m = m + m.Map(m.logger) + m.Map(defaultReturnHandler()) + m.NotFound(http.NotFound) + m.InternalServerError(func(rw http.ResponseWriter, err error) { + http.Error(rw, err.Error(), 500) + }) + return m +} + +// New creates a bare bones Macaron instance. +// Use this method if you want to have full control over the middleware that is used. +func New() *Macaron { + return NewWithLogger(os.Stdout) +} + +// Classic creates a classic Macaron with some basic default middleware: +// macaron.Logger, macaron.Recovery and macaron.Static. +func Classic() *Macaron { + m := New() + m.Use(Logger()) + m.Use(Recovery()) + m.Use(Static("public")) + return m +} + +// Handlers sets the entire middleware stack with the given Handlers. +// This will clear any current middleware handlers, +// and panics if any of the handlers is not a callable function +func (m *Macaron) Handlers(handlers ...Handler) { + m.handlers = make([]Handler, 0) + for _, handler := range handlers { + m.Use(handler) + } +} + +// Action sets the handler that will be called after all the middleware has been invoked. +// This is set to macaron.Router in a macaron.Classic(). +func (m *Macaron) Action(handler Handler) { + handler = validateAndWrapHandler(handler) + m.action = handler +} + +// BeforeHandler represents a handler executes at beginning of every request. +// Macaron stops future process when it returns true. +type BeforeHandler func(rw http.ResponseWriter, req *http.Request) bool + +func (m *Macaron) Before(handler BeforeHandler) { + m.befores = append(m.befores, handler) +} + +// Use adds a middleware Handler to the stack, +// and panics if the handler is not a callable func. +// Middleware Handlers are invoked in the order that they are added. +func (m *Macaron) Use(handler Handler) { + handler = validateAndWrapHandler(handler) + m.handlers = append(m.handlers, handler) +} + +func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context { + c := &Context{ + Injector: inject.New(), + handlers: m.handlers, + action: m.action, + index: 0, + Router: m.Router, + Req: Request{req}, + Resp: NewResponseWriter(req.Method, rw), + Render: &DummyRender{rw}, + Data: make(map[string]interface{}), + } + c.SetParent(m) + c.Map(c) + c.MapTo(c.Resp, (*http.ResponseWriter)(nil)) + c.Map(req) + return c +} + +// ServeHTTP is the HTTP Entry point for a Macaron instance. +// Useful if you want to control your own HTTP server. +// Be aware that none of middleware will run without registering any router. +func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if m.hasURLPrefix { + req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix) + } + for _, h := range m.befores { + if h(rw, req) { + return + } + } + m.Router.ServeHTTP(rw, req) +} + +func GetDefaultListenInfo() (string, int) { + host := os.Getenv("HOST") + if len(host) == 0 { + host = "0.0.0.0" + } + port := com.StrTo(os.Getenv("PORT")).MustInt() + if port == 0 { + port = 4000 + } + return host, port +} + +// Run the http server. Listening on os.GetEnv("PORT") or 4000 by default. +func (m *Macaron) Run(args ...interface{}) { + host, port := GetDefaultListenInfo() + if len(args) == 1 { + switch arg := args[0].(type) { + case string: + host = arg + case int: + port = arg + } + } else if len(args) >= 2 { + if arg, ok := args[0].(string); ok { + host = arg + } + if arg, ok := args[1].(int); ok { + port = arg + } + } + + addr := host + ":" + com.ToStr(port) + logger := m.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger) + logger.Printf("listening on %s (%s)\n", addr, safeEnv()) + logger.Fatalln(http.ListenAndServe(addr, m)) +} + +// SetURLPrefix sets URL prefix of router layer, so that it support suburl. +func (m *Macaron) SetURLPrefix(prefix string) { + m.urlPrefix = prefix + m.hasURLPrefix = len(m.urlPrefix) > 0 +} + +// ____ ____ .__ ___. .__ +// \ \ / /____ _______|__|____ \_ |__ | | ____ ______ +// \ Y /\__ \\_ __ \ \__ \ | __ \| | _/ __ \ / ___/ +// \ / / __ \| | \/ |/ __ \| \_\ \ |_\ ___/ \___ \ +// \___/ (____ /__| |__(____ /___ /____/\___ >____ > +// \/ \/ \/ \/ \/ + +const ( + DEV = "development" + PROD = "production" + TEST = "test" +) + +var ( + // Env is the environment that Macaron is executing in. + // The MACARON_ENV is read on initialization to set this variable. + Env = DEV + envLock sync.Mutex + + // Path of work directory. + Root string + + // Flash applies to current request. + FlashNow bool + + // Configuration convention object. + cfg *ini.File +) + +func setENV(e string) { + envLock.Lock() + defer envLock.Unlock() + + if len(e) > 0 { + Env = e + } +} + +func safeEnv() string { + envLock.Lock() + defer envLock.Unlock() + + return Env +} + +func init() { + setENV(os.Getenv("MACARON_ENV")) + + var err error + Root, err = os.Getwd() + if err != nil { + panic("error getting work directory: " + err.Error()) + } +} + +// SetConfig sets data sources for configuration. +func SetConfig(source interface{}, others ...interface{}) (_ *ini.File, err error) { + cfg, err = ini.Load(source, others...) + return Config(), err +} + +// Config returns configuration convention object. +// It returns an empty object if there is no one available. +func Config() *ini.File { + if cfg == nil { + return ini.Empty() + } + return cfg +} diff --git a/vendor/gitea.com/macaron/macaron/macaronlogo.png b/vendor/gitea.com/macaron/macaron/macaronlogo.png Binary files differnew file mode 100644 index 0000000000..399759769a --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/macaronlogo.png diff --git a/vendor/gitea.com/macaron/macaron/recovery.go b/vendor/gitea.com/macaron/macaron/recovery.go new file mode 100644 index 0000000000..49c3096d64 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/recovery.go @@ -0,0 +1,163 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net/http" + "runtime" + + "gitea.com/macaron/inject" +) + +const ( + panicHtml = `<html> +<head><title>PANIC: %s</title> +<meta charset="utf-8" /> +<style type="text/css"> +html, body { + font-family: "Roboto", sans-serif; + color: #333333; + background-color: #ea5343; + margin: 0px; +} +h1 { + color: #d04526; + background-color: #ffffff; + padding: 20px; + border-bottom: 1px dashed #2b3848; +} +pre { + margin: 20px; + padding: 20px; + border: 2px solid #2b3848; + background-color: #ffffff; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} +</style> +</head><body> +<h1>PANIC</h1> +<pre style="font-weight: bold;">%s</pre> +<pre>%s</pre> +</body> +</html>` +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// stack returns a nicely formated stack frame, skipping skip frames +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { + name = name[lastslash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. +// While Martini is in development mode, Recovery will also output the panic as HTML. +func Recovery() Handler { + return func(c *Context, log *log.Logger) { + defer func() { + if err := recover(); err != nil { + stack := stack(3) + log.Printf("PANIC: %s\n%s", err, stack) + + // Lookup the current responsewriter + val := c.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil))) + res := val.Interface().(http.ResponseWriter) + + // respond with panic message while in development mode + var body []byte + if Env == DEV { + res.Header().Set("Content-Type", "text/html") + body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) + } + + res.WriteHeader(http.StatusInternalServerError) + if nil != body { + res.Write(body) + } + } + }() + + c.Next() + } +} diff --git a/vendor/gitea.com/macaron/macaron/render.go b/vendor/gitea.com/macaron/macaron/render.go new file mode 100644 index 0000000000..6f2e93f21b --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/render.go @@ -0,0 +1,725 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "html/template" + "io" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/unknwon/com" +) + +const ( + _CONTENT_TYPE = "Content-Type" + _CONTENT_LENGTH = "Content-Length" + _CONTENT_BINARY = "application/octet-stream" + _CONTENT_JSON = "application/json" + _CONTENT_HTML = "text/html" + _CONTENT_PLAIN = "text/plain" + _CONTENT_XHTML = "application/xhtml+xml" + _CONTENT_XML = "text/xml" + _DEFAULT_CHARSET = "UTF-8" +) + +var ( + // Provides a temporary buffer to execute templates into and catch errors. + bufpool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, + } + + // Included helper functions for use when rendering html + helperFuncs = template.FuncMap{ + "yield": func() (string, error) { + return "", fmt.Errorf("yield called with no layout defined") + }, + "current": func() (string, error) { + return "", nil + }, + } +) + +type ( + // TemplateFile represents a interface of template file that has name and can be read. + TemplateFile interface { + Name() string + Data() []byte + Ext() string + } + // TemplateFileSystem represents a interface of template file system that able to list all files. + TemplateFileSystem interface { + ListFiles() []TemplateFile + Get(string) (io.Reader, error) + } + + // Delims represents a set of Left and Right delimiters for HTML template rendering + Delims struct { + // Left delimiter, defaults to {{ + Left string + // Right delimiter, defaults to }} + Right string + } + + // RenderOptions represents a struct for specifying configuration options for the Render middleware. + RenderOptions struct { + // Directory to load templates. Default is "templates". + Directory string + // Addtional directories to overwite templates. + AppendDirectories []string + // Layout template name. Will not render a layout if "". Default is to "". + Layout string + // Extensions to parse template files from. Defaults are [".tmpl", ".html"]. + Extensions []string + // Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Default is []. + Funcs []template.FuncMap + // Delims sets the action delimiters to the specified strings in the Delims struct. + Delims Delims + // Appends the given charset to the Content-Type header. Default is "UTF-8". + Charset string + // Outputs human readable JSON. + IndentJSON bool + // Outputs human readable XML. + IndentXML bool + // Prefixes the JSON output with the given bytes. + PrefixJSON []byte + // Prefixes the XML output with the given bytes. + PrefixXML []byte + // Allows changing of output to XHTML instead of HTML. Default is "text/html" + HTMLContentType string + // TemplateFileSystem is the interface for supporting any implmentation of template file system. + TemplateFileSystem + } + + // HTMLOptions is a struct for overriding some rendering Options for specific HTML call + HTMLOptions struct { + // Layout template name. Overrides Options.Layout. + Layout string + } + + Render interface { + http.ResponseWriter + SetResponseWriter(http.ResponseWriter) + + JSON(int, interface{}) + JSONString(interface{}) (string, error) + RawData(int, []byte) // Serve content as binary + PlainText(int, []byte) // Serve content as plain text + HTML(int, string, interface{}, ...HTMLOptions) + HTMLSet(int, string, string, interface{}, ...HTMLOptions) + HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) + HTMLString(string, interface{}, ...HTMLOptions) (string, error) + HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) + HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) + XML(int, interface{}) + Error(int, ...string) + Status(int) + SetTemplatePath(string, string) + HasTemplateSet(string) bool + } +) + +// TplFile implements TemplateFile interface. +type TplFile struct { + name string + data []byte + ext string +} + +// NewTplFile cerates new template file with given name and data. +func NewTplFile(name string, data []byte, ext string) *TplFile { + return &TplFile{name, data, ext} +} + +func (f *TplFile) Name() string { + return f.name +} + +func (f *TplFile) Data() []byte { + return f.data +} + +func (f *TplFile) Ext() string { + return f.ext +} + +// TplFileSystem implements TemplateFileSystem interface. +type TplFileSystem struct { + files []TemplateFile +} + +// NewTemplateFileSystem creates new template file system with given options. +func NewTemplateFileSystem(opt RenderOptions, omitData bool) TplFileSystem { + fs := TplFileSystem{} + fs.files = make([]TemplateFile, 0, 10) + + // Directories are composed in reverse order because later one overwrites previous ones, + // so once found, we can directly jump out of the loop. + dirs := make([]string, 0, len(opt.AppendDirectories)+1) + for i := len(opt.AppendDirectories) - 1; i >= 0; i-- { + dirs = append(dirs, opt.AppendDirectories[i]) + } + dirs = append(dirs, opt.Directory) + + var err error + for i := range dirs { + // Skip ones that does not exists for symlink test, + // but allow non-symlink ones added after start. + if !com.IsExist(dirs[i]) { + continue + } + + dirs[i], err = filepath.EvalSymlinks(dirs[i]) + if err != nil { + panic("EvalSymlinks(" + dirs[i] + "): " + err.Error()) + } + } + lastDir := dirs[len(dirs)-1] + + // We still walk the last (original) directory because it's non-sense we load templates not exist in original directory. + if err = filepath.Walk(lastDir, func(path string, info os.FileInfo, err error) error { + r, err := filepath.Rel(lastDir, path) + if err != nil { + return err + } + + ext := GetExt(r) + + for _, extension := range opt.Extensions { + if ext != extension { + continue + } + + var data []byte + if !omitData { + // Loop over candidates of directory, break out once found. + // The file always exists because it's inside the walk function, + // and read original file is the worst case. + for i := range dirs { + path = filepath.Join(dirs[i], r) + if !com.IsFile(path) { + continue + } + + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + break + } + } + + name := filepath.ToSlash((r[0 : len(r)-len(ext)])) + fs.files = append(fs.files, NewTplFile(name, data, ext)) + } + + return nil + }); err != nil { + panic("NewTemplateFileSystem: " + err.Error()) + } + + return fs +} + +func (fs TplFileSystem) ListFiles() []TemplateFile { + return fs.files +} + +func (fs TplFileSystem) Get(name string) (io.Reader, error) { + for i := range fs.files { + if fs.files[i].Name()+fs.files[i].Ext() == name { + return bytes.NewReader(fs.files[i].Data()), nil + } + } + return nil, fmt.Errorf("file '%s' not found", name) +} + +func PrepareCharset(charset string) string { + if len(charset) != 0 { + return "; charset=" + charset + } + + return "; charset=" + _DEFAULT_CHARSET +} + +func GetExt(s string) string { + index := strings.Index(s, ".") + if index == -1 { + return "" + } + return s[index:] +} + +func compile(opt RenderOptions) *template.Template { + t := template.New(opt.Directory) + t.Delims(opt.Delims.Left, opt.Delims.Right) + // Parse an initial template in case we don't have any. + template.Must(t.Parse("Macaron")) + + if opt.TemplateFileSystem == nil { + opt.TemplateFileSystem = NewTemplateFileSystem(opt, false) + } + + for _, f := range opt.TemplateFileSystem.ListFiles() { + tmpl := t.New(f.Name()) + for _, funcs := range opt.Funcs { + tmpl.Funcs(funcs) + } + // Bomb out if parse fails. We don't want any silent server starts. + template.Must(tmpl.Funcs(helperFuncs).Parse(string(f.Data()))) + } + + return t +} + +const ( + DEFAULT_TPL_SET_NAME = "DEFAULT" +) + +// TemplateSet represents a template set of type *template.Template. +type TemplateSet struct { + lock sync.RWMutex + sets map[string]*template.Template + dirs map[string]string +} + +// NewTemplateSet initializes a new empty template set. +func NewTemplateSet() *TemplateSet { + return &TemplateSet{ + sets: make(map[string]*template.Template), + dirs: make(map[string]string), + } +} + +func (ts *TemplateSet) Set(name string, opt *RenderOptions) *template.Template { + t := compile(*opt) + + ts.lock.Lock() + defer ts.lock.Unlock() + + ts.sets[name] = t + ts.dirs[name] = opt.Directory + return t +} + +func (ts *TemplateSet) Get(name string) *template.Template { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.sets[name] +} + +func (ts *TemplateSet) GetDir(name string) string { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.dirs[name] +} + +func prepareRenderOptions(options []RenderOptions) RenderOptions { + var opt RenderOptions + if len(options) > 0 { + opt = options[0] + } + + // Defaults. + if len(opt.Directory) == 0 { + opt.Directory = "templates" + } + if len(opt.Extensions) == 0 { + opt.Extensions = []string{".tmpl", ".html"} + } + if len(opt.HTMLContentType) == 0 { + opt.HTMLContentType = _CONTENT_HTML + } + + return opt +} + +func ParseTplSet(tplSet string) (tplName string, tplDir string) { + tplSet = strings.TrimSpace(tplSet) + if len(tplSet) == 0 { + panic("empty template set argument") + } + infos := strings.Split(tplSet, ":") + if len(infos) == 1 { + tplDir = infos[0] + tplName = path.Base(tplDir) + } else { + tplName = infos[0] + tplDir = infos[1] + } + + if !com.IsDir(tplDir) { + panic("template set path does not exist or is not a directory") + } + return tplName, tplDir +} + +func renderHandler(opt RenderOptions, tplSets []string) Handler { + cs := PrepareCharset(opt.Charset) + ts := NewTemplateSet() + ts.Set(DEFAULT_TPL_SET_NAME, &opt) + + var tmpOpt RenderOptions + for _, tplSet := range tplSets { + tplName, tplDir := ParseTplSet(tplSet) + tmpOpt = opt + tmpOpt.Directory = tplDir + ts.Set(tplName, &tmpOpt) + } + + return func(ctx *Context) { + r := &TplRender{ + ResponseWriter: ctx.Resp, + TemplateSet: ts, + Opt: &opt, + CompiledCharset: cs, + } + ctx.Data["TmplLoadTimes"] = func() string { + if r.startTime.IsZero() { + return "" + } + return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms" + } + + ctx.Render = r + ctx.MapTo(r, (*Render)(nil)) + } +} + +// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain. +// An single variadic macaron.RenderOptions struct can be optionally provided to configure +// HTML rendering. The default directory for templates is "templates" and the default +// file extension is ".tmpl" and ".html". +// +// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the +// MACARON_ENV environment variable to "production". +func Renderer(options ...RenderOptions) Handler { + return renderHandler(prepareRenderOptions(options), []string{}) +} + +func Renderers(options RenderOptions, tplSets ...string) Handler { + return renderHandler(prepareRenderOptions([]RenderOptions{options}), tplSets) +} + +type TplRender struct { + http.ResponseWriter + *TemplateSet + Opt *RenderOptions + CompiledCharset string + + startTime time.Time +} + +func (r *TplRender) SetResponseWriter(rw http.ResponseWriter) { + r.ResponseWriter = rw +} + +func (r *TplRender) JSON(status int, v interface{}) { + var ( + result []byte + err error + ) + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // json rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_JSON+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixJSON) > 0 { + r.Write(r.Opt.PrefixJSON) + } + r.Write(result) +} + +func (r *TplRender) JSONString(v interface{}) (string, error) { + var result []byte + var err error + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + return "", err + } + return string(result), nil +} + +func (r *TplRender) XML(status int, v interface{}) { + var result []byte + var err error + if r.Opt.IndentXML { + result, err = xml.MarshalIndent(v, "", " ") + } else { + result, err = xml.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // XML rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_XML+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixXML) > 0 { + r.Write(r.Opt.PrefixXML) + } + r.Write(result) +} + +func (r *TplRender) data(status int, contentType string, v []byte) { + if r.Header().Get(_CONTENT_TYPE) == "" { + r.Header().Set(_CONTENT_TYPE, contentType) + } + r.WriteHeader(status) + r.Write(v) +} + +func (r *TplRender) RawData(status int, v []byte) { + r.data(status, _CONTENT_BINARY, v) +} + +func (r *TplRender) PlainText(status int, v []byte) { + r.data(status, _CONTENT_PLAIN, v) +} + +func (r *TplRender) execute(t *template.Template, name string, data interface{}) (*bytes.Buffer, error) { + buf := bufpool.Get().(*bytes.Buffer) + return buf, t.ExecuteTemplate(buf, name, data) +} + +func (r *TplRender) addYield(t *template.Template, tplName string, data interface{}) { + funcs := template.FuncMap{ + "yield": func() (template.HTML, error) { + buf, err := r.execute(t, tplName, data) + // return safe html here since we are rendering our own template + return template.HTML(buf.String()), err + }, + "current": func() (string, error) { + return tplName, nil + }, + } + t.Funcs(funcs) +} + +func (r *TplRender) renderBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) { + t := r.TemplateSet.Get(setName) + if Env == DEV { + opt := *r.Opt + opt.Directory = r.TemplateSet.GetDir(setName) + t = r.TemplateSet.Set(setName, &opt) + } + if t == nil { + return nil, fmt.Errorf("html/template: template \"%s\" is undefined", tplName) + } + + opt := r.prepareHTMLOptions(htmlOpt) + + if len(opt.Layout) > 0 { + r.addYield(t, tplName, data) + tplName = opt.Layout + } + + out, err := r.execute(t, tplName, data) + if err != nil { + return nil, err + } + + return out, nil +} + +func (r *TplRender) renderHTML(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.startTime = time.Now() + + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + http.Error(r, err.Error(), http.StatusInternalServerError) + return + } + + r.Header().Set(_CONTENT_TYPE, r.Opt.HTMLContentType+r.CompiledCharset) + r.WriteHeader(status) + + if _, err := out.WriteTo(r); err != nil { + out.Reset() + } + bufpool.Put(out) +} + +func (r *TplRender) HTML(status int, name string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSet(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, setName, tplName, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + return []byte(""), err + } + return out.Bytes(), nil +} + +func (r *TplRender) HTMLBytes(name string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + return r.HTMLSetBytes(DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetString(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLSetBytes(setName, tplName, data, htmlOpt...) + return string(p), err +} + +func (r *TplRender) HTMLString(name string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLBytes(name, data, htmlOpt...) + return string(p), err +} + +// Error writes the given HTTP status to the current ResponseWriter +func (r *TplRender) Error(status int, message ...string) { + r.WriteHeader(status) + if len(message) > 0 { + r.Write([]byte(message[0])) + } +} + +func (r *TplRender) Status(status int) { + r.WriteHeader(status) +} + +func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { + if len(htmlOpt) > 0 { + return htmlOpt[0] + } + + return HTMLOptions{ + Layout: r.Opt.Layout, + } +} + +func (r *TplRender) SetTemplatePath(setName, dir string) { + if len(setName) == 0 { + setName = DEFAULT_TPL_SET_NAME + } + opt := *r.Opt + opt.Directory = dir + r.TemplateSet.Set(setName, &opt) +} + +func (r *TplRender) HasTemplateSet(name string) bool { + return r.TemplateSet.Get(name) != nil +} + +// DummyRender is used when user does not choose any real render to use. +// This way, we can print out friendly message which asks them to register one, +// instead of ugly and confusing 'nil pointer' panic. +type DummyRender struct { + http.ResponseWriter +} + +func renderNotRegistered() { + panic("middleware render hasn't been registered") +} + +func (r *DummyRender) SetResponseWriter(http.ResponseWriter) { + renderNotRegistered() +} + +func (r *DummyRender) JSON(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) JSONString(interface{}) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) RawData(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) PlainText(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) HTML(int, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSet(int, string, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLString(string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) XML(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) Error(int, ...string) { + renderNotRegistered() +} + +func (r *DummyRender) Status(int) { + renderNotRegistered() +} + +func (r *DummyRender) SetTemplatePath(string, string) { + renderNotRegistered() +} + +func (r *DummyRender) HasTemplateSet(string) bool { + renderNotRegistered() + return false +} diff --git a/vendor/gitea.com/macaron/macaron/response_writer.go b/vendor/gitea.com/macaron/macaron/response_writer.go new file mode 100644 index 0000000000..9133948f9b --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/response_writer.go @@ -0,0 +1,114 @@ +// Copyright 2013 Martini Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bufio" + "fmt" + "net" + "net/http" +) + +// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about +// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter +// if the functionality calls for it. +type ResponseWriter interface { + http.ResponseWriter + http.Flusher + // Status returns the status code of the response or 0 if the response has not been written. + Status() int + // Written returns whether or not the ResponseWriter has been written. + Written() bool + // Size returns the size of the response body. + Size() int + // Before allows for a function to be called before the ResponseWriter has been written to. This is + // useful for setting headers or any other operations that must happen before a response has been written. + Before(BeforeFunc) +} + +// BeforeFunc is a function that is called before the ResponseWriter has been written to. +type BeforeFunc func(ResponseWriter) + +// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter +func NewResponseWriter(method string, rw http.ResponseWriter) ResponseWriter { + return &responseWriter{method, rw, 0, 0, nil} +} + +type responseWriter struct { + method string + http.ResponseWriter + status int + size int + beforeFuncs []BeforeFunc +} + +func (rw *responseWriter) WriteHeader(s int) { + rw.callBefore() + rw.ResponseWriter.WriteHeader(s) + rw.status = s +} + +func (rw *responseWriter) Write(b []byte) (size int, err error) { + if !rw.Written() { + // The status will be StatusOK if WriteHeader has not been called yet + rw.WriteHeader(http.StatusOK) + } + if rw.method != "HEAD" { + size, err = rw.ResponseWriter.Write(b) + rw.size += size + } + return size, err +} + +func (rw *responseWriter) Status() int { + return rw.status +} + +func (rw *responseWriter) Size() int { + return rw.size +} + +func (rw *responseWriter) Written() bool { + return rw.status != 0 +} + +func (rw *responseWriter) Before(before BeforeFunc) { + rw.beforeFuncs = append(rw.beforeFuncs, before) +} + +func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := rw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} + +func (rw *responseWriter) CloseNotify() <-chan bool { + return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +func (rw *responseWriter) callBefore() { + for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { + rw.beforeFuncs[i](rw) + } +} + +func (rw *responseWriter) Flush() { + flusher, ok := rw.ResponseWriter.(http.Flusher) + if ok { + flusher.Flush() + } +} diff --git a/vendor/gitea.com/macaron/macaron/return_handler.go b/vendor/gitea.com/macaron/macaron/return_handler.go new file mode 100644 index 0000000000..2e37c78a32 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/return_handler.go @@ -0,0 +1,76 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "net/http" + "reflect" + + "gitea.com/macaron/inject" +) + +// ReturnHandler is a service that Martini provides that is called +// when a route handler returns something. The ReturnHandler is +// responsible for writing to the ResponseWriter based on the values +// that are passed into this function. +type ReturnHandler func(*Context, []reflect.Value) + +func canDeref(val reflect.Value) bool { + return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr +} + +func isError(val reflect.Value) bool { + _, ok := val.Interface().(error) + return ok +} + +func isByteSlice(val reflect.Value) bool { + return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 +} + +func defaultReturnHandler() ReturnHandler { + return func(ctx *Context, vals []reflect.Value) { + rv := ctx.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil))) + resp := rv.Interface().(http.ResponseWriter) + var respVal reflect.Value + if len(vals) > 1 && vals[0].Kind() == reflect.Int { + resp.WriteHeader(int(vals[0].Int())) + respVal = vals[1] + } else if len(vals) > 0 { + respVal = vals[0] + + if isError(respVal) { + err := respVal.Interface().(error) + if err != nil { + ctx.internalServerError(ctx, err) + } + return + } else if canDeref(respVal) { + if respVal.IsNil() { + return // Ignore nil error + } + } + } + if canDeref(respVal) { + respVal = respVal.Elem() + } + if isByteSlice(respVal) { + resp.Write(respVal.Bytes()) + } else { + resp.Write([]byte(respVal.String())) + } + } +} diff --git a/vendor/gitea.com/macaron/macaron/router.go b/vendor/gitea.com/macaron/macaron/router.go new file mode 100644 index 0000000000..df593d669a --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/router.go @@ -0,0 +1,380 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "net/http" + "strings" + "sync" +) + +var ( + // Known HTTP methods. + _HTTP_METHODS = map[string]bool{ + "GET": true, + "POST": true, + "PUT": true, + "DELETE": true, + "PATCH": true, + "OPTIONS": true, + "HEAD": true, + } +) + +// routeMap represents a thread-safe map for route tree. +type routeMap struct { + lock sync.RWMutex + routes map[string]map[string]*Leaf +} + +// NewRouteMap initializes and returns a new routeMap. +func NewRouteMap() *routeMap { + rm := &routeMap{ + routes: make(map[string]map[string]*Leaf), + } + for m := range _HTTP_METHODS { + rm.routes[m] = make(map[string]*Leaf) + } + return rm +} + +// getLeaf returns Leaf object if a route has been registered. +func (rm *routeMap) getLeaf(method, pattern string) *Leaf { + rm.lock.RLock() + defer rm.lock.RUnlock() + + return rm.routes[method][pattern] +} + +// add adds new route to route tree map. +func (rm *routeMap) add(method, pattern string, leaf *Leaf) { + rm.lock.Lock() + defer rm.lock.Unlock() + + rm.routes[method][pattern] = leaf +} + +type group struct { + pattern string + handlers []Handler +} + +// Router represents a Macaron router layer. +type Router struct { + m *Macaron + autoHead bool + routers map[string]*Tree + *routeMap + namedRoutes map[string]*Leaf + + groups []group + notFound http.HandlerFunc + internalServerError func(*Context, error) + + // handlerWrapper is used to wrap arbitrary function from Handler to inject.FastInvoker. + handlerWrapper func(Handler) Handler +} + +func NewRouter() *Router { + return &Router{ + routers: make(map[string]*Tree), + routeMap: NewRouteMap(), + namedRoutes: make(map[string]*Leaf), + } +} + +// SetAutoHead sets the value who determines whether add HEAD method automatically +// when GET method is added. +func (r *Router) SetAutoHead(v bool) { + r.autoHead = v +} + +type Params map[string]string + +// Handle is a function that can be registered to a route to handle HTTP requests. +// Like http.HandlerFunc, but has a third parameter for the values of wildcards (variables). +type Handle func(http.ResponseWriter, *http.Request, Params) + +// Route represents a wrapper of leaf route and upper level router. +type Route struct { + router *Router + leaf *Leaf +} + +// Name sets name of route. +func (r *Route) Name(name string) { + if len(name) == 0 { + panic("route name cannot be empty") + } else if r.router.namedRoutes[name] != nil { + panic("route with given name already exists: " + name) + } + r.router.namedRoutes[name] = r.leaf +} + +// handle adds new route to the router tree. +func (r *Router) handle(method, pattern string, handle Handle) *Route { + method = strings.ToUpper(method) + + var leaf *Leaf + // Prevent duplicate routes. + if leaf = r.getLeaf(method, pattern); leaf != nil { + return &Route{r, leaf} + } + + // Validate HTTP methods. + if !_HTTP_METHODS[method] && method != "*" { + panic("unknown HTTP method: " + method) + } + + // Generate methods need register. + methods := make(map[string]bool) + if method == "*" { + for m := range _HTTP_METHODS { + methods[m] = true + } + } else { + methods[method] = true + } + + // Add to router tree. + for m := range methods { + if t, ok := r.routers[m]; ok { + leaf = t.Add(pattern, handle) + } else { + t := NewTree() + leaf = t.Add(pattern, handle) + r.routers[m] = t + } + r.add(m, pattern, leaf) + } + return &Route{r, leaf} +} + +// Handle registers a new request handle with the given pattern, method and handlers. +func (r *Router) Handle(method string, pattern string, handlers []Handler) *Route { + if len(r.groups) > 0 { + groupPattern := "" + h := make([]Handler, 0) + for _, g := range r.groups { + groupPattern += g.pattern + h = append(h, g.handlers...) + } + + pattern = groupPattern + pattern + h = append(h, handlers...) + handlers = h + } + handlers = validateAndWrapHandlers(handlers, r.handlerWrapper) + + return r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) { + c := r.m.createContext(resp, req) + c.params = params + c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers)) + c.handlers = append(c.handlers, r.m.handlers...) + c.handlers = append(c.handlers, handlers...) + c.run() + }) +} + +func (r *Router) Group(pattern string, fn func(), h ...Handler) { + r.groups = append(r.groups, group{pattern, h}) + fn() + r.groups = r.groups[:len(r.groups)-1] +} + +// Get is a shortcut for r.Handle("GET", pattern, handlers) +func (r *Router) Get(pattern string, h ...Handler) (leaf *Route) { + leaf = r.Handle("GET", pattern, h) + if r.autoHead { + r.Head(pattern, h...) + } + return leaf +} + +// Patch is a shortcut for r.Handle("PATCH", pattern, handlers) +func (r *Router) Patch(pattern string, h ...Handler) *Route { + return r.Handle("PATCH", pattern, h) +} + +// Post is a shortcut for r.Handle("POST", pattern, handlers) +func (r *Router) Post(pattern string, h ...Handler) *Route { + return r.Handle("POST", pattern, h) +} + +// Put is a shortcut for r.Handle("PUT", pattern, handlers) +func (r *Router) Put(pattern string, h ...Handler) *Route { + return r.Handle("PUT", pattern, h) +} + +// Delete is a shortcut for r.Handle("DELETE", pattern, handlers) +func (r *Router) Delete(pattern string, h ...Handler) *Route { + return r.Handle("DELETE", pattern, h) +} + +// Options is a shortcut for r.Handle("OPTIONS", pattern, handlers) +func (r *Router) Options(pattern string, h ...Handler) *Route { + return r.Handle("OPTIONS", pattern, h) +} + +// Head is a shortcut for r.Handle("HEAD", pattern, handlers) +func (r *Router) Head(pattern string, h ...Handler) *Route { + return r.Handle("HEAD", pattern, h) +} + +// Any is a shortcut for r.Handle("*", pattern, handlers) +func (r *Router) Any(pattern string, h ...Handler) *Route { + return r.Handle("*", pattern, h) +} + +// Route is a shortcut for same handlers but different HTTP methods. +// +// Example: +// m.Route("/", "GET,POST", h) +func (r *Router) Route(pattern, methods string, h ...Handler) (route *Route) { + for _, m := range strings.Split(methods, ",") { + route = r.Handle(strings.TrimSpace(m), pattern, h) + } + return route +} + +// Combo returns a combo router. +func (r *Router) Combo(pattern string, h ...Handler) *ComboRouter { + return &ComboRouter{r, pattern, h, map[string]bool{}, nil} +} + +// NotFound configurates http.HandlerFunc which is called when no matching route is +// found. If it is not set, http.NotFound is used. +// Be sure to set 404 response code in your handler. +func (r *Router) NotFound(handlers ...Handler) { + handlers = validateAndWrapHandlers(handlers) + r.notFound = func(rw http.ResponseWriter, req *http.Request) { + c := r.m.createContext(rw, req) + c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers)) + c.handlers = append(c.handlers, r.m.handlers...) + c.handlers = append(c.handlers, handlers...) + c.run() + } +} + +// InternalServerError configurates handler which is called when route handler returns +// error. If it is not set, default handler is used. +// Be sure to set 500 response code in your handler. +func (r *Router) InternalServerError(handlers ...Handler) { + handlers = validateAndWrapHandlers(handlers) + r.internalServerError = func(c *Context, err error) { + c.index = 0 + c.handlers = handlers + c.Map(err) + c.run() + } +} + +// SetHandlerWrapper sets handlerWrapper for the router. +func (r *Router) SetHandlerWrapper(f func(Handler) Handler) { + r.handlerWrapper = f +} + +func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if t, ok := r.routers[req.Method]; ok { + // Fast match for static routes + leaf := r.getLeaf(req.Method, req.URL.Path) + if leaf != nil { + leaf.handle(rw, req, nil) + return + } + + h, p, ok := t.Match(req.URL.EscapedPath()) + if ok { + if splat, ok := p["*0"]; ok { + p["*"] = splat // Easy name. + } + h(rw, req, p) + return + } + } + + r.notFound(rw, req) +} + +// URLFor builds path part of URL by given pair values. +func (r *Router) URLFor(name string, pairs ...string) string { + leaf, ok := r.namedRoutes[name] + if !ok { + panic("route with given name does not exists: " + name) + } + return leaf.URLPath(pairs...) +} + +// ComboRouter represents a combo router. +type ComboRouter struct { + router *Router + pattern string + handlers []Handler + methods map[string]bool // Registered methods. + + lastRoute *Route +} + +func (cr *ComboRouter) checkMethod(name string) { + if cr.methods[name] { + panic("method '" + name + "' has already been registered") + } + cr.methods[name] = true +} + +func (cr *ComboRouter) route(fn func(string, ...Handler) *Route, method string, h ...Handler) *ComboRouter { + cr.checkMethod(method) + cr.lastRoute = fn(cr.pattern, append(cr.handlers, h...)...) + return cr +} + +func (cr *ComboRouter) Get(h ...Handler) *ComboRouter { + if cr.router.autoHead { + cr.Head(h...) + } + return cr.route(cr.router.Get, "GET", h...) +} + +func (cr *ComboRouter) Patch(h ...Handler) *ComboRouter { + return cr.route(cr.router.Patch, "PATCH", h...) +} + +func (cr *ComboRouter) Post(h ...Handler) *ComboRouter { + return cr.route(cr.router.Post, "POST", h...) +} + +func (cr *ComboRouter) Put(h ...Handler) *ComboRouter { + return cr.route(cr.router.Put, "PUT", h...) +} + +func (cr *ComboRouter) Delete(h ...Handler) *ComboRouter { + return cr.route(cr.router.Delete, "DELETE", h...) +} + +func (cr *ComboRouter) Options(h ...Handler) *ComboRouter { + return cr.route(cr.router.Options, "OPTIONS", h...) +} + +func (cr *ComboRouter) Head(h ...Handler) *ComboRouter { + return cr.route(cr.router.Head, "HEAD", h...) +} + +// Name sets name of ComboRouter route. +func (cr *ComboRouter) Name(name string) { + if cr.lastRoute == nil { + panic("no corresponding route to be named") + } + cr.lastRoute.Name(name) +} diff --git a/vendor/gitea.com/macaron/macaron/static.go b/vendor/gitea.com/macaron/macaron/static.go new file mode 100644 index 0000000000..60c521110e --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/static.go @@ -0,0 +1,220 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "encoding/base64" + "log" + "net/http" + "path" + "path/filepath" + "strings" + "sync" +) + +// StaticOptions is a struct for specifying configuration options for the macaron.Static middleware. +type StaticOptions struct { + // Prefix is the optional prefix used to serve the static directory content + Prefix string + // SkipLogging will disable [Static] log messages when a static file is served. + SkipLogging bool + // IndexFile defines which file to serve as index if it exists. + IndexFile string + // Expires defines which user-defined function to use for producing a HTTP Expires Header + // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching + Expires func() string + // ETag defines if we should add an ETag header + // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#validating-cached-responses-with-etags + ETag bool + // FileSystem is the interface for supporting any implmentation of file system. + FileSystem http.FileSystem +} + +// FIXME: to be deleted. +type staticMap struct { + lock sync.RWMutex + data map[string]*http.Dir +} + +func (sm *staticMap) Set(dir *http.Dir) { + sm.lock.Lock() + defer sm.lock.Unlock() + + sm.data[string(*dir)] = dir +} + +func (sm *staticMap) Get(name string) *http.Dir { + sm.lock.RLock() + defer sm.lock.RUnlock() + + return sm.data[name] +} + +func (sm *staticMap) Delete(name string) { + sm.lock.Lock() + defer sm.lock.Unlock() + + delete(sm.data, name) +} + +var statics = staticMap{sync.RWMutex{}, map[string]*http.Dir{}} + +// staticFileSystem implements http.FileSystem interface. +type staticFileSystem struct { + dir *http.Dir +} + +func newStaticFileSystem(directory string) staticFileSystem { + if !filepath.IsAbs(directory) { + directory = filepath.Join(Root, directory) + } + dir := http.Dir(directory) + statics.Set(&dir) + return staticFileSystem{&dir} +} + +func (fs staticFileSystem) Open(name string) (http.File, error) { + return fs.dir.Open(name) +} + +func prepareStaticOption(dir string, opt StaticOptions) StaticOptions { + // Defaults + if len(opt.IndexFile) == 0 { + opt.IndexFile = "index.html" + } + // Normalize the prefix if provided + if opt.Prefix != "" { + // Ensure we have a leading '/' + if opt.Prefix[0] != '/' { + opt.Prefix = "/" + opt.Prefix + } + // Remove any trailing '/' + opt.Prefix = strings.TrimRight(opt.Prefix, "/") + } + if opt.FileSystem == nil { + opt.FileSystem = newStaticFileSystem(dir) + } + return opt +} + +func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions { + var opt StaticOptions + if len(options) > 0 { + opt = options[0] + } + return prepareStaticOption(dir, opt) +} + +func staticHandler(ctx *Context, log *log.Logger, opt StaticOptions) bool { + if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" { + return false + } + + file := ctx.Req.URL.Path + // if we have a prefix, filter requests by stripping the prefix + if opt.Prefix != "" { + if !strings.HasPrefix(file, opt.Prefix) { + return false + } + file = file[len(opt.Prefix):] + if file != "" && file[0] != '/' { + return false + } + } + + f, err := opt.FileSystem.Open(file) + if err != nil { + return false + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return true // File exists but fail to open. + } + + // Try to serve index file + if fi.IsDir() { + // Redirect if missing trailing slash. + if !strings.HasSuffix(ctx.Req.URL.Path, "/") { + http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound) + return true + } + + file = path.Join(file, opt.IndexFile) + f, err = opt.FileSystem.Open(file) + if err != nil { + return false // Discard error. + } + defer f.Close() + + fi, err = f.Stat() + if err != nil || fi.IsDir() { + return true + } + } + + if !opt.SkipLogging { + log.Println("[Static] Serving " + file) + } + + // Add an Expires header to the static content + if opt.Expires != nil { + ctx.Resp.Header().Set("Expires", opt.Expires()) + } + + if opt.ETag { + tag := GenerateETag(string(fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat)) + ctx.Resp.Header().Set("ETag", tag) + } + + http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f) + return true +} + +// GenerateETag generates an ETag based on size, filename and file modification time +func GenerateETag(fileSize, fileName, modTime string) string { + etag := fileSize + fileName + modTime + return base64.StdEncoding.EncodeToString([]byte(etag)) +} + +// Static returns a middleware handler that serves static files in the given directory. +func Static(directory string, staticOpt ...StaticOptions) Handler { + opt := prepareStaticOptions(directory, staticOpt) + + return func(ctx *Context, log *log.Logger) { + staticHandler(ctx, log, opt) + } +} + +// Statics registers multiple static middleware handlers all at once. +func Statics(opt StaticOptions, dirs ...string) Handler { + if len(dirs) == 0 { + panic("no static directory is given") + } + opts := make([]StaticOptions, len(dirs)) + for i := range dirs { + opts[i] = prepareStaticOption(dirs[i], opt) + } + + return func(ctx *Context, log *log.Logger) { + for i := range opts { + if staticHandler(ctx, log, opts[i]) { + return + } + } + } +} diff --git a/vendor/gitea.com/macaron/macaron/tree.go b/vendor/gitea.com/macaron/macaron/tree.go new file mode 100644 index 0000000000..0ab094dd68 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/tree.go @@ -0,0 +1,390 @@ +// Copyright 2015 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "regexp" + "strings" + + "github.com/unknwon/com" +) + +type patternType int8 + +const ( + _PATTERN_STATIC patternType = iota // /home + _PATTERN_REGEXP // /:id([0-9]+) + _PATTERN_PATH_EXT // /*.* + _PATTERN_HOLDER // /:user + _PATTERN_MATCH_ALL // /* +) + +// Leaf represents a leaf route information. +type Leaf struct { + parent *Tree + + typ patternType + pattern string + rawPattern string // Contains wildcard instead of regexp + wildcards []string + reg *regexp.Regexp + optional bool + + handle Handle +} + +var wildcardPattern = regexp.MustCompile(`:[a-zA-Z0-9]+`) + +func isSpecialRegexp(pattern, regStr string, pos []int) bool { + return len(pattern) >= pos[1]+len(regStr) && pattern[pos[1]:pos[1]+len(regStr)] == regStr +} + +// getNextWildcard tries to find next wildcard and update pattern with corresponding regexp. +func getNextWildcard(pattern string) (wildcard, _ string) { + pos := wildcardPattern.FindStringIndex(pattern) + if pos == nil { + return "", pattern + } + wildcard = pattern[pos[0]:pos[1]] + + // Reach last character or no regexp is given. + if len(pattern) == pos[1] { + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } else if pattern[pos[1]] != '(' { + switch { + case isSpecialRegexp(pattern, ":int", pos): + pattern = strings.Replace(pattern, ":int", "([0-9]+)", 1) + case isSpecialRegexp(pattern, ":string", pos): + pattern = strings.Replace(pattern, ":string", "([\\w]+)", 1) + default: + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } + } + + // Cut out placeholder directly. + return wildcard, pattern[:pos[0]] + pattern[pos[1]:] +} + +func getWildcards(pattern string) (string, []string) { + wildcards := make([]string, 0, 2) + + // Keep getting next wildcard until nothing is left. + var wildcard string + for { + wildcard, pattern = getNextWildcard(pattern) + if len(wildcard) > 0 { + wildcards = append(wildcards, wildcard) + } else { + break + } + } + + return pattern, wildcards +} + +// getRawPattern removes all regexp but keeps wildcards for building URL path. +func getRawPattern(rawPattern string) string { + rawPattern = strings.Replace(rawPattern, ":int", "", -1) + rawPattern = strings.Replace(rawPattern, ":string", "", -1) + + for { + startIdx := strings.Index(rawPattern, "(") + if startIdx == -1 { + break + } + + closeIdx := strings.Index(rawPattern, ")") + if closeIdx > -1 { + rawPattern = rawPattern[:startIdx] + rawPattern[closeIdx+1:] + } + } + return rawPattern +} + +func checkPattern(pattern string) (typ patternType, rawPattern string, wildcards []string, reg *regexp.Regexp) { + pattern = strings.TrimLeft(pattern, "?") + rawPattern = getRawPattern(pattern) + + if pattern == "*" { + typ = _PATTERN_MATCH_ALL + } else if pattern == "*.*" { + typ = _PATTERN_PATH_EXT + } else if strings.Contains(pattern, ":") { + typ = _PATTERN_REGEXP + pattern, wildcards = getWildcards(pattern) + if pattern == "(.+)" { + typ = _PATTERN_HOLDER + } else { + reg = regexp.MustCompile(pattern) + } + } + return typ, rawPattern, wildcards, reg +} + +func NewLeaf(parent *Tree, pattern string, handle Handle) *Leaf { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + optional := false + if len(pattern) > 0 && pattern[0] == '?' { + optional = true + } + return &Leaf{parent, typ, pattern, rawPattern, wildcards, reg, optional, handle} +} + +// URLPath build path part of URL by given pair values. +func (l *Leaf) URLPath(pairs ...string) string { + if len(pairs)%2 != 0 { + panic("number of pairs does not match") + } + + urlPath := l.rawPattern + parent := l.parent + for parent != nil { + urlPath = parent.rawPattern + "/" + urlPath + parent = parent.parent + } + for i := 0; i < len(pairs); i += 2 { + if len(pairs[i]) == 0 { + panic("pair value cannot be empty: " + com.ToStr(i)) + } else if pairs[i][0] != ':' && pairs[i] != "*" && pairs[i] != "*.*" { + pairs[i] = ":" + pairs[i] + } + urlPath = strings.Replace(urlPath, pairs[i], pairs[i+1], 1) + } + return urlPath +} + +// Tree represents a router tree in Macaron. +type Tree struct { + parent *Tree + + typ patternType + pattern string + rawPattern string + wildcards []string + reg *regexp.Regexp + + subtrees []*Tree + leaves []*Leaf +} + +func NewSubtree(parent *Tree, pattern string) *Tree { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + return &Tree{parent, typ, pattern, rawPattern, wildcards, reg, make([]*Tree, 0, 5), make([]*Leaf, 0, 5)} +} + +func NewTree() *Tree { + return NewSubtree(nil, "") +} + +func (t *Tree) addLeaf(pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.leaves); i++ { + if t.leaves[i].pattern == pattern { + return t.leaves[i] + } + } + + leaf := NewLeaf(t, pattern, handle) + + // Add exact same leaf to grandparent/parent level without optional. + if leaf.optional { + parent := leaf.parent + if parent.parent != nil { + parent.parent.addLeaf(parent.pattern, handle) + } else { + parent.addLeaf("", handle) // Root tree can add as empty pattern. + } + } + + i := 0 + for ; i < len(t.leaves); i++ { + if leaf.typ < t.leaves[i].typ { + break + } + } + + if i == len(t.leaves) { + t.leaves = append(t.leaves, leaf) + } else { + t.leaves = append(t.leaves[:i], append([]*Leaf{leaf}, t.leaves[i:]...)...) + } + return leaf +} + +func (t *Tree) addSubtree(segment, pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.subtrees); i++ { + if t.subtrees[i].pattern == segment { + return t.subtrees[i].addNextSegment(pattern, handle) + } + } + + subtree := NewSubtree(t, segment) + i := 0 + for ; i < len(t.subtrees); i++ { + if subtree.typ < t.subtrees[i].typ { + break + } + } + + if i == len(t.subtrees) { + t.subtrees = append(t.subtrees, subtree) + } else { + t.subtrees = append(t.subtrees[:i], append([]*Tree{subtree}, t.subtrees[i:]...)...) + } + return subtree.addNextSegment(pattern, handle) +} + +func (t *Tree) addNextSegment(pattern string, handle Handle) *Leaf { + pattern = strings.TrimPrefix(pattern, "/") + + i := strings.Index(pattern, "/") + if i == -1 { + return t.addLeaf(pattern, handle) + } + return t.addSubtree(pattern[:i], pattern[i+1:], handle) +} + +func (t *Tree) Add(pattern string, handle Handle) *Leaf { + pattern = strings.TrimSuffix(pattern, "/") + return t.addNextSegment(pattern, handle) +} + +func (t *Tree) matchLeaf(globLevel int, url string, params Params) (Handle, bool) { + url, err := PathUnescape(url) + if err != nil { + return nil, false + } + for i := 0; i < len(t.leaves); i++ { + switch t.leaves[i].typ { + case _PATTERN_STATIC: + if t.leaves[i].pattern == url { + return t.leaves[i].handle, true + } + case _PATTERN_REGEXP: + results := t.leaves[i].reg.FindStringSubmatch(url) + // Number of results and wildcasrd should be exact same. + if len(results)-1 != len(t.leaves[i].wildcards) { + break + } + + for j := 0; j < len(t.leaves[i].wildcards); j++ { + params[t.leaves[i].wildcards[j]] = results[j+1] + } + return t.leaves[i].handle, true + case _PATTERN_PATH_EXT: + j := strings.LastIndex(url, ".") + if j > -1 { + params[":path"] = url[:j] + params[":ext"] = url[j+1:] + } else { + params[":path"] = url + } + return t.leaves[i].handle, true + case _PATTERN_HOLDER: + params[t.leaves[i].wildcards[0]] = url + return t.leaves[i].handle, true + case _PATTERN_MATCH_ALL: + params["*"] = url + params["*"+com.ToStr(globLevel)] = url + return t.leaves[i].handle, true + } + } + return nil, false +} + +func (t *Tree) matchSubtree(globLevel int, segment, url string, params Params) (Handle, bool) { + unescapedSegment, err := PathUnescape(segment) + if err != nil { + return nil, false + } + for i := 0; i < len(t.subtrees); i++ { + switch t.subtrees[i].typ { + case _PATTERN_STATIC: + if t.subtrees[i].pattern == unescapedSegment { + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + } + case _PATTERN_REGEXP: + results := t.subtrees[i].reg.FindStringSubmatch(unescapedSegment) + if len(results)-1 != len(t.subtrees[i].wildcards) { + break + } + + for j := 0; j < len(t.subtrees[i].wildcards); j++ { + params[t.subtrees[i].wildcards[j]] = results[j+1] + } + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + case _PATTERN_HOLDER: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params[t.subtrees[i].wildcards[0]] = unescapedSegment + return handle, true + } + case _PATTERN_MATCH_ALL: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params["*"+com.ToStr(globLevel)] = unescapedSegment + return handle, true + } + } + } + + if len(t.leaves) > 0 { + leaf := t.leaves[len(t.leaves)-1] + unescapedURL, err := PathUnescape(segment + "/" + url) + if err != nil { + return nil, false + } + if leaf.typ == _PATTERN_PATH_EXT { + j := strings.LastIndex(unescapedURL, ".") + if j > -1 { + params[":path"] = unescapedURL[:j] + params[":ext"] = unescapedURL[j+1:] + } else { + params[":path"] = unescapedURL + } + return leaf.handle, true + } else if leaf.typ == _PATTERN_MATCH_ALL { + params["*"] = unescapedURL + params["*"+com.ToStr(globLevel)] = unescapedURL + return leaf.handle, true + } + } + return nil, false +} + +func (t *Tree) matchNextSegment(globLevel int, url string, params Params) (Handle, bool) { + i := strings.Index(url, "/") + if i == -1 { + return t.matchLeaf(globLevel, url, params) + } + return t.matchSubtree(globLevel, url[:i], url[i+1:], params) +} + +func (t *Tree) Match(url string) (Handle, Params, bool) { + url = strings.TrimPrefix(url, "/") + url = strings.TrimSuffix(url, "/") + params := make(Params) + handle, ok := t.matchNextSegment(0, url, params) + return handle, params, ok +} + +// MatchTest returns true if given URL is matched by given pattern. +func MatchTest(pattern, url string) bool { + t := NewTree() + t.Add(pattern, nil) + _, _, ok := t.Match(url) + return ok +} diff --git a/vendor/gitea.com/macaron/macaron/util_go17.go b/vendor/gitea.com/macaron/macaron/util_go17.go new file mode 100644 index 0000000000..a80c696c7c --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/util_go17.go @@ -0,0 +1,25 @@ +// +build !go1.8 + +// Copyright 2017 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import "net/url" + +// PathUnescape unescapes a path. Ideally, this function would use +// url.PathUnescape(..), but the function was not introduced until go1.8. +func PathUnescape(s string) (string, error) { + return url.QueryUnescape(s) +} diff --git a/vendor/gitea.com/macaron/macaron/util_go18.go b/vendor/gitea.com/macaron/macaron/util_go18.go new file mode 100644 index 0000000000..d5eb1dfb28 --- /dev/null +++ b/vendor/gitea.com/macaron/macaron/util_go18.go @@ -0,0 +1,24 @@ +// +build go1.8 + +// Copyright 2017 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import "net/url" + +// PathUnescape unescapes a path. +func PathUnescape(s string) (string, error) { + return url.PathUnescape(s) +} diff --git a/vendor/gitea.com/macaron/session/.drone.yml b/vendor/gitea.com/macaron/session/.drone.yml new file mode 100644 index 0000000000..717ada4b8f --- /dev/null +++ b/vendor/gitea.com/macaron/session/.drone.yml @@ -0,0 +1,11 @@ +kind: pipeline +name: default + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/.gitignore b/vendor/gitea.com/macaron/session/.gitignore new file mode 100644 index 0000000000..834722925c --- /dev/null +++ b/vendor/gitea.com/macaron/session/.gitignore @@ -0,0 +1,4 @@ +ledis/tmp.db +nodb/tmp.db +/vendor +/.idea diff --git a/vendor/gitea.com/macaron/session/LICENSE b/vendor/gitea.com/macaron/session/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/session/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/README.md b/vendor/gitea.com/macaron/session/README.md new file mode 100644 index 0000000000..ebbbff5453 --- /dev/null +++ b/vendor/gitea.com/macaron/session/README.md @@ -0,0 +1,22 @@ +# session + +Middleware session provides session management for [Macaron](https://gitea.com/macaron/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase, Ledis and Nodb. + +### Installation + +The minimum requirement of Go is 1.11 . + + go get gitea.com/macaron/session + +## Getting Help + +- [API Reference](https://gowalker.org/gitea.com/macaron/session) +- [Documentation](https://go-macaron.com/docs/middlewares/session) + +## Credits + +This package is a modified version of [go-macaron/session](github.com/go-macaron/session). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/couchbase/couchbase.go b/vendor/gitea.com/macaron/session/couchbase/couchbase.go new file mode 100644 index 0000000000..8f5a32f969 --- /dev/null +++ b/vendor/gitea.com/macaron/session/couchbase/couchbase.go @@ -0,0 +1,227 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "strings" + "sync" + + "gitea.com/macaron/session" + "github.com/couchbaselabs/go-couchbase" +) + +// CouchbaseSessionStore represents a couchbase session store implementation. +type CouchbaseSessionStore struct { + b *couchbase.Bucket + sid string + lock sync.RWMutex + data map[interface{}]interface{} + maxlifetime int64 +} + +// Set sets value to given key in session. +func (s *CouchbaseSessionStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *CouchbaseSessionStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *CouchbaseSessionStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *CouchbaseSessionStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *CouchbaseSessionStore) Release() error { + defer s.b.Close() + + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + return s.b.Set(s.sid, int(s.maxlifetime), data) +} + +// Flush deletes all session data. +func (s *CouchbaseSessionStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// CouchbaseProvider represents a couchbase session provider implementation. +type CouchbaseProvider struct { + maxlifetime int64 + connStr string + pool string + bucket string + b *couchbase.Bucket +} + +func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket { + c, err := couchbase.Connect(cp.connStr) + if err != nil { + return nil + } + + pool, err := c.GetPool(cp.pool) + if err != nil { + return nil + } + + bucket, err := pool.GetBucket(cp.bucket) + if err != nil { + return nil + } + + return bucket +} + +// Init initializes memory session provider. +// connStr is couchbase server REST/JSON URL +// e.g. http://host:port/, Pool, Bucket +func (p *CouchbaseProvider) Init(maxlifetime int64, connStr string) error { + p.maxlifetime = maxlifetime + configs := strings.Split(connStr, ",") + if len(configs) > 0 { + p.connStr = configs[0] + } + if len(configs) > 1 { + p.pool = configs[1] + } + if len(configs) > 2 { + p.bucket = configs[2] + } + + return nil +} + +// Read returns raw session store by session ID. +func (p *CouchbaseProvider) Read(sid string) (session.RawStore, error) { + p.b = p.getBucket() + + var doc []byte + + err := p.b.Get(sid, &doc) + var kv map[interface{}]interface{} + if doc == nil { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(doc) + if err != nil { + return nil, err + } + } + + cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime} + return cs, nil +} + +// Exist returns true if session with given ID exists. +func (p *CouchbaseProvider) Exist(sid string) bool { + p.b = p.getBucket() + defer p.b.Close() + + var doc []byte + + if err := p.b.Get(sid, &doc); err != nil || doc == nil { + return false + } else { + return true + } +} + +// Destroy deletes a session by session ID. +func (p *CouchbaseProvider) Destroy(sid string) error { + p.b = p.getBucket() + defer p.b.Close() + + p.b.Delete(sid) + return nil +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *CouchbaseProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { + p.b = p.getBucket() + + var doc []byte + if err := p.b.Get(oldsid, &doc); err != nil || doc == nil { + p.b.Set(sid, int(p.maxlifetime), "") + } else { + err := p.b.Delete(oldsid) + if err != nil { + return nil, err + } + _, _ = p.b.Add(sid, int(p.maxlifetime), doc) + } + + err := p.b.Get(sid, &doc) + if err != nil { + return nil, err + } + var kv map[interface{}]interface{} + if doc == nil { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(doc) + if err != nil { + return nil, err + } + } + + cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime} + return cs, nil +} + +// Count counts and returns number of sessions. +func (p *CouchbaseProvider) Count() int { + // FIXME + return 0 +} + +// GC calls GC to clean expired sessions. +func (p *CouchbaseProvider) GC() {} + +func init() { + session.Register("couchbase", &CouchbaseProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/file.go b/vendor/gitea.com/macaron/session/file.go new file mode 100644 index 0000000000..3e575564ee --- /dev/null +++ b/vendor/gitea.com/macaron/session/file.go @@ -0,0 +1,266 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "sync" + "time" + + "github.com/unknwon/com" +) + +// FileStore represents a file session store implementation. +type FileStore struct { + p *FileProvider + sid string + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewFileStore creates and returns a file session store. +func NewFileStore(p *FileProvider, sid string, kv map[interface{}]interface{}) *FileStore { + return &FileStore{ + p: p, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *FileStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *FileStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *FileStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *FileStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *FileStore) Release() error { + s.p.lock.Lock() + defer s.p.lock.Unlock() + + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := EncodeGob(s.data) + if err != nil { + return err + } + + return ioutil.WriteFile(s.p.filepath(s.sid), data, 0600) +} + +// Flush deletes all session data. +func (s *FileStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// FileProvider represents a file session provider implementation. +type FileProvider struct { + lock sync.RWMutex + maxlifetime int64 + rootPath string +} + +// Init initializes file session provider with given root path. +func (p *FileProvider) Init(maxlifetime int64, rootPath string) error { + p.lock.Lock() + p.maxlifetime = maxlifetime + p.rootPath = rootPath + p.lock.Unlock() + return nil +} + +func (p *FileProvider) filepath(sid string) string { + return path.Join(p.rootPath, string(sid[0]), string(sid[1]), sid) +} + +// Read returns raw session store by session ID. +func (p *FileProvider) Read(sid string) (_ RawStore, err error) { + filename := p.filepath(sid) + if err = os.MkdirAll(path.Dir(filename), 0700); err != nil { + return nil, err + } + p.lock.RLock() + defer p.lock.RUnlock() + + var f *os.File + if com.IsFile(filename) { + f, err = os.OpenFile(filename, os.O_RDONLY, 0600) + } else { + f, err = os.Create(filename) + } + if err != nil { + return nil, err + } + defer f.Close() + + if err = os.Chtimes(filename, time.Now(), time.Now()); err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + data, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + if len(data) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = DecodeGob(data) + if err != nil { + return nil, err + } + } + return NewFileStore(p, sid, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *FileProvider) Exist(sid string) bool { + p.lock.RLock() + defer p.lock.RUnlock() + return com.IsFile(p.filepath(sid)) +} + +// Destroy deletes a session by session ID. +func (p *FileProvider) Destroy(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() + return os.Remove(p.filepath(sid)) +} + +func (p *FileProvider) regenerate(oldsid, sid string) (err error) { + p.lock.Lock() + defer p.lock.Unlock() + + filename := p.filepath(sid) + if com.IsExist(filename) { + return fmt.Errorf("new sid '%s' already exists", sid) + } + + oldname := p.filepath(oldsid) + if !com.IsFile(oldname) { + data, err := EncodeGob(make(map[interface{}]interface{})) + if err != nil { + return err + } + if err = os.MkdirAll(path.Dir(oldname), 0700); err != nil { + return err + } + if err = ioutil.WriteFile(oldname, data, 0600); err != nil { + return err + } + } + + if err = os.MkdirAll(path.Dir(filename), 0700); err != nil { + return err + } + if err = os.Rename(oldname, filename); err != nil { + return err + } + return nil +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *FileProvider) Regenerate(oldsid, sid string) (_ RawStore, err error) { + if err := p.regenerate(oldsid, sid); err != nil { + return nil, err + } + + return p.Read(sid) +} + +// Count counts and returns number of sessions. +func (p *FileProvider) Count() int { + count := 0 + if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if !fi.IsDir() { + count++ + } + return nil + }); err != nil { + log.Printf("error counting session files: %v", err) + return 0 + } + return count +} + +// GC calls GC to clean expired sessions. +func (p *FileProvider) GC() { + p.lock.RLock() + defer p.lock.RUnlock() + + if !com.IsExist(p.rootPath) { + return + } + + if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if !fi.IsDir() && + (fi.ModTime().Unix()+p.maxlifetime) < time.Now().Unix() { + return os.Remove(path) + } + return nil + }); err != nil { + log.Printf("error garbage collecting session files: %v", err) + } +} + +func init() { + Register("file", &FileProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/flash.go b/vendor/gitea.com/macaron/session/flash.go new file mode 100644 index 0000000000..93c461d47a --- /dev/null +++ b/vendor/gitea.com/macaron/session/flash.go @@ -0,0 +1,61 @@ +// Copyright 2018 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/url" + + "gitea.com/macaron/macaron" +) + +type Flash struct { + ctx *macaron.Context + url.Values + ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string +} + +func (f *Flash) set(name, msg string, current ...bool) { + isShow := false + if (len(current) == 0 && macaron.FlashNow) || + (len(current) > 0 && current[0]) { + isShow = true + } + + if isShow { + f.ctx.Data["Flash"] = f + } else { + f.Set(name, msg) + } +} + +func (f *Flash) Error(msg string, current ...bool) { + f.ErrorMsg = msg + f.set("error", msg, current...) +} + +func (f *Flash) Warning(msg string, current ...bool) { + f.WarningMsg = msg + f.set("warning", msg, current...) +} + +func (f *Flash) Info(msg string, current ...bool) { + f.InfoMsg = msg + f.set("info", msg, current...) +} + +func (f *Flash) Success(msg string, current ...bool) { + f.SuccessMsg = msg + f.set("success", msg, current...) +} diff --git a/vendor/gitea.com/macaron/session/go.mod b/vendor/gitea.com/macaron/session/go.mod new file mode 100644 index 0000000000..626199661d --- /dev/null +++ b/vendor/gitea.com/macaron/session/go.mod @@ -0,0 +1,32 @@ +module gitea.com/macaron/session + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 + github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d // indirect + github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b // indirect + github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 + github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 // indirect + github.com/edsrzf/mmap-go v1.0.0 // indirect + github.com/go-redis/redis v6.15.2+incompatible + github.com/go-sql-driver/mysql v1.4.1 + github.com/lib/pq v1.2.0 + github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de // indirect + github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af + github.com/mattn/go-sqlite3 v1.11.0 // indirect + github.com/pelletier/go-toml v1.4.0 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect + github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect + github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 + github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/stretchr/testify v1.3.0 // indirect + github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect + google.golang.org/appengine v1.6.1 // indirect + gopkg.in/ini.v1 v1.44.0 +) diff --git a/vendor/gitea.com/macaron/session/go.sum b/vendor/gitea.com/macaron/session/go.sum new file mode 100644 index 0000000000..dc7dc6f34a --- /dev/null +++ b/vendor/gitea.com/macaron/session/go.sum @@ -0,0 +1,116 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA= +github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d h1:XMf4E1U+b9E3ElF0mjvfXZdflBRZz4gLp16nQ/QSHQM= +github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= +github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b h1:bZ9rKU2/V8sY+NulSfxDOnXTWcs1rySqdF1sVepihvo= +github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= +github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8= +github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ= +github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= +github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk= +github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af h1:UaWHNBdukWrSG3DRvHFR/hyfg681fceqQDYVTBncKfQ= +github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= +github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68= +github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92 h1:qvsJwGToa8rxb42cDRhkbKeX2H5N8BH+s2aUikGt8mI= +github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs= +github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c h1:+EXw7AwNOKzPFXMZ1yNjO40aWCh3PIquJB2fYlv9wcs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gitea.com/macaron/session/memcache/memcache.go b/vendor/gitea.com/macaron/session/memcache/memcache.go new file mode 100644 index 0000000000..ff12097542 --- /dev/null +++ b/vendor/gitea.com/macaron/session/memcache/memcache.go @@ -0,0 +1,203 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "fmt" + "strings" + "sync" + + "gitea.com/macaron/session" + "github.com/bradfitz/gomemcache/memcache" +) + +// MemcacheStore represents a memcache session store implementation. +type MemcacheStore struct { + c *memcache.Client + sid string + expire int32 + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewMemcacheStore creates and returns a memcache session store. +func NewMemcacheStore(c *memcache.Client, sid string, expire int32, kv map[interface{}]interface{}) *MemcacheStore { + return &MemcacheStore{ + c: c, + sid: sid, + expire: expire, + data: kv, + } +} + +func NewItem(sid string, data []byte, expire int32) *memcache.Item { + return &memcache.Item{ + Key: sid, + Value: data, + Expiration: expire, + } +} + +// Set sets value to given key in session. +func (s *MemcacheStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *MemcacheStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *MemcacheStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *MemcacheStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *MemcacheStore) Release() error { + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + return s.c.Set(NewItem(s.sid, data, s.expire)) +} + +// Flush deletes all session data. +func (s *MemcacheStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// MemcacheProvider represents a memcache session provider implementation. +type MemcacheProvider struct { + c *memcache.Client + expire int32 +} + +// Init initializes memcache session provider. +// connStrs: 127.0.0.1:9090;127.0.0.1:9091 +func (p *MemcacheProvider) Init(expire int64, connStrs string) error { + p.expire = int32(expire) + p.c = memcache.New(strings.Split(connStrs, ";")...) + return nil +} + +// Read returns raw session store by session ID. +func (p *MemcacheProvider) Read(sid string) (session.RawStore, error) { + if !p.Exist(sid) { + if err := p.c.Set(NewItem(sid, []byte(""), p.expire)); err != nil { + return nil, err + } + } + + var kv map[interface{}]interface{} + item, err := p.c.Get(sid) + if err != nil { + return nil, err + } + if len(item.Value) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(item.Value) + if err != nil { + return nil, err + } + } + + return NewMemcacheStore(p.c, sid, p.expire, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *MemcacheProvider) Exist(sid string) bool { + _, err := p.c.Get(sid) + return err == nil +} + +// Destroy deletes a session by session ID. +func (p *MemcacheProvider) Destroy(sid string) error { + return p.c.Delete(sid) +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *MemcacheProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + item := NewItem(sid, []byte(""), p.expire) + if p.Exist(oldsid) { + item, err = p.c.Get(oldsid) + if err != nil { + return nil, err + } else if err = p.c.Delete(oldsid); err != nil { + return nil, err + } + item.Key = sid + } + if err = p.c.Set(item); err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(item.Value) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(item.Value) + if err != nil { + return nil, err + } + } + + return NewMemcacheStore(p.c, sid, p.expire, kv), nil +} + +// Count counts and returns number of sessions. +func (p *MemcacheProvider) Count() int { + // FIXME: how come this library does not have Stats method? + return -1 +} + +// GC calls GC to clean expired sessions. +func (p *MemcacheProvider) GC() {} + +func init() { + session.Register("memcache", &MemcacheProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/memcache/memcache.goconvey b/vendor/gitea.com/macaron/session/memcache/memcache.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/session/memcache/memcache.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/memory.go b/vendor/gitea.com/macaron/session/memory.go new file mode 100644 index 0000000000..fbb5b8013f --- /dev/null +++ b/vendor/gitea.com/macaron/session/memory.go @@ -0,0 +1,217 @@ +// Copyright 2013 Beego Authors
+// Copyright 2014 The Macaron Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package session
+
+import (
+ "container/list"
+ "fmt"
+ "sync"
+ "time"
+)
+
+// MemStore represents a in-memory session store implementation.
+type MemStore struct {
+ sid string
+ lock sync.RWMutex
+ data map[interface{}]interface{}
+ lastAccess time.Time
+}
+
+// NewMemStore creates and returns a memory session store.
+func NewMemStore(sid string) *MemStore {
+ return &MemStore{
+ sid: sid,
+ data: make(map[interface{}]interface{}),
+ lastAccess: time.Now(),
+ }
+}
+
+// Set sets value to given key in session.
+func (s *MemStore) Set(key, val interface{}) error {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.data[key] = val
+ return nil
+}
+
+// Get gets value by given key in session.
+func (s *MemStore) Get(key interface{}) interface{} {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+
+ return s.data[key]
+}
+
+// Delete deletes a key from session.
+func (s *MemStore) Delete(key interface{}) error {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ delete(s.data, key)
+ return nil
+}
+
+// ID returns current session ID.
+func (s *MemStore) ID() string {
+ return s.sid
+}
+
+// Release releases resource and save data to provider.
+func (_ *MemStore) Release() error {
+ return nil
+}
+
+// Flush deletes all session data.
+func (s *MemStore) Flush() error {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.data = make(map[interface{}]interface{})
+ return nil
+}
+
+// MemProvider represents a in-memory session provider implementation.
+type MemProvider struct {
+ lock sync.RWMutex
+ maxLifetime int64
+ data map[string]*list.Element
+ // A priority list whose lastAccess newer gets higer priority.
+ list *list.List
+}
+
+// Init initializes memory session provider.
+func (p *MemProvider) Init(maxLifetime int64, _ string) error {
+ p.lock.Lock()
+ p.maxLifetime = maxLifetime
+ p.lock.Unlock()
+ return nil
+}
+
+// update expands time of session store by given ID.
+func (p *MemProvider) update(sid string) error {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if e, ok := p.data[sid]; ok {
+ e.Value.(*MemStore).lastAccess = time.Now()
+ p.list.MoveToFront(e)
+ return nil
+ }
+ return nil
+}
+
+// Read returns raw session store by session ID.
+func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
+ p.lock.RLock()
+ e, ok := p.data[sid]
+ p.lock.RUnlock()
+
+ if ok {
+ if err = p.update(sid); err != nil {
+ return nil, err
+ }
+ return e.Value.(*MemStore), nil
+ }
+
+ // Create a new session.
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ s := NewMemStore(sid)
+ p.data[sid] = p.list.PushBack(s)
+ return s, nil
+}
+
+// Exist returns true if session with given ID exists.
+func (p *MemProvider) Exist(sid string) bool {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ _, ok := p.data[sid]
+ return ok
+}
+
+// Destroy deletes a session by session ID.
+func (p *MemProvider) Destroy(sid string) error {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ e, ok := p.data[sid]
+ if !ok {
+ return nil
+ }
+
+ p.list.Remove(e)
+ delete(p.data, sid)
+ return nil
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) {
+ if p.Exist(sid) {
+ return nil, fmt.Errorf("new sid '%s' already exists", sid)
+ }
+
+ s, err := p.Read(oldsid)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = p.Destroy(oldsid); err != nil {
+ return nil, err
+ }
+
+ s.(*MemStore).sid = sid
+
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ p.data[sid] = p.list.PushBack(s)
+ return s, nil
+}
+
+// Count counts and returns number of sessions.
+func (p *MemProvider) Count() int {
+ return p.list.Len()
+}
+
+// GC calls GC to clean expired sessions.
+func (p *MemProvider) GC() {
+ p.lock.RLock()
+ for {
+ // No session in the list.
+ e := p.list.Back()
+ if e == nil {
+ break
+ }
+
+ if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
+ p.lock.RUnlock()
+ p.lock.Lock()
+ p.list.Remove(e)
+ delete(p.data, e.Value.(*MemStore).sid)
+ p.lock.Unlock()
+ p.lock.RLock()
+ } else {
+ break
+ }
+ }
+ p.lock.RUnlock()
+}
+
+func init() {
+ Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)})
+}
diff --git a/vendor/gitea.com/macaron/session/mysql/mysql.go b/vendor/gitea.com/macaron/session/mysql/mysql.go new file mode 100644 index 0000000000..da5079b24a --- /dev/null +++ b/vendor/gitea.com/macaron/session/mysql/mysql.go @@ -0,0 +1,199 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "database/sql" + "fmt" + "log" + "sync" + "time" + + "gitea.com/macaron/session" + _ "github.com/go-sql-driver/mysql" +) + +// MysqlStore represents a mysql session store implementation. +type MysqlStore struct { + c *sql.DB + sid string + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewMysqlStore creates and returns a mysql session store. +func NewMysqlStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *MysqlStore { + return &MysqlStore{ + c: c, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *MysqlStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *MysqlStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *MysqlStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *MysqlStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *MysqlStore) Release() error { + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + _, err = s.c.Exec("UPDATE session SET data=?, expiry=? WHERE `key`=?", + data, time.Now().Unix(), s.sid) + return err +} + +// Flush deletes all session data. +func (s *MysqlStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// MysqlProvider represents a mysql session provider implementation. +type MysqlProvider struct { + c *sql.DB + expire int64 +} + +// Init initializes mysql session provider. +// connStr: username:password@protocol(address)/dbname?param=value +func (p *MysqlProvider) Init(expire int64, connStr string) (err error) { + p.expire = expire + + p.c, err = sql.Open("mysql", connStr) + if err != nil { + return err + } + return p.c.Ping() +} + +// Read returns raw session store by session ID. +func (p *MysqlProvider) Read(sid string) (session.RawStore, error) { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) + if err == sql.ErrNoRows { + _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", + sid, "", time.Now().Unix()) + } + if err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(data) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(data) + if err != nil { + return nil, err + } + } + + return NewMysqlStore(p.c, sid, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *MysqlProvider) Exist(sid string) bool { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) + if err != nil && err != sql.ErrNoRows { + panic("session/mysql: error checking existence: " + err.Error()) + } + return err != sql.ErrNoRows +} + +// Destroy deletes a session by session ID. +func (p *MysqlProvider) Destroy(sid string) error { + _, err := p.c.Exec("DELETE FROM session WHERE `key`=?", sid) + return err +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *MysqlProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + if !p.Exist(oldsid) { + if _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", + oldsid, "", time.Now().Unix()); err != nil { + return nil, err + } + } + + if _, err = p.c.Exec("UPDATE session SET `key`=? WHERE `key`=?", sid, oldsid); err != nil { + return nil, err + } + + return p.Read(sid) +} + +// Count counts and returns number of sessions. +func (p *MysqlProvider) Count() (total int) { + if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { + panic("session/mysql: error counting records: " + err.Error()) + } + return total +} + +// GC calls GC to clean expired sessions. +func (p *MysqlProvider) GC() { + if _, err := p.c.Exec("DELETE FROM session WHERE expiry + ? <= UNIX_TIMESTAMP(NOW())", p.expire); err != nil { + log.Printf("session/mysql: error garbage collecting: %v", err) + } +} + +func init() { + session.Register("mysql", &MysqlProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/mysql/mysql.goconvey b/vendor/gitea.com/macaron/session/mysql/mysql.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/session/mysql/mysql.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/nodb/nodb.go b/vendor/gitea.com/macaron/session/nodb/nodb.go new file mode 100644 index 0000000000..db174e7ed1 --- /dev/null +++ b/vendor/gitea.com/macaron/session/nodb/nodb.go @@ -0,0 +1,207 @@ +// Copyright 2015 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "fmt" + "sync" + + "gitea.com/macaron/session" + "github.com/lunny/nodb" + "github.com/lunny/nodb/config" +) + +// NodbStore represents a nodb session store implementation. +type NodbStore struct { + c *nodb.DB + sid string + expire int64 + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewNodbStore creates and returns a ledis session store. +func NewNodbStore(c *nodb.DB, sid string, expire int64, kv map[interface{}]interface{}) *NodbStore { + return &NodbStore{ + c: c, + expire: expire, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *NodbStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *NodbStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *NodbStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *NodbStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *NodbStore) Release() error { + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + if err = s.c.Set([]byte(s.sid), data); err != nil { + return err + } + _, err = s.c.Expire([]byte(s.sid), s.expire) + return err +} + +// Flush deletes all session data. +func (s *NodbStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// NodbProvider represents a ledis session provider implementation. +type NodbProvider struct { + c *nodb.DB + expire int64 +} + +// Init initializes nodb session provider. +func (p *NodbProvider) Init(expire int64, configs string) error { + p.expire = expire + + cfg := new(config.Config) + cfg.DataDir = configs + dbs, err := nodb.Open(cfg) + if err != nil { + return fmt.Errorf("session/nodb: error opening db: %v", err) + } + + p.c, err = dbs.Select(0) + return err +} + +// Read returns raw session store by session ID. +func (p *NodbProvider) Read(sid string) (session.RawStore, error) { + if !p.Exist(sid) { + if err := p.c.Set([]byte(sid), []byte("")); err != nil { + return nil, err + } + } + + var kv map[interface{}]interface{} + kvs, err := p.c.Get([]byte(sid)) + if err != nil { + return nil, err + } + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(kvs) + if err != nil { + return nil, err + } + } + + return NewNodbStore(p.c, sid, p.expire, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *NodbProvider) Exist(sid string) bool { + count, err := p.c.Exists([]byte(sid)) + return err == nil && count > 0 +} + +// Destroy deletes a session by session ID. +func (p *NodbProvider) Destroy(sid string) error { + _, err := p.c.Del([]byte(sid)) + return err +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *NodbProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + kvs := make([]byte, 0) + if p.Exist(oldsid) { + if kvs, err = p.c.Get([]byte(oldsid)); err != nil { + return nil, err + } else if _, err = p.c.Del([]byte(oldsid)); err != nil { + return nil, err + } + } + + if err = p.c.Set([]byte(sid), kvs); err != nil { + return nil, err + } else if _, err = p.c.Expire([]byte(sid), p.expire); err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + + return NewNodbStore(p.c, sid, p.expire, kv), nil +} + +// Count counts and returns number of sessions. +func (p *NodbProvider) Count() int { + // FIXME: how come this library does not have DbSize() method? + return -1 +} + +// GC calls GC to clean expired sessions. +func (p *NodbProvider) GC() {} + +func init() { + session.Register("nodb", &NodbProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/nodb/nodb.goconvey b/vendor/gitea.com/macaron/session/nodb/nodb.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/session/nodb/nodb.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/postgres/postgres.go b/vendor/gitea.com/macaron/session/postgres/postgres.go new file mode 100644 index 0000000000..c307241a3c --- /dev/null +++ b/vendor/gitea.com/macaron/session/postgres/postgres.go @@ -0,0 +1,200 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "database/sql" + "fmt" + "log" + "sync" + "time" + + "gitea.com/macaron/session" + _ "github.com/lib/pq" +) + +// PostgresStore represents a postgres session store implementation. +type PostgresStore struct { + c *sql.DB + sid string + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewPostgresStore creates and returns a postgres session store. +func NewPostgresStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *PostgresStore { + return &PostgresStore{ + c: c, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *PostgresStore) Set(key, value interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = value + return nil +} + +// Get gets value by given key in session. +func (s *PostgresStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *PostgresStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *PostgresStore) ID() string { + return s.sid +} + +// save postgres session values to database. +// must call this method to save values to database. +func (s *PostgresStore) Release() error { + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + _, err = s.c.Exec("UPDATE session SET data=$1, expiry=$2 WHERE key=$3", + data, time.Now().Unix(), s.sid) + return err +} + +// Flush deletes all session data. +func (s *PostgresStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// PostgresProvider represents a postgres session provider implementation. +type PostgresProvider struct { + c *sql.DB + maxlifetime int64 +} + +// Init initializes postgres session provider. +// connStr: user=a password=b host=localhost port=5432 dbname=c sslmode=disable +func (p *PostgresProvider) Init(maxlifetime int64, connStr string) (err error) { + p.maxlifetime = maxlifetime + + p.c, err = sql.Open("postgres", connStr) + if err != nil { + return err + } + return p.c.Ping() +} + +// Read returns raw session store by session ID. +func (p *PostgresProvider) Read(sid string) (session.RawStore, error) { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) + if err == sql.ErrNoRows { + _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", + sid, "", time.Now().Unix()) + } + if err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(data) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(data) + if err != nil { + return nil, err + } + } + + return NewPostgresStore(p.c, sid, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *PostgresProvider) Exist(sid string) bool { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) + if err != nil && err != sql.ErrNoRows { + panic("session/postgres: error checking existence: " + err.Error()) + } + return err != sql.ErrNoRows +} + +// Destroy deletes a session by session ID. +func (p *PostgresProvider) Destroy(sid string) error { + _, err := p.c.Exec("DELETE FROM session WHERE key=$1", sid) + return err +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *PostgresProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + if !p.Exist(oldsid) { + if _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", + oldsid, "", time.Now().Unix()); err != nil { + return nil, err + } + } + + if _, err = p.c.Exec("UPDATE session SET key=$1 WHERE key=$2", sid, oldsid); err != nil { + return nil, err + } + + return p.Read(sid) +} + +// Count counts and returns number of sessions. +func (p *PostgresProvider) Count() (total int) { + if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { + panic("session/postgres: error counting records: " + err.Error()) + } + return total +} + +// GC calls GC to clean expired sessions. +func (p *PostgresProvider) GC() { + if _, err := p.c.Exec("DELETE FROM session WHERE EXTRACT(EPOCH FROM NOW()) - expiry > $1", p.maxlifetime); err != nil { + log.Printf("session/postgres: error garbage collecting: %v", err) + } +} + +func init() { + session.Register("postgres", &PostgresProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/postgres/postgres.goconvey b/vendor/gitea.com/macaron/session/postgres/postgres.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/session/postgres/postgres.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/redis/redis.go b/vendor/gitea.com/macaron/session/redis/redis.go new file mode 100644 index 0000000000..5f242d6b37 --- /dev/null +++ b/vendor/gitea.com/macaron/session/redis/redis.go @@ -0,0 +1,239 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "fmt" + "strings" + "sync" + "time" + + "gitea.com/macaron/session" + "github.com/go-redis/redis" + "github.com/unknwon/com" + "gopkg.in/ini.v1" +) + +// RedisStore represents a redis session store implementation. +type RedisStore struct { + c *redis.Client + prefix, sid string + duration time.Duration + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewRedisStore creates and returns a redis session store. +func NewRedisStore(c *redis.Client, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { + return &RedisStore{ + c: c, + prefix: prefix, + sid: sid, + duration: dur, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *RedisStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *RedisStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *RedisStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *RedisStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *RedisStore) Release() error { + // Skip encoding if the data is empty + if len(s.data) == 0 { + return nil + } + + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + return s.c.Set(s.prefix+s.sid, string(data), s.duration).Err() +} + +// Flush deletes all session data. +func (s *RedisStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// RedisProvider represents a redis session provider implementation. +type RedisProvider struct { + c *redis.Client + duration time.Duration + prefix string +} + +// Init initializes redis session provider. +// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session; +func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { + p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime)) + if err != nil { + return err + } + + cfg, err := ini.Load([]byte(strings.Replace(configs, ",", "\n", -1))) + if err != nil { + return err + } + + opt := &redis.Options{ + Network: "tcp", + } + for k, v := range cfg.Section("").KeysHash() { + switch k { + case "network": + opt.Network = v + case "addr": + opt.Addr = v + case "password": + opt.Password = v + case "db": + opt.DB = com.StrTo(v).MustInt() + case "pool_size": + opt.PoolSize = com.StrTo(v).MustInt() + case "idle_timeout": + opt.IdleTimeout, err = time.ParseDuration(v + "s") + if err != nil { + return fmt.Errorf("error parsing idle timeout: %v", err) + } + case "prefix": + p.prefix = v + default: + return fmt.Errorf("session/redis: unsupported option '%s'", k) + } + } + + p.c = redis.NewClient(opt) + return p.c.Ping().Err() +} + +// Read returns raw session store by session ID. +func (p *RedisProvider) Read(sid string) (session.RawStore, error) { + psid := p.prefix + sid + if !p.Exist(sid) { + if err := p.c.Set(psid, "", p.duration).Err(); err != nil { + return nil, err + } + } + + var kv map[interface{}]interface{} + kvs, err := p.c.Get(psid).Result() + if err != nil { + return nil, err + } + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + + return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *RedisProvider) Exist(sid string) bool { + v, err := p.c.Exists(p.prefix + sid).Result() + return err == nil && v == 1 +} + +// Destroy deletes a session by session ID. +func (p *RedisProvider) Destroy(sid string) error { + return p.c.Del(p.prefix + sid).Err() +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + poldsid := p.prefix + oldsid + psid := p.prefix + sid + + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } else if !p.Exist(oldsid) { + // Make a fake old session. + if err = p.c.Set(poldsid, "", p.duration).Err(); err != nil { + return nil, err + } + } + + if err = p.c.Rename(poldsid, psid).Err(); err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + kvs, err := p.c.Get(psid).Result() + if err != nil { + return nil, err + } + + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + + return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil +} + +// Count counts and returns number of sessions. +func (p *RedisProvider) Count() int { + return int(p.c.DbSize().Val()) +} + +// GC calls GC to clean expired sessions. +func (_ *RedisProvider) GC() {} + +func init() { + session.Register("redis", &RedisProvider{}) +} diff --git a/vendor/gitea.com/macaron/session/redis/redis.goconvey b/vendor/gitea.com/macaron/session/redis/redis.goconvey new file mode 100644 index 0000000000..8485e986e4 --- /dev/null +++ b/vendor/gitea.com/macaron/session/redis/redis.goconvey @@ -0,0 +1 @@ +ignore
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/session/session.go b/vendor/gitea.com/macaron/session/session.go new file mode 100644 index 0000000000..93f18342d0 --- /dev/null +++ b/vendor/gitea.com/macaron/session/session.go @@ -0,0 +1,393 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package session a middleware that provides the session management of Macaron. +package session + +import ( + "encoding/hex" + "errors" + "fmt" + "net/http" + "net/url" + "time" + + "gitea.com/macaron/macaron" +) + +const _VERSION = "0.6.0" + +func Version() string { + return _VERSION +} + +// RawStore is the interface that operates the session data. +type RawStore interface { + // Set sets value to given key in session. + Set(interface{}, interface{}) error + // Get gets value by given key in session. + Get(interface{}) interface{} + // Delete deletes a key from session. + Delete(interface{}) error + // ID returns current session ID. + ID() string + // Release releases session resource and save data to provider. + Release() error + // Flush deletes all session data. + Flush() error +} + +// Store is the interface that contains all data for one session process with specific ID. +type Store interface { + RawStore + // Read returns raw session store by session ID. + Read(string) (RawStore, error) + // Destroy deletes a session. + Destroy(*macaron.Context) error + // RegenerateId regenerates a session store from old session ID to new one. + RegenerateId(*macaron.Context) (RawStore, error) + // Count counts and returns number of sessions. + Count() int + // GC calls GC to clean expired sessions. + GC() +} + +type store struct { + RawStore + *Manager +} + +var _ Store = &store{} + +// Options represents a struct for specifying configuration options for the session middleware. +type Options struct { + // Name of provider. Default is "memory". + Provider string + // Provider configuration, it's corresponding to provider. + ProviderConfig string + // Cookie name to save session ID. Default is "MacaronSession". + CookieName string + // Cookie path to store. Default is "/". + CookiePath string + // GC interval time in seconds. Default is 3600. + Gclifetime int64 + // Max life time in seconds. Default is whatever GC interval time is. + Maxlifetime int64 + // Use HTTPS only. Default is false. + Secure bool + // Cookie life time. Default is 0. + CookieLifeTime int + // Cookie domain name. Default is empty. + Domain string + // Session ID length. Default is 16. + IDLength int + // Configuration section name. Default is "session". + Section string + // Ignore release for websocket. Default is false. + IgnoreReleaseForWebSocket bool +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + if len(opt.Section) == 0 { + opt.Section = "session" + } + sec := macaron.Config().Section(opt.Section) + + if len(opt.Provider) == 0 { + opt.Provider = sec.Key("PROVIDER").MustString("memory") + } + if len(opt.ProviderConfig) == 0 { + opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions") + } + if len(opt.CookieName) == 0 { + opt.CookieName = sec.Key("COOKIE_NAME").MustString("MacaronSession") + } + if len(opt.CookiePath) == 0 { + opt.CookiePath = sec.Key("COOKIE_PATH").MustString("/") + } + if opt.Gclifetime == 0 { + opt.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(3600) + } + if opt.Maxlifetime == 0 { + opt.Maxlifetime = sec.Key("MAX_LIFE_TIME").MustInt64(opt.Gclifetime) + } + if !opt.Secure { + opt.Secure = sec.Key("SECURE").MustBool() + } + if opt.CookieLifeTime == 0 { + opt.CookieLifeTime = sec.Key("COOKIE_LIFE_TIME").MustInt() + } + if len(opt.Domain) == 0 { + opt.Domain = sec.Key("DOMAIN").String() + } + if opt.IDLength == 0 { + opt.IDLength = sec.Key("ID_LENGTH").MustInt(16) + } + if !opt.IgnoreReleaseForWebSocket { + opt.IgnoreReleaseForWebSocket = sec.Key("IGNORE_RELEASE_FOR_WEBSOCKET").MustBool() + } + + return opt +} + +// Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain. +// An single variadic session.Options struct can be optionally provided to configure. +func Sessioner(options ...Options) macaron.Handler { + opt := prepareOptions(options) + manager, err := NewManager(opt.Provider, opt) + if err != nil { + panic(err) + } + go manager.startGC() + + return func(ctx *macaron.Context) { + sess, err := manager.Start(ctx) + if err != nil { + panic("session(start): " + err.Error()) + } + + // Get flash. + vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash")) + if len(vals) > 0 { + f := &Flash{Values: vals} + f.ErrorMsg = f.Get("error") + f.SuccessMsg = f.Get("success") + f.InfoMsg = f.Get("info") + f.WarningMsg = f.Get("warning") + ctx.Data["Flash"] = f + ctx.SetCookie("macaron_flash", "", -1, opt.CookiePath) + } + + f := &Flash{ctx, url.Values{}, "", "", "", ""} + ctx.Resp.Before(func(macaron.ResponseWriter) { + if flash := f.Encode(); len(flash) > 0 { + ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath) + } + }) + + ctx.Map(f) + s := store{ + RawStore: sess, + Manager: manager, + } + + ctx.MapTo(s, (*Store)(nil)) + + ctx.Next() + + if manager.opt.IgnoreReleaseForWebSocket && ctx.Req.Header.Get("Upgrade") == "websocket" { + return + } + + if err = sess.Release(); err != nil { + panic("session(release): " + err.Error()) + } + } +} + +// Provider is the interface that provides session manipulations. +type Provider interface { + // Init initializes session provider. + Init(gclifetime int64, config string) error + // Read returns raw session store by session ID. + Read(sid string) (RawStore, error) + // Exist returns true if session with given ID exists. + Exist(sid string) bool + // Destroy deletes a session by session ID. + Destroy(sid string) error + // Regenerate regenerates a session store from old session ID to new one. + Regenerate(oldsid, sid string) (RawStore, error) + // Count counts and returns number of sessions. + Count() int + // GC calls GC to clean expired sessions. + GC() +} + +var providers = make(map[string]Provider) + +// Register registers a provider. +func Register(name string, provider Provider) { + if provider == nil { + panic("session: cannot register provider with nil value") + } + if _, dup := providers[name]; dup { + panic(fmt.Errorf("session: cannot register provider '%s' twice", name)) + } + providers[name] = provider +} + +// _____ +// / \ _____ ____ _____ ____ ___________ +// / \ / \\__ \ / \\__ \ / ___\_/ __ \_ __ \ +// / Y \/ __ \| | \/ __ \_/ /_/ > ___/| | \/ +// \____|__ (____ /___| (____ /\___ / \___ >__| +// \/ \/ \/ \//_____/ \/ + +// Manager represents a struct that contains session provider and its configuration. +type Manager struct { + provider Provider + opt Options +} + +// NewManager creates and returns a new session manager by given provider name and configuration. +// It panics when given provider isn't registered. +func NewManager(name string, opt Options) (*Manager, error) { + p, ok := providers[name] + if !ok { + return nil, fmt.Errorf("session: unknown provider '%s'(forgotten import?)", name) + } + return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig) +} + +// sessionID generates a new session ID with rand string, unix nano time, remote addr by hash function. +func (m *Manager) sessionID() string { + return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2)) +} + +// validSessionID tests whether a provided session ID is a valid session ID. +func (m *Manager) validSessionID(sid string) (bool, error) { + if len(sid) != m.opt.IDLength { + return false, errors.New("invalid 'sid': " + sid) + } + + for i := range sid { + switch { + case '0' <= sid[i] && sid[i] <= '9': + case 'a' <= sid[i] && sid[i] <= 'f': + default: + return false, errors.New("invalid 'sid': " + sid) + } + } + return true, nil +} + +// Start starts a session by generating new one +// or retrieve existence one by reading session ID from HTTP request if it's valid. +func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) { + sid := ctx.GetCookie(m.opt.CookieName) + valid, _ := m.validSessionID(sid) + if len(sid) > 0 && valid && m.provider.Exist(sid) { + return m.provider.Read(sid) + } + + sid = m.sessionID() + sess, err := m.provider.Read(sid) + if err != nil { + return nil, err + } + + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Value: sid, + Path: m.opt.CookiePath, + HttpOnly: true, + Secure: m.opt.Secure, + Domain: m.opt.Domain, + } + if m.opt.CookieLifeTime >= 0 { + cookie.MaxAge = m.opt.CookieLifeTime + } + http.SetCookie(ctx.Resp, cookie) + ctx.Req.AddCookie(cookie) + return sess, nil +} + +// Read returns raw session store by session ID. +func (m *Manager) Read(sid string) (RawStore, error) { + // Ensure we're trying to read a valid session ID + if _, err := m.validSessionID(sid); err != nil { + return nil, err + } + + return m.provider.Read(sid) +} + +// Destroy deletes a session by given ID. +func (m *Manager) Destroy(ctx *macaron.Context) error { + sid := ctx.GetCookie(m.opt.CookieName) + if len(sid) == 0 { + return nil + } + + if _, err := m.validSessionID(sid); err != nil { + return err + } + + if err := m.provider.Destroy(sid); err != nil { + return err + } + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Path: m.opt.CookiePath, + HttpOnly: true, + Expires: time.Now(), + MaxAge: -1, + } + http.SetCookie(ctx.Resp, cookie) + return nil +} + +// RegenerateId regenerates a session store from old session ID to new one. +func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) { + sid := m.sessionID() + oldsid := ctx.GetCookie(m.opt.CookieName) + _, err = m.validSessionID(oldsid) + if err != nil { + return nil, err + } + sess, err = m.provider.Regenerate(oldsid, sid) + if err != nil { + return nil, err + } + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Value: sid, + Path: m.opt.CookiePath, + HttpOnly: true, + Secure: m.opt.Secure, + Domain: m.opt.Domain, + } + if m.opt.CookieLifeTime >= 0 { + cookie.MaxAge = m.opt.CookieLifeTime + } + http.SetCookie(ctx.Resp, cookie) + ctx.Req.AddCookie(cookie) + return sess, nil +} + +// Count counts and returns number of sessions. +func (m *Manager) Count() int { + return m.provider.Count() +} + +// GC starts GC job in a certain period. +func (m *Manager) GC() { + m.provider.GC() +} + +// startGC starts GC job in a certain period. +func (m *Manager) startGC() { + m.GC() + time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() }) +} + +// SetSecure indicates whether to set cookie with HTTPS or not. +func (m *Manager) SetSecure(secure bool) { + m.opt.Secure = secure +} diff --git a/vendor/gitea.com/macaron/session/utils.go b/vendor/gitea.com/macaron/session/utils.go new file mode 100644 index 0000000000..762b978cdf --- /dev/null +++ b/vendor/gitea.com/macaron/session/utils.go @@ -0,0 +1,63 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "bytes" + "crypto/rand" + "encoding/gob" + "io" + + "github.com/unknwon/com" +) + +func init() { + gob.Register([]interface{}{}) + gob.Register(map[int]interface{}{}) + gob.Register(map[string]interface{}{}) + gob.Register(map[interface{}]interface{}{}) + gob.Register(map[string]string{}) + gob.Register(map[int]string{}) + gob.Register(map[int]int{}) + gob.Register(map[int]int64{}) +} + +func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { + for _, v := range obj { + gob.Register(v) + } + buf := bytes.NewBuffer(nil) + err := gob.NewEncoder(buf).Encode(obj) + return buf.Bytes(), err +} + +func DecodeGob(encoded []byte) (out map[interface{}]interface{}, err error) { + buf := bytes.NewBuffer(encoded) + err = gob.NewDecoder(buf).Decode(&out) + return out, err +} + +// NOTE: A local copy in case of underlying package change +var alphanum = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") + +// generateRandomKey creates a random key with the given strength. +func generateRandomKey(strength int) []byte { + k := make([]byte, strength) + if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil { + return com.RandomCreateBytes(strength, alphanum...) + } + return k +} diff --git a/vendor/gitea.com/macaron/toolbox/.drone.yml b/vendor/gitea.com/macaron/toolbox/.drone.yml new file mode 100644 index 0000000000..39499f444a --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/.drone.yml @@ -0,0 +1,9 @@ +kind: pipeline +name: default + +steps: +- name: test + image: golang:1.11 + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic diff --git a/vendor/gitea.com/macaron/toolbox/.gitignore b/vendor/gitea.com/macaron/toolbox/.gitignore new file mode 100644 index 0000000000..c3265c1186 --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/.gitignore @@ -0,0 +1,2 @@ +profile/ +/.idea diff --git a/vendor/gitea.com/macaron/toolbox/LICENSE b/vendor/gitea.com/macaron/toolbox/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/toolbox/README.md b/vendor/gitea.com/macaron/toolbox/README.md new file mode 100644 index 0000000000..75055d5f07 --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/README.md @@ -0,0 +1,112 @@ +toolbox +======= + +Middleware toolbox provides health chcek, pprof, profile and statistic services for [Macaron](https://gitea.com/macaron/macaron). + +[![Build Status](https://drone.gitea.com/api/badges/macaron/toolbox/status.svg)](https://drone.gitea.com/macaron/toolbox) +[API Reference](https://gowalker.org/gitea.com/macaron/toolbox) + +### Installation + + go get gitea.com/macaron/toolbox + +## Usage + +```go +// main.go +import ( + "gitea.com/macaron/macaron" + "gitea.com/macaron/toolbox" +) + +func main() { + m := macaron.Classic() + m.Use(toolbox.Toolboxer(m)) + m.Run() +} +``` + +Open your browser and visit `http://localhost:4000/debug` to see the effects. + +## Options + +`toolbox.Toolboxer` comes with a variety of configuration options: + +```go +type dummyChecker struct { +} + +func (dc *dummyChecker) Desc() string { + return "Dummy checker" +} + +func (dc *dummyChecker) Check() error { + return nil +} + +// ... +m.Use(toolbox.Toolboxer(m, toolbox.Options{ + URLPrefix: "/debug", // URL prefix for toolbox dashboard + HealthCheckURL: "/healthcheck", // URL for health check request + HealthCheckers: []HealthChecker{ + new(dummyChecker), + }, // Health checkers + HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{ + &toolbox.HealthCheckFuncDesc{ + Desc: "Database connection", + Func: func() error { return "OK" }, + }, + }, // Health check functions + DisableDebug: false, // Turns off all debug functionality when true + PprofURLPrefix: "/debug/pprof/", // URL prefix of pprof + ProfileURLPrefix: "/debug/profile/", // URL prefix of profile + ProfilePath: "profile", // Path store profile files +})) +// ... +``` + +## Route Statistic + +Toolbox also comes with a route call statistic functionality: + +```go +import ( + "os" + "time" + //... + "gitea.com/macaron/toolbox" +) + +func main() { + //... + m.Get("/", func(t toolbox.Toolbox) { + start := time.Now() + + // Other operations. + + t.AddStatistics("GET", "/", time.Since(start)) + }) + + m.Get("/dump", func(t toolbox.Toolbox) { + t.GetMap(os.Stdout) + }) +} +``` + +Output take from test: + +``` ++---------------------------------------------------+------------+------------------+------------------+------------------+------------------+------------------+ +| Request URL | Method | Times | Total Used(s) | Max Used(μs) | Min Used(μs) | Avg Used(μs) | ++---------------------------------------------------+------------+------------------+------------------+------------------+------------------+------------------+ +| /api/user | POST | 2 | 0.000122 | 120.000000 | 2.000000 | 61.000000 | +| /api/user | GET | 1 | 0.000013 | 13.000000 | 13.000000 | 13.000000 | +| /api/user | DELETE | 1 | 0.000001 | 1.400000 | 1.400000 | 1.400000 | +| /api/admin | POST | 1 | 0.000014 | 14.000000 | 14.000000 | 14.000000 | +| /api/user/unknwon | POST | 1 | 0.000012 | 12.000000 | 12.000000 | 12.000000 | ++---------------------------------------------------+------------+------------------+------------------+------------------+------------------+------------------+ +``` + +## License + +This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
\ No newline at end of file diff --git a/vendor/gitea.com/macaron/toolbox/go.mod b/vendor/gitea.com/macaron/toolbox/go.mod new file mode 100644 index 0000000000..f54b8074ca --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/go.mod @@ -0,0 +1,9 @@ +module gitea.com/macaron/toolbox + +go 1.11 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e +) diff --git a/vendor/gitea.com/macaron/toolbox/go.sum b/vendor/gitea.com/macaron/toolbox/go.sum new file mode 100644 index 0000000000..56302b6a5f --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/go.sum @@ -0,0 +1,30 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gitea.com/macaron/toolbox/healthcheck.go b/vendor/gitea.com/macaron/toolbox/healthcheck.go new file mode 100644 index 0000000000..25b5bdfe26 --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/healthcheck.go @@ -0,0 +1,83 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package toolbox + +import ( + "bytes" +) + +// HealthChecker represents a health check instance. +type HealthChecker interface { + Desc() string + Check() error +} + +// HealthCheckFunc represents a callable function for health check. +type HealthCheckFunc func() error + +// HealthCheckFunc represents a callable function for health check with description. +type HealthCheckFuncDesc struct { + Desc string + Func HealthCheckFunc +} + +type healthCheck struct { + desc string + HealthChecker + check HealthCheckFunc // Not nil if add job as a function. +} + +// AddHealthCheck adds new health check job. +func (t *toolbox) AddHealthCheck(hc HealthChecker) { + t.healthCheckJobs = append(t.healthCheckJobs, &healthCheck{ + HealthChecker: hc, + }) +} + +// AddHealthCheckFunc adds a function as a new health check job. +func (t *toolbox) AddHealthCheckFunc(desc string, fn HealthCheckFunc) { + t.healthCheckJobs = append(t.healthCheckJobs, &healthCheck{ + desc: desc, + check: fn, + }) +} + +func (t *toolbox) handleHealthCheck() string { + if len(t.healthCheckJobs) == 0 { + return "no health check jobs" + } + + var buf bytes.Buffer + var err error + for _, job := range t.healthCheckJobs { + buf.WriteString("* ") + if job.check != nil { + buf.WriteString(job.desc) + err = job.check() + } else { + buf.WriteString(job.Desc()) + err = job.Check() + } + buf.WriteString(": ") + if err == nil { + buf.WriteString("OK") + } else { + buf.WriteString(err.Error()) + } + buf.WriteString("\n") + } + return buf.String() +} diff --git a/vendor/gitea.com/macaron/toolbox/profile.go b/vendor/gitea.com/macaron/toolbox/profile.go new file mode 100644 index 0000000000..359a87fde4 --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/profile.go @@ -0,0 +1,163 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package toolbox + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path" + "runtime" + "runtime/debug" + "runtime/pprof" + "time" + + "gitea.com/macaron/macaron" + "github.com/unknwon/com" +) + +var ( + profilePath string + pid int + startTime = time.Now() + inCPUProfile bool +) + +// StartCPUProfile starts CPU profile monitor. +func StartCPUProfile() error { + if inCPUProfile { + return errors.New("CPU profile has alreday been started!") + } + inCPUProfile = true + + os.MkdirAll(profilePath, os.ModePerm) + f, err := os.Create(path.Join(profilePath, "cpu-"+com.ToStr(pid)+".pprof")) + if err != nil { + panic("fail to record CPU profile: " + err.Error()) + } + pprof.StartCPUProfile(f) + return nil +} + +// StopCPUProfile stops CPU profile monitor. +func StopCPUProfile() error { + if !inCPUProfile { + return errors.New("CPU profile hasn't been started!") + } + pprof.StopCPUProfile() + inCPUProfile = false + return nil +} + +func init() { + pid = os.Getpid() +} + +// DumpMemProf dumps memory profile in pprof. +func DumpMemProf(w io.Writer) { + pprof.WriteHeapProfile(w) +} + +func dumpMemProf() { + os.MkdirAll(profilePath, os.ModePerm) + f, err := os.Create(path.Join(profilePath, "mem-"+com.ToStr(pid)+".memprof")) + if err != nil { + panic("fail to record memory profile: " + err.Error()) + } + runtime.GC() + DumpMemProf(f) + f.Close() +} + +func avg(items []time.Duration) time.Duration { + var sum time.Duration + for _, item := range items { + sum += item + } + return time.Duration(int64(sum) / int64(len(items))) +} + +func dumpGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { + + if gcstats.NumGC > 0 { + lastPause := gcstats.Pause[0] + elapsed := time.Now().Sub(startTime) + overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 + allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() + + fmt.Fprintf(w, "NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n", + gcstats.NumGC, + com.ToStr(lastPause), + com.ToStr(avg(gcstats.Pause)), + overhead, + com.HumaneFileSize(memStats.Alloc), + com.HumaneFileSize(memStats.Sys), + com.HumaneFileSize(uint64(allocatedRate)), + com.ToStr(gcstats.PauseQuantiles[94]), + com.ToStr(gcstats.PauseQuantiles[98]), + com.ToStr(gcstats.PauseQuantiles[99])) + } else { + // while GC has disabled + elapsed := time.Now().Sub(startTime) + allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() + + fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n", + com.HumaneFileSize(memStats.Alloc), + com.HumaneFileSize(memStats.Sys), + com.HumaneFileSize(uint64(allocatedRate))) + } +} + +// DumpGCSummary dumps GC information to io.Writer +func DumpGCSummary(w io.Writer) { + memStats := &runtime.MemStats{} + runtime.ReadMemStats(memStats) + gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} + debug.ReadGCStats(gcstats) + + dumpGC(memStats, gcstats, w) +} + +func handleProfile(ctx *macaron.Context) string { + switch ctx.Query("op") { + case "startcpu": + if err := StartCPUProfile(); err != nil { + return err.Error() + } + case "stopcpu": + if err := StopCPUProfile(); err != nil { + return err.Error() + } + case "mem": + dumpMemProf() + case "gc": + var buf bytes.Buffer + DumpGCSummary(&buf) + return string(buf.Bytes()) + default: + return fmt.Sprintf(`<p>Available operations:</p> +<ol> + <li><a href="%[1]s?op=startcpu">Start CPU profile</a></li> + <li><a href="%[1]s?op=stopcpu">Stop CPU profile</a></li> + <li><a href="%[1]s?op=mem">Dump memory profile</a></li> + <li><a href="%[1]s?op=gc">Dump GC summary</a></li> +</ol>`, opt.ProfileURLPrefix) + } + ctx.Redirect(opt.ProfileURLPrefix) + return "" +} diff --git a/vendor/gitea.com/macaron/toolbox/statistic.go b/vendor/gitea.com/macaron/toolbox/statistic.go new file mode 100644 index 0000000000..47e6ab23ee --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/statistic.go @@ -0,0 +1,138 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package toolbox + +import ( + "encoding/json" + "fmt" + "io" + "strings" + "sync" + "time" +) + +// Statistics struct +type Statistics struct { + RequestUrl string + RequestNum int64 + MinTime time.Duration + MaxTime time.Duration + TotalTime time.Duration +} + +// UrlMap contains several statistics struct to log different data +type UrlMap struct { + lock sync.RWMutex + LengthLimit int // limit the urlmap's length if it's equal to 0 there's no limit + urlmap map[string]map[string]*Statistics +} + +// add statistics task. +// it needs request method, request url and statistics time duration +func (m *UrlMap) AddStatistics(requestMethod, requestUrl string, requesttime time.Duration) { + m.lock.Lock() + defer m.lock.Unlock() + + if method, ok := m.urlmap[requestUrl]; ok { + if s, ok := method[requestMethod]; ok { + s.RequestNum += 1 + if s.MaxTime < requesttime { + s.MaxTime = requesttime + } + if s.MinTime > requesttime { + s.MinTime = requesttime + } + s.TotalTime += requesttime + } else { + nb := &Statistics{ + RequestUrl: requestUrl, + RequestNum: 1, + MinTime: requesttime, + MaxTime: requesttime, + TotalTime: requesttime, + } + m.urlmap[requestUrl][requestMethod] = nb + } + + } else { + if m.LengthLimit > 0 && m.LengthLimit <= len(m.urlmap) { + return + } + methodmap := make(map[string]*Statistics) + nb := &Statistics{ + RequestUrl: requestUrl, + RequestNum: 1, + MinTime: requesttime, + MaxTime: requesttime, + TotalTime: requesttime, + } + methodmap[requestMethod] = nb + m.urlmap[requestUrl] = methodmap + } +} + +// put url statistics result in io.Writer +func (m *UrlMap) GetMap(w io.Writer) { + m.lock.RLock() + defer m.lock.RUnlock() + + sep := fmt.Sprintf("+%s+%s+%s+%s+%s+%s+%s+\n", strings.Repeat("-", 51), strings.Repeat("-", 12), + strings.Repeat("-", 18), strings.Repeat("-", 18), strings.Repeat("-", 18), strings.Repeat("-", 18), strings.Repeat("-", 18)) + fmt.Fprintf(w, sep) + fmt.Fprintf(w, "| % -50s| % -10s | % -16s | % -16s | % -16s | % -16s | % -16s |\n", "Request URL", "Method", "Times", "Total Used(s)", "Max Used(μs)", "Min Used(μs)", "Avg Used(μs)") + fmt.Fprintf(w, sep) + + for k, v := range m.urlmap { + for kk, vv := range v { + fmt.Fprintf(w, "| % -50s| % -10s | % 16d | % 16f | % 16.6f | % 16.6f | % 16.6f |\n", k, + kk, vv.RequestNum, vv.TotalTime.Seconds(), float64(vv.MaxTime.Nanoseconds())/1000, + float64(vv.MinTime.Nanoseconds())/1000, float64(time.Duration(int64(vv.TotalTime)/vv.RequestNum).Nanoseconds())/1000, + ) + } + } + fmt.Fprintf(w, sep) +} + +type URLMapInfo struct { + URL string `json:"url"` + Method string `json:"method"` + Times int64 `json:"times"` + TotalUsed float64 `json:"total_used"` + MaxUsed float64 `json:"max_used"` + MinUsed float64 `json:"min_used"` + AvgUsed float64 `json:"avg_used"` +} + +func (m *UrlMap) JSON(w io.Writer) { + infos := make([]*URLMapInfo, 0, len(m.urlmap)) + for k, v := range m.urlmap { + for kk, vv := range v { + infos = append(infos, &URLMapInfo{ + URL: k, + Method: kk, + Times: vv.RequestNum, + TotalUsed: vv.TotalTime.Seconds(), + MaxUsed: float64(vv.MaxTime.Nanoseconds()) / 1000, + MinUsed: float64(vv.MinTime.Nanoseconds()) / 1000, + AvgUsed: float64(time.Duration(int64(vv.TotalTime)/vv.RequestNum).Nanoseconds()) / 1000, + }) + } + } + + if err := json.NewEncoder(w).Encode(infos); err != nil { + panic("URLMap.JSON: " + err.Error()) + } +} diff --git a/vendor/gitea.com/macaron/toolbox/toolbox.go b/vendor/gitea.com/macaron/toolbox/toolbox.go new file mode 100644 index 0000000000..42e565e45b --- /dev/null +++ b/vendor/gitea.com/macaron/toolbox/toolbox.go @@ -0,0 +1,158 @@ +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package toolbox is a middleware that provides health check, pprof, profile and statistic services for Macaron. +package toolbox + +import ( + "fmt" + "io" + "net/http" + "net/http/pprof" + "path" + "time" + + "gitea.com/macaron/macaron" +) + +const _VERSION = "0.1.4" + +func Version() string { + return _VERSION +} + +// Toolbox represents a tool box service for Macaron instance. +type Toolbox interface { + AddHealthCheck(HealthChecker) + AddHealthCheckFunc(string, HealthCheckFunc) + AddStatistics(string, string, time.Duration) + GetMap(io.Writer) + JSON(io.Writer) +} + +type toolbox struct { + *UrlMap + healthCheckJobs []*healthCheck +} + +// Options represents a struct for specifying configuration options for the Toolbox middleware. +type Options struct { + // URL prefix for toolbox dashboard. Default is "/debug". + URLPrefix string + // URL for health check request. Default is "/healthcheck". + HealthCheckURL string + // Health checkers. + HealthCheckers []HealthChecker + // Health check functions. + HealthCheckFuncs []*HealthCheckFuncDesc + // URL for URL map json. Default is "/urlmap.json". + URLMapPrefix string + // DisableDebug turns off all debug functionality. + DisableDebug bool + // URL prefix of pprof. Default is "/debug/pprof/". + PprofURLPrefix string + // URL prefix of profile. Default is "/debug/profile/". + ProfileURLPrefix string + // Path store profile files. Default is "profile". + ProfilePath string +} + +var opt Options + +func prepareOptions(options []Options) { + if len(options) > 0 { + opt = options[0] + } + + // Defaults. + if len(opt.URLPrefix) == 0 { + opt.URLPrefix = "/debug" + } + if len(opt.HealthCheckURL) == 0 { + opt.HealthCheckURL = "/healthcheck" + } + if len(opt.URLMapPrefix) == 0 { + opt.URLMapPrefix = "/urlmap.json" + } + if len(opt.PprofURLPrefix) == 0 { + opt.PprofURLPrefix = "/debug/pprof/" + } else if opt.PprofURLPrefix[len(opt.PprofURLPrefix)-1] != '/' { + opt.PprofURLPrefix += "/" + } + if len(opt.ProfileURLPrefix) == 0 { + opt.ProfileURLPrefix = "/debug/profile/" + } else if opt.ProfileURLPrefix[len(opt.ProfileURLPrefix)-1] != '/' { + opt.ProfileURLPrefix += "/" + } + if len(opt.ProfilePath) == 0 { + opt.ProfilePath = path.Join(macaron.Root, "profile") + } +} + +func dashboard() string { + return fmt.Sprintf(`<p>Toolbox Index:</p> + <ol> + <li><a href="%s">Pprof Information</a></li> + <li><a href="%s">Profile Operations</a></li> + </ol>`, opt.PprofURLPrefix, opt.ProfileURLPrefix) +} + +var _ Toolbox = &toolbox{} + +// Toolboxer is a middleware provides health check, pprof, profile and statistic services for your application. +func Toolboxer(m *macaron.Macaron, options ...Options) macaron.Handler { + prepareOptions(options) + t := &toolbox{ + healthCheckJobs: make([]*healthCheck, 0, len(opt.HealthCheckers)+len(opt.HealthCheckFuncs)), + } + + // Dashboard. + m.Get(opt.URLPrefix, dashboard) + + // Health check. + for _, hc := range opt.HealthCheckers { + t.AddHealthCheck(hc) + } + for _, fd := range opt.HealthCheckFuncs { + t.AddHealthCheckFunc(fd.Desc, fd.Func) + } + m.Route(opt.HealthCheckURL, "HEAD,GET", t.handleHealthCheck) + + // URL map. + m.Get(opt.URLMapPrefix, func(rw http.ResponseWriter) { + t.JSON(rw) + }) + + if !opt.DisableDebug { + // Pprof + m.Any(path.Join(opt.PprofURLPrefix, "cmdline"), pprof.Cmdline) + m.Any(path.Join(opt.PprofURLPrefix, "profile"), pprof.Profile) + m.Any(path.Join(opt.PprofURLPrefix, "symbol"), pprof.Symbol) + m.Any(opt.PprofURLPrefix, pprof.Index) + m.Any(path.Join(opt.PprofURLPrefix, "*"), pprof.Index) + + // Profile + profilePath = opt.ProfilePath + m.Get(opt.ProfileURLPrefix, handleProfile) + } + + // Routes statistic. + t.UrlMap = &UrlMap{ + urlmap: make(map[string]map[string]*Statistics), + } + + return func(ctx *macaron.Context) { + ctx.MapTo(t, (*Toolbox)(nil)) + } +} |