From 6433ba0ec3dfde67f45267aa12bd713c4a44c740 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Jan 2021 23:36:53 +0800 Subject: [PATCH] Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de> --- .golangci.yml | 2 +- cmd/dump.go | 2 +- cmd/web.go | 9 +- contrib/pr/checkout.go | 4 +- .../doc/advanced/config-cheat-sheet.en-us.md | 6 +- .../advanced/logging-documentation.en-us.md | 37 +- docs/content/page/index.en-us.md | 2 +- docs/content/page/index.fr-fr.md | 2 +- docs/content/page/index.zh-cn.md | 2 +- docs/content/page/index.zh-tw.md | 2 +- go.mod | 16 +- go.sum | 60 +- .../api_helper_for_declarative_test.go | 2 +- integrations/api_pull_test.go | 2 +- integrations/api_releases_test.go | 8 +- integrations/create_no_session_test.go | 10 +- integrations/integration_test.go | 11 +- integrations/lfs_getobject_test.go | 8 +- integrations/links_test.go | 3 +- modules/auth/sso/interface.go | 12 +- modules/auth/sso/oauth2.go | 2 + modules/cache/cache.go | 6 +- modules/cache/cache_redis.go | 2 +- modules/context/api.go | 141 ++- modules/context/auth.go | 129 ++- modules/context/captcha.go | 26 + modules/context/context.go | 559 ++++++++--- .../macaron/csrf => modules/context}/csrf.go | 144 ++- modules/context/form.go | 227 +++++ modules/context/org.go | 4 +- modules/context/permission.go | 12 +- modules/context/private.go | 45 + modules/context/repo.go | 570 +++++------ modules/context/response.go | 29 +- modules/context/secret.go | 100 ++ .../macaron/csrf => modules/context}/xsrf.go | 13 +- modules/context/xsrf_test.go | 90 ++ modules/{auth => forms}/admin.go | 25 +- modules/{auth => forms}/auth_form.go | 15 +- modules/{auth => forms}/org.go | 24 +- modules/{auth => forms}/repo_branch_form.go | 15 +- modules/{auth => forms}/repo_form.go | 173 ++-- modules/{auth => forms}/repo_form_test.go | 2 +- modules/{auth => forms}/user_form.go | 108 +- .../{auth => forms}/user_form_auth_openid.go | 24 +- modules/{auth => forms}/user_form_test.go | 2 +- modules/lfs/locks.go | 4 +- modules/lfs/server.go | 13 +- .../{auth/auth.go => middlewares/binding.go} | 16 +- modules/middlewares/cookie.go | 61 ++ modules/middlewares/data.go | 10 + modules/middlewares/flash.go | 65 ++ modules/middlewares/locale.go | 8 +- modules/middlewares/redis.go | 217 ---- modules/middlewares/virtual.go | 196 ---- modules/session/redis.go | 2 +- modules/session/store.go | 12 + modules/session/virtual.go | 13 +- modules/setting/log.go | 12 - modules/setting/session.go | 2 +- modules/templates/base.go | 14 + modules/templates/dynamic.go | 25 - modules/templates/static.go | 112 --- modules/test/context_tests.go | 109 +- modules/timeutil/since_test.go | 12 +- modules/translation/translation.go | 49 +- modules/validation/binding.go | 2 +- modules/validation/binding_test.go | 11 +- modules/validation/glob_pattern_test.go | 2 +- modules/validation/refname_test.go | 2 +- modules/validation/validurl_test.go | 2 +- modules/web/route.go | 322 ++++++ modules/web/route_test.go | 169 ++++ options/locale/locale_de-DE.ini | 3 +- routers/admin/admin.go | 11 +- routers/admin/auths.go | 9 +- routers/admin/users.go | 9 +- routers/admin/users_test.go | 12 +- routers/api/v1/admin/org.go | 5 +- routers/api/v1/admin/repo.go | 7 +- routers/api/v1/admin/user.go | 15 +- routers/api/v1/api.go | 245 +++-- routers/api/v1/misc/markdown.go | 8 +- routers/api/v1/misc/markdown_test.go | 34 +- routers/api/v1/org/hook.go | 14 +- routers/api/v1/org/label.go | 9 +- routers/api/v1/org/org.go | 9 +- routers/api/v1/org/team.go | 9 +- routers/api/v1/repo/branch.go | 11 +- routers/api/v1/repo/collaborators.go | 5 +- routers/api/v1/repo/commits.go | 6 +- routers/api/v1/repo/file.go | 11 +- routers/api/v1/repo/fork.go | 4 +- routers/api/v1/repo/git_hook.go | 4 +- routers/api/v1/repo/hook.go | 14 +- routers/api/v1/repo/issue.go | 12 +- routers/api/v1/repo/issue_comment.go | 15 +- routers/api/v1/repo/issue_label.go | 12 +- routers/api/v1/repo/issue_reaction.go | 25 +- routers/api/v1/repo/issue_tracked_time.go | 5 +- routers/api/v1/repo/key.go | 4 +- routers/api/v1/repo/label.go | 7 +- routers/api/v1/repo/migrate.go | 7 +- routers/api/v1/repo/milestone.go | 8 +- routers/api/v1/repo/pull.go | 14 +- routers/api/v1/repo/pull_review.go | 18 +- routers/api/v1/repo/release.go | 8 +- routers/api/v1/repo/release_attachment.go | 7 +- routers/api/v1/repo/repo.go | 21 +- routers/api/v1/repo/repo_test.go | 9 +- routers/api/v1/repo/status.go | 4 +- routers/api/v1/repo/topic.go | 4 +- routers/api/v1/repo/transfer.go | 5 +- routers/api/v1/swagger/options.go | 2 +- routers/api/v1/user/app.go | 14 +- routers/api/v1/user/email.go | 9 +- routers/api/v1/user/gpg_key.go | 6 +- routers/api/v1/user/key.go | 6 +- routers/init.go | 10 +- routers/install.go | 54 +- routers/org/org.go | 6 +- routers/org/org_labels.go | 12 +- routers/org/setting.go | 9 +- routers/org/teams.go | 9 +- routers/private/hook.go | 12 +- routers/private/internal.go | 82 +- routers/private/key.go | 7 +- routers/private/mail.go | 8 +- routers/private/manager.go | 20 +- routers/private/manager_unix.go | 7 +- routers/private/manager_windows.go | 7 +- routers/private/serv.go | 7 +- routers/repo/branch.go | 6 +- routers/repo/editor.go | 25 +- routers/repo/http.go | 178 ++-- routers/repo/issue.go | 17 +- routers/repo/issue_label.go | 12 +- routers/repo/issue_label_test.go | 12 +- routers/repo/issue_lock.go | 7 +- routers/repo/issue_timetrack.go | 6 +- routers/repo/migrate.go | 10 +- routers/repo/milestone.go | 9 +- routers/repo/projects.go | 17 +- routers/repo/pull.go | 17 +- routers/repo/pull_review.go | 9 +- routers/repo/release.go | 9 +- routers/repo/release_test.go | 6 +- routers/repo/repo.go | 6 +- routers/repo/setting.go | 14 +- routers/repo/setting_protected_branch.go | 6 +- routers/repo/settings_test.go | 9 +- routers/repo/webhook.go | 59 +- routers/repo/wiki.go | 9 +- routers/repo/wiki_test.go | 12 +- routers/routes/{chi.go => base.go} | 203 ++-- routers/routes/{recovery.go => install.go} | 101 +- routers/routes/{macaron.go => web.go} | 569 ++++++----- routers/swagger_json.go | 8 +- routers/user/auth.go | 44 +- routers/user/auth_openid.go | 21 +- routers/user/oauth.go | 16 +- routers/user/setting/account.go | 13 +- routers/user/setting/account_test.go | 6 +- routers/user/setting/applications.go | 6 +- routers/user/setting/keys.go | 6 +- routers/user/setting/oauth2.go | 9 +- routers/user/setting/profile.go | 11 +- routers/user/setting/security_openid.go | 10 +- routers/user/setting/security_twofa.go | 6 +- routers/user/setting/security_u2f.go | 14 +- templates/admin/config.tmpl | 17 - templates/repo/sub_menu.tmpl | 2 +- templates/swagger/v1_json.tmpl | 4 +- .../{macaron => go-chi}/binding/.drone.yml | 0 .../{macaron => go-chi}/binding/.gitignore | 0 .../{macaron => go-chi}/binding/LICENSE | 0 vendor/gitea.com/go-chi/binding/README.md | 5 + .../{macaron => go-chi}/binding/binding.go | 250 ++--- .../{macaron => go-chi}/binding/errors.go | 0 .../{macaron => go-chi}/binding/go.mod | 6 +- .../toolbox => go-chi/binding}/go.sum | 12 +- .../{macaron => go-chi}/cache/.drone.yml | 0 .../{macaron => go-chi}/cache/.gitignore | 0 .../{macaron => go-chi}/cache/LICENSE | 0 .../{macaron => go-chi}/cache/README.md | 0 .../{macaron => go-chi}/cache/cache.go | 42 +- .../{macaron => go-chi}/cache/file.go | 3 +- .../{macaron => go-chi}/cache/go.mod | 3 +- .../{macaron => go-chi}/cache/go.sum | 7 - .../cache/memcache/memcache.go | 2 +- .../cache/memcache/memcache.goconvey | 0 .../{macaron => go-chi}/cache/memory.go | 0 .../{macaron => go-chi}/cache/utils.go | 0 .../{macaron => go-chi}/captcha/.drone.yml | 0 .../{macaron => go-chi}/captcha/LICENSE | 0 .../{macaron => go-chi}/captcha/README.md | 0 .../{macaron => go-chi}/captcha/captcha.go | 89 +- .../toolbox => go-chi/captcha}/go.mod | 5 +- .../{macaron => go-chi}/captcha/go.sum | 17 +- .../{macaron => go-chi}/captcha/image.go | 0 .../{macaron => go-chi}/captcha/siprng.go | 0 vendor/gitea.com/lunny/log/.gitignore | 26 - vendor/gitea.com/lunny/log/LICENSE | 27 - vendor/gitea.com/lunny/log/README.md | 51 - vendor/gitea.com/lunny/log/README_CN.md | 54 - vendor/gitea.com/lunny/log/dbwriter.go | 36 - vendor/gitea.com/lunny/log/filewriter.go | 112 --- vendor/gitea.com/lunny/log/go.mod | 5 - vendor/gitea.com/lunny/log/go.sum | 2 - vendor/gitea.com/lunny/log/logext.go | 595 ----------- vendor/gitea.com/lunny/nodb/.gitignore | 7 - vendor/gitea.com/lunny/nodb/LICENSE | 21 - vendor/gitea.com/lunny/nodb/README.md | 83 -- vendor/gitea.com/lunny/nodb/README_CN.md | 80 -- vendor/gitea.com/lunny/nodb/batch.go | 106 -- vendor/gitea.com/lunny/nodb/binlog.go | 391 -------- vendor/gitea.com/lunny/nodb/binlog_util.go | 215 ---- vendor/gitea.com/lunny/nodb/config/config.go | 135 --- .../gitea.com/lunny/nodb/config/config.toml | 45 - vendor/gitea.com/lunny/nodb/const.go | 98 -- vendor/gitea.com/lunny/nodb/doc.go | 61 -- vendor/gitea.com/lunny/nodb/dump.go | 200 ---- vendor/gitea.com/lunny/nodb/go.mod | 11 - vendor/gitea.com/lunny/nodb/go.sum | 42 - vendor/gitea.com/lunny/nodb/info.go | 24 - vendor/gitea.com/lunny/nodb/multi.go | 73 -- vendor/gitea.com/lunny/nodb/nodb.go | 128 --- vendor/gitea.com/lunny/nodb/nodb_db.go | 171 ---- vendor/gitea.com/lunny/nodb/replication.go | 312 ------ vendor/gitea.com/lunny/nodb/scan.go | 144 --- vendor/gitea.com/lunny/nodb/store/db.go | 61 -- .../lunny/nodb/store/driver/batch.go | 39 - .../lunny/nodb/store/driver/driver.go | 67 -- .../lunny/nodb/store/driver/store.go | 46 - .../lunny/nodb/store/goleveldb/batch.go | 27 - .../lunny/nodb/store/goleveldb/const.go | 4 - .../lunny/nodb/store/goleveldb/db.go | 187 ---- .../lunny/nodb/store/goleveldb/iterator.go | 49 - .../lunny/nodb/store/goleveldb/snapshot.go | 26 - vendor/gitea.com/lunny/nodb/store/iterator.go | 327 ------ vendor/gitea.com/lunny/nodb/store/snapshot.go | 16 - vendor/gitea.com/lunny/nodb/store/store.go | 51 - vendor/gitea.com/lunny/nodb/store/tx.go | 42 - .../gitea.com/lunny/nodb/store/writebatch.go | 9 - vendor/gitea.com/lunny/nodb/t_bit.go | 922 ----------------- vendor/gitea.com/lunny/nodb/t_hash.go | 509 ---------- vendor/gitea.com/lunny/nodb/t_kv.go | 387 ------- vendor/gitea.com/lunny/nodb/t_list.go | 492 --------- vendor/gitea.com/lunny/nodb/t_set.go | 601 ----------- vendor/gitea.com/lunny/nodb/t_ttl.go | 195 ---- vendor/gitea.com/lunny/nodb/t_zset.go | 943 ------------------ vendor/gitea.com/lunny/nodb/tx.go | 113 --- vendor/gitea.com/lunny/nodb/util.go | 113 --- vendor/gitea.com/macaron/binding/README.md | 20 - vendor/gitea.com/macaron/binding/go.sum | 30 - vendor/gitea.com/macaron/captcha/go.mod | 10 - vendor/gitea.com/macaron/cors/.drone.yml | 9 - vendor/gitea.com/macaron/cors/.gitignore | 12 - vendor/gitea.com/macaron/cors/README.md | 5 - vendor/gitea.com/macaron/cors/cors.go | 169 ---- vendor/gitea.com/macaron/cors/go.mod | 5 - vendor/gitea.com/macaron/cors/go.sum | 31 - vendor/gitea.com/macaron/csrf/.drone.yml | 24 - vendor/gitea.com/macaron/csrf/LICENSE | 191 ---- vendor/gitea.com/macaron/csrf/README.md | 18 - vendor/gitea.com/macaron/csrf/go.mod | 12 - vendor/gitea.com/macaron/csrf/go.sum | 83 -- vendor/gitea.com/macaron/gzip/.drone.yml | 24 - vendor/gitea.com/macaron/gzip/README.md | 19 - vendor/gitea.com/macaron/gzip/go.mod | 11 - vendor/gitea.com/macaron/gzip/go.sum | 48 - vendor/gitea.com/macaron/gzip/gzip.go | 368 ------- vendor/gitea.com/macaron/i18n/.drone.yml | 24 - vendor/gitea.com/macaron/i18n/.gitignore | 1 - vendor/gitea.com/macaron/i18n/LICENSE | 191 ---- vendor/gitea.com/macaron/i18n/README.md | 16 - vendor/gitea.com/macaron/i18n/go.mod | 10 - vendor/gitea.com/macaron/i18n/go.sum | 52 - vendor/gitea.com/macaron/i18n/i18n.go | 242 ----- vendor/gitea.com/macaron/inject/.drone.yml | 20 - vendor/gitea.com/macaron/inject/LICENSE | 191 ---- vendor/gitea.com/macaron/inject/README.md | 11 - vendor/gitea.com/macaron/inject/go.mod | 5 - vendor/gitea.com/macaron/inject/go.sum | 13 - vendor/gitea.com/macaron/inject/inject.go | 262 ----- vendor/gitea.com/macaron/macaron/.drone.yml | 12 - vendor/gitea.com/macaron/macaron/.gitignore | 3 - vendor/gitea.com/macaron/macaron/LICENSE | 191 ---- vendor/gitea.com/macaron/macaron/README.md | 97 -- vendor/gitea.com/macaron/macaron/context.go | 563 ----------- vendor/gitea.com/macaron/macaron/go.mod | 12 - vendor/gitea.com/macaron/macaron/go.sum | 31 - vendor/gitea.com/macaron/macaron/logger.go | 73 -- vendor/gitea.com/macaron/macaron/macaron.go | 327 ------ .../gitea.com/macaron/macaron/macaronlogo.png | Bin 88924 -> 0 bytes vendor/gitea.com/macaron/macaron/recovery.go | 163 --- vendor/gitea.com/macaron/macaron/render.go | 724 -------------- .../macaron/macaron/response_writer.go | 124 --- .../macaron/macaron/return_handler.go | 76 -- vendor/gitea.com/macaron/macaron/router.go | 380 ------- vendor/gitea.com/macaron/macaron/static.go | 231 ----- vendor/gitea.com/macaron/macaron/tree.go | 390 -------- vendor/gitea.com/macaron/macaron/util_go17.go | 25 - vendor/gitea.com/macaron/macaron/util_go18.go | 24 - vendor/gitea.com/macaron/session/.drone.yml | 11 - vendor/gitea.com/macaron/session/.gitignore | 4 - vendor/gitea.com/macaron/session/LICENSE | 191 ---- vendor/gitea.com/macaron/session/README.md | 22 - .../macaron/session/couchbase/couchbase.go | 227 ----- vendor/gitea.com/macaron/session/file.go | 274 ----- vendor/gitea.com/macaron/session/flash.go | 61 -- vendor/gitea.com/macaron/session/go.mod | 30 - vendor/gitea.com/macaron/session/go.sum | 118 --- .../macaron/session/memcache/memcache.go | 203 ---- .../session/memcache/memcache.goconvey | 1 - vendor/gitea.com/macaron/session/memory.go | 223 ----- .../gitea.com/macaron/session/mysql/mysql.go | 201 ---- .../macaron/session/mysql/mysql.goconvey | 1 - vendor/gitea.com/macaron/session/nodb/nodb.go | 207 ---- .../macaron/session/nodb/nodb.goconvey | 1 - .../macaron/session/postgres/postgres.go | 202 ---- .../session/postgres/postgres.goconvey | 1 - vendor/gitea.com/macaron/session/session.go | 393 -------- vendor/gitea.com/macaron/session/utils.go | 63 -- vendor/gitea.com/macaron/toolbox/.drone.yml | 9 - vendor/gitea.com/macaron/toolbox/.gitignore | 2 - vendor/gitea.com/macaron/toolbox/LICENSE | 191 ---- vendor/gitea.com/macaron/toolbox/README.md | 112 --- .../gitea.com/macaron/toolbox/healthcheck.go | 83 -- vendor/gitea.com/macaron/toolbox/profile.go | 163 --- vendor/gitea.com/macaron/toolbox/statistic.go | 138 --- vendor/gitea.com/macaron/toolbox/toolbox.go | 158 --- .../github.com/NYTimes/gziphandler/.gitignore | 1 + .../NYTimes/gziphandler/.travis.yml | 10 + .../NYTimes/gziphandler/CODE_OF_CONDUCT.md | 75 ++ .../NYTimes/gziphandler/CONTRIBUTING.md | 30 + .../NYTimes/gziphandler}/LICENSE | 2 +- .../github.com/NYTimes/gziphandler/README.md | 56 ++ vendor/github.com/NYTimes/gziphandler/go.mod | 5 + vendor/github.com/NYTimes/gziphandler/go.sum | 7 + vendor/github.com/NYTimes/gziphandler/gzip.go | 532 ++++++++++ .../NYTimes/gziphandler/gzip_go18.go | 43 + vendor/github.com/go-chi/cors/LICENSE | 21 + vendor/github.com/go-chi/cors/README.md | 39 + vendor/github.com/go-chi/cors/cors.go | 400 ++++++++ vendor/github.com/go-chi/cors/utils.go | 70 ++ .../github.com/siddontang/go-snappy/AUTHORS | 12 - .../siddontang/go-snappy/CONTRIBUTORS | 34 - .../github.com/siddontang/go-snappy/LICENSE | 27 - .../siddontang/go-snappy/snappy/decode.go | 124 --- .../siddontang/go-snappy/snappy/encode.go | 174 ---- .../siddontang/go-snappy/snappy/snappy.go | 38 - vendor/modules.txt | 65 +- 353 files changed, 5412 insertions(+), 20734 deletions(-) create mode 100644 modules/context/captcha.go rename {vendor/gitea.com/macaron/csrf => modules/context}/csrf.go (69%) create mode 100644 modules/context/form.go create mode 100644 modules/context/private.go create mode 100644 modules/context/secret.go rename {vendor/gitea.com/macaron/csrf => modules/context}/xsrf.go (90%) create mode 100644 modules/context/xsrf_test.go rename modules/{auth => forms}/admin.go (70%) rename modules/{auth => forms}/auth_form.go (87%) rename modules/{auth => forms}/org.go (76%) rename modules/{auth => forms}/repo_branch_form.go (52%) rename modules/{auth => forms}/repo_form.go (78%) rename modules/{auth => forms}/repo_form_test.go (99%) rename modules/{auth => forms}/user_form.go (71%) rename modules/{auth => forms}/user_form_auth_openid.go (58%) rename modules/{auth => forms}/user_form_test.go (98%) rename modules/{auth/auth.go => middlewares/binding.go} (92%) create mode 100644 modules/middlewares/data.go create mode 100644 modules/middlewares/flash.go delete mode 100644 modules/middlewares/redis.go delete mode 100644 modules/middlewares/virtual.go create mode 100644 modules/session/store.go create mode 100644 modules/web/route.go create mode 100644 modules/web/route_test.go rename routers/routes/{chi.go => base.go} (53%) rename routers/routes/{recovery.go => install.go} (50%) rename routers/routes/{macaron.go => web.go} (70%) rename vendor/gitea.com/{macaron => go-chi}/binding/.drone.yml (100%) rename vendor/gitea.com/{macaron => go-chi}/binding/.gitignore (100%) rename vendor/gitea.com/{macaron => go-chi}/binding/LICENSE (100%) create mode 100644 vendor/gitea.com/go-chi/binding/README.md rename vendor/gitea.com/{macaron => go-chi}/binding/binding.go (80%) rename vendor/gitea.com/{macaron => go-chi}/binding/errors.go (100%) rename vendor/gitea.com/{macaron => go-chi}/binding/go.mod (57%) rename vendor/gitea.com/{macaron/toolbox => go-chi/binding}/go.sum (68%) rename vendor/gitea.com/{macaron => go-chi}/cache/.drone.yml (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/.gitignore (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/LICENSE (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/README.md (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/cache.go (74%) rename vendor/gitea.com/{macaron => go-chi}/cache/file.go (98%) rename vendor/gitea.com/{macaron => go-chi}/cache/go.mod (93%) rename vendor/gitea.com/{macaron => go-chi}/cache/go.sum (93%) rename vendor/gitea.com/{macaron => go-chi}/cache/memcache/memcache.go (98%) rename vendor/gitea.com/{macaron => go-chi}/cache/memcache/memcache.goconvey (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/memory.go (100%) rename vendor/gitea.com/{macaron => go-chi}/cache/utils.go (100%) rename vendor/gitea.com/{macaron => go-chi}/captcha/.drone.yml (100%) rename vendor/gitea.com/{macaron => go-chi}/captcha/LICENSE (100%) rename vendor/gitea.com/{macaron => go-chi}/captcha/README.md (100%) rename vendor/gitea.com/{macaron => go-chi}/captcha/captcha.go (77%) rename vendor/gitea.com/{macaron/toolbox => go-chi/captcha}/go.mod (55%) rename vendor/gitea.com/{macaron => go-chi}/captcha/go.sum (85%) rename vendor/gitea.com/{macaron => go-chi}/captcha/image.go (100%) rename vendor/gitea.com/{macaron => go-chi}/captcha/siprng.go (100%) delete mode 100644 vendor/gitea.com/lunny/log/.gitignore delete mode 100644 vendor/gitea.com/lunny/log/LICENSE delete mode 100644 vendor/gitea.com/lunny/log/README.md delete mode 100644 vendor/gitea.com/lunny/log/README_CN.md delete mode 100644 vendor/gitea.com/lunny/log/dbwriter.go delete mode 100644 vendor/gitea.com/lunny/log/filewriter.go delete mode 100644 vendor/gitea.com/lunny/log/go.mod delete mode 100644 vendor/gitea.com/lunny/log/go.sum delete mode 100644 vendor/gitea.com/lunny/log/logext.go delete mode 100644 vendor/gitea.com/lunny/nodb/.gitignore delete mode 100644 vendor/gitea.com/lunny/nodb/LICENSE delete mode 100644 vendor/gitea.com/lunny/nodb/README.md delete mode 100644 vendor/gitea.com/lunny/nodb/README_CN.md delete mode 100644 vendor/gitea.com/lunny/nodb/batch.go delete mode 100644 vendor/gitea.com/lunny/nodb/binlog.go delete mode 100644 vendor/gitea.com/lunny/nodb/binlog_util.go delete mode 100644 vendor/gitea.com/lunny/nodb/config/config.go delete mode 100644 vendor/gitea.com/lunny/nodb/config/config.toml delete mode 100644 vendor/gitea.com/lunny/nodb/const.go delete mode 100644 vendor/gitea.com/lunny/nodb/doc.go delete mode 100644 vendor/gitea.com/lunny/nodb/dump.go delete mode 100644 vendor/gitea.com/lunny/nodb/go.mod delete mode 100644 vendor/gitea.com/lunny/nodb/go.sum delete mode 100644 vendor/gitea.com/lunny/nodb/info.go delete mode 100644 vendor/gitea.com/lunny/nodb/multi.go delete mode 100644 vendor/gitea.com/lunny/nodb/nodb.go delete mode 100644 vendor/gitea.com/lunny/nodb/nodb_db.go delete mode 100644 vendor/gitea.com/lunny/nodb/replication.go delete mode 100644 vendor/gitea.com/lunny/nodb/scan.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/db.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/driver/batch.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/driver/driver.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/driver/store.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/goleveldb/batch.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/goleveldb/const.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/goleveldb/db.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/goleveldb/iterator.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/goleveldb/snapshot.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/iterator.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/snapshot.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/store.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/tx.go delete mode 100644 vendor/gitea.com/lunny/nodb/store/writebatch.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_bit.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_hash.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_kv.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_list.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_set.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_ttl.go delete mode 100644 vendor/gitea.com/lunny/nodb/t_zset.go delete mode 100644 vendor/gitea.com/lunny/nodb/tx.go delete mode 100644 vendor/gitea.com/lunny/nodb/util.go delete mode 100644 vendor/gitea.com/macaron/binding/README.md delete mode 100644 vendor/gitea.com/macaron/binding/go.sum delete mode 100644 vendor/gitea.com/macaron/captcha/go.mod delete mode 100644 vendor/gitea.com/macaron/cors/.drone.yml delete mode 100644 vendor/gitea.com/macaron/cors/.gitignore delete mode 100644 vendor/gitea.com/macaron/cors/README.md delete mode 100644 vendor/gitea.com/macaron/cors/cors.go delete mode 100644 vendor/gitea.com/macaron/cors/go.mod delete mode 100644 vendor/gitea.com/macaron/cors/go.sum delete mode 100644 vendor/gitea.com/macaron/csrf/.drone.yml delete mode 100644 vendor/gitea.com/macaron/csrf/LICENSE delete mode 100644 vendor/gitea.com/macaron/csrf/README.md delete mode 100644 vendor/gitea.com/macaron/csrf/go.mod delete mode 100644 vendor/gitea.com/macaron/csrf/go.sum delete mode 100644 vendor/gitea.com/macaron/gzip/.drone.yml delete mode 100644 vendor/gitea.com/macaron/gzip/README.md delete mode 100644 vendor/gitea.com/macaron/gzip/go.mod delete mode 100644 vendor/gitea.com/macaron/gzip/go.sum delete mode 100644 vendor/gitea.com/macaron/gzip/gzip.go delete mode 100644 vendor/gitea.com/macaron/i18n/.drone.yml delete mode 100644 vendor/gitea.com/macaron/i18n/.gitignore delete mode 100644 vendor/gitea.com/macaron/i18n/LICENSE delete mode 100644 vendor/gitea.com/macaron/i18n/README.md delete mode 100644 vendor/gitea.com/macaron/i18n/go.mod delete mode 100644 vendor/gitea.com/macaron/i18n/go.sum delete mode 100644 vendor/gitea.com/macaron/i18n/i18n.go delete mode 100644 vendor/gitea.com/macaron/inject/.drone.yml delete mode 100644 vendor/gitea.com/macaron/inject/LICENSE delete mode 100644 vendor/gitea.com/macaron/inject/README.md delete mode 100644 vendor/gitea.com/macaron/inject/go.mod delete mode 100644 vendor/gitea.com/macaron/inject/go.sum delete mode 100644 vendor/gitea.com/macaron/inject/inject.go delete mode 100644 vendor/gitea.com/macaron/macaron/.drone.yml delete mode 100644 vendor/gitea.com/macaron/macaron/.gitignore delete mode 100644 vendor/gitea.com/macaron/macaron/LICENSE delete mode 100644 vendor/gitea.com/macaron/macaron/README.md delete mode 100644 vendor/gitea.com/macaron/macaron/context.go delete mode 100644 vendor/gitea.com/macaron/macaron/go.mod delete mode 100644 vendor/gitea.com/macaron/macaron/go.sum delete mode 100644 vendor/gitea.com/macaron/macaron/logger.go delete mode 100644 vendor/gitea.com/macaron/macaron/macaron.go delete mode 100644 vendor/gitea.com/macaron/macaron/macaronlogo.png delete mode 100644 vendor/gitea.com/macaron/macaron/recovery.go delete mode 100644 vendor/gitea.com/macaron/macaron/render.go delete mode 100644 vendor/gitea.com/macaron/macaron/response_writer.go delete mode 100644 vendor/gitea.com/macaron/macaron/return_handler.go delete mode 100644 vendor/gitea.com/macaron/macaron/router.go delete mode 100644 vendor/gitea.com/macaron/macaron/static.go delete mode 100644 vendor/gitea.com/macaron/macaron/tree.go delete mode 100644 vendor/gitea.com/macaron/macaron/util_go17.go delete mode 100644 vendor/gitea.com/macaron/macaron/util_go18.go delete mode 100644 vendor/gitea.com/macaron/session/.drone.yml delete mode 100644 vendor/gitea.com/macaron/session/.gitignore delete mode 100644 vendor/gitea.com/macaron/session/LICENSE delete mode 100644 vendor/gitea.com/macaron/session/README.md delete mode 100644 vendor/gitea.com/macaron/session/couchbase/couchbase.go delete mode 100644 vendor/gitea.com/macaron/session/file.go delete mode 100644 vendor/gitea.com/macaron/session/flash.go delete mode 100644 vendor/gitea.com/macaron/session/go.mod delete mode 100644 vendor/gitea.com/macaron/session/go.sum delete mode 100644 vendor/gitea.com/macaron/session/memcache/memcache.go delete mode 100644 vendor/gitea.com/macaron/session/memcache/memcache.goconvey delete mode 100644 vendor/gitea.com/macaron/session/memory.go delete mode 100644 vendor/gitea.com/macaron/session/mysql/mysql.go delete mode 100644 vendor/gitea.com/macaron/session/mysql/mysql.goconvey delete mode 100644 vendor/gitea.com/macaron/session/nodb/nodb.go delete mode 100644 vendor/gitea.com/macaron/session/nodb/nodb.goconvey delete mode 100644 vendor/gitea.com/macaron/session/postgres/postgres.go delete mode 100644 vendor/gitea.com/macaron/session/postgres/postgres.goconvey delete mode 100644 vendor/gitea.com/macaron/session/session.go delete mode 100644 vendor/gitea.com/macaron/session/utils.go delete mode 100644 vendor/gitea.com/macaron/toolbox/.drone.yml delete mode 100644 vendor/gitea.com/macaron/toolbox/.gitignore delete mode 100644 vendor/gitea.com/macaron/toolbox/LICENSE delete mode 100644 vendor/gitea.com/macaron/toolbox/README.md delete mode 100644 vendor/gitea.com/macaron/toolbox/healthcheck.go delete mode 100644 vendor/gitea.com/macaron/toolbox/profile.go delete mode 100644 vendor/gitea.com/macaron/toolbox/statistic.go delete mode 100644 vendor/gitea.com/macaron/toolbox/toolbox.go create mode 100644 vendor/github.com/NYTimes/gziphandler/.gitignore create mode 100644 vendor/github.com/NYTimes/gziphandler/.travis.yml create mode 100644 vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md rename vendor/{gitea.com/macaron/cors => github.com/NYTimes/gziphandler}/LICENSE (99%) create mode 100644 vendor/github.com/NYTimes/gziphandler/README.md create mode 100644 vendor/github.com/NYTimes/gziphandler/go.mod create mode 100644 vendor/github.com/NYTimes/gziphandler/go.sum create mode 100644 vendor/github.com/NYTimes/gziphandler/gzip.go create mode 100644 vendor/github.com/NYTimes/gziphandler/gzip_go18.go create mode 100644 vendor/github.com/go-chi/cors/LICENSE create mode 100644 vendor/github.com/go-chi/cors/README.md create mode 100644 vendor/github.com/go-chi/cors/cors.go create mode 100644 vendor/github.com/go-chi/cors/utils.go delete mode 100644 vendor/github.com/siddontang/go-snappy/AUTHORS delete mode 100644 vendor/github.com/siddontang/go-snappy/CONTRIBUTORS delete mode 100644 vendor/github.com/siddontang/go-snappy/LICENSE delete mode 100644 vendor/github.com/siddontang/go-snappy/snappy/decode.go delete mode 100644 vendor/github.com/siddontang/go-snappy/snappy/encode.go delete mode 100644 vendor/github.com/siddontang/go-snappy/snappy/snappy.go diff --git a/.golangci.yml b/.golangci.yml index 34752127e..2d098f6fc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -70,7 +70,7 @@ issues: - path: modules/log/ linters: - errcheck - - path: routers/routes/macaron.go + - path: routers/routes/web.go linters: - dupl - path: routers/api/v1/repo/issue_subscription.go diff --git a/cmd/dump.go b/cmd/dump.go index 1a2e62576..65e2c817f 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -21,7 +21,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" - "gitea.com/macaron/session" + "gitea.com/go-chi/session" archiver "github.com/mholt/archiver/v3" "github.com/urfave/cli" ) diff --git a/cmd/web.go b/cmd/web.go index 2e8c45a76..79915ad19 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -102,8 +102,7 @@ func runWeb(ctx *cli.Context) error { return err } } - c := routes.NewChi() - routes.RegisterInstallRoute(c) + c := routes.InstallRoutes() err := listen(c, false) select { case <-graceful.GetManager().IsShutdown(): @@ -134,11 +133,9 @@ func runWeb(ctx *cli.Context) error { return err } } - // Set up Chi routes - c := routes.NewChi() - c.Mount("/", routes.NormalRoutes()) - routes.DelegateToMacaron(c) + // Set up Chi routes + c := routes.NormalRoutes() err := listen(c, true) <-graceful.GetManager().Done() log.Info("PID: %d Gitea Web Finished", os.Getpid()) diff --git a/contrib/pr/checkout.go b/contrib/pr/checkout.go index 9346577bd..63eca484a 100644 --- a/contrib/pr/checkout.go +++ b/contrib/pr/checkout.go @@ -116,9 +116,7 @@ func runPR() { //routers.GlobalInit() external.RegisterParsers() markup.Init() - c := routes.NewChi() - c.Mount("/", routes.NormalRoutes()) - routes.DelegateToMacaron(c) + c := routes.NormalRoutes() log.Printf("[PR] Ready for testing !\n") log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 5d2670151..e8866ab33 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -549,7 +549,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Session (`session`) -- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, mysql, couchbase, memcache, nodb, postgres\]. +- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, mysql, couchbase, memcache, postgres\]. - `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for others, the connection string. - `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access. - `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID. @@ -609,8 +609,6 @@ Default templates for project boards: - `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. You can configure each mode in per mode log subsections `\[log.modename\]`. By default the file mode will log to `$ROOT_PATH/gitea.log`. - `LEVEL`: **Info**: General log level. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\] - `STACKTRACE_LEVEL`: **None**: Default log level at which to log create stack traces. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\] -- `REDIRECT_MACARON_LOG`: **false**: Redirects the Macaron log to its own logger or the default logger. -- `MACARON`: **file**: Logging mode for the macaron logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.macaron\]`. By default the file mode will log to `$ROOT_PATH/macaron.log`. (If you set this to `,` it will log to default gitea logger.) - `ROUTER_LOG_LEVEL`: **Info**: The log level that the router should log at. (If you are setting the access log, its recommended to place this at Debug.) - `ROUTER`: **console**: The mode or name of the log the router should log to. (If you set this to `,` it will log to default gitea logger.) NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` for this option to take effect. Configure each mode in per mode log subsections `\[log.modename.router\]`. @@ -618,7 +616,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` - `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default gitea logger.) - `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log. - The following variables are available: - - `Ctx`: the `macaron.Context` of the request. + - `Ctx`: the `context.Context` of the request. - `Identity`: the SignedUserName or `"-"` if not logged in. - `Start`: the start time of the request. - `ResponseWriter`: the responseWriter from the request. diff --git a/docs/content/doc/advanced/logging-documentation.en-us.md b/docs/content/doc/advanced/logging-documentation.en-us.md index bf9fc4888..195820329 100644 --- a/docs/content/doc/advanced/logging-documentation.en-us.md +++ b/docs/content/doc/advanced/logging-documentation.en-us.md @@ -67,40 +67,11 @@ The provider type of the sublogger can be set using the `MODE` value in its subsection, but will default to the name. This allows you to have multiple subloggers that will log to files. -### The "Macaron" logger - -By default Macaron will log to its own go `log` instance. This writes -to `os.Stdout`. You can redirect this log to a Gitea configurable logger -through setting the `REDIRECT_MACARON_LOG` setting in the `[log]` -section which you can configure the outputs of by setting the `MACARON` -value in the `[log]` section of the configuration. `MACARON` defaults -to `file` if unset. - -Please note, the macaron logger will log at `INFO` level, setting the -`LEVEL` of this logger to `WARN` or above will result in no macaron logs. - -Each output sublogger for this logger is configured in -`[log.sublogger.macaron]` sections. There are certain default values -which will not be inherited from the `[log]` or relevant -`[log.sublogger]` sections: - -- `FLAGS` is `stdflags` (Equal to - `date,time,medfile,shortfuncname,levelinitial`) -- `FILE_NAME` will default to `%(ROOT_PATH)/macaron.log` -- `EXPRESSION` will default to `""` -- `PREFIX` will default to `""` - -NB: You can redirect the macaron logger to send its events to the gitea -log using the value: `MACARON = ,` - ### The "Router" logger -There are two types of Router log. By default Macaron send its own -router log which will be directed to Macaron's go `log`, however if you -`REDIRECT_MACARON_LOG` you will enable Gitea's router log. You can -disable both types of Router log by setting `DISABLE_ROUTER_LOG`. +You can disable Router log by setting `DISABLE_ROUTER_LOG`. -If you enable the redirect, you can configure the outputs of this +You can configure the outputs of this router log by setting the `ROUTER` value in the `[log]` section of the configuration. `ROUTER` will default to `console` if unset. The Gitea Router logs the same data as the Macaron log but has slightly different @@ -162,11 +133,11 @@ This value represent a go template. It's default value is: The template is passed following options: -- `Ctx` is the `macaron.Context` +- `Ctx` is the `context.Context` - `Identity` is the `SignedUserName` or `"-"` if the user is not logged in - `Start` is the start time of the request -- `ResponseWriter` is the `macaron.ResponseWriter` +- `ResponseWriter` is the `http.ResponseWriter` Caution must be taken when changing this template as it runs outside of the standard panic recovery trap. The template should also be as simple diff --git a/docs/content/page/index.en-us.md b/docs/content/page/index.en-us.md index d190b20ae..6bf163ecb 100644 --- a/docs/content/page/index.en-us.md +++ b/docs/content/page/index.en-us.md @@ -267,7 +267,7 @@ Windows, on architectures like amd64, i386, ARM, PowerPC, and others. ## Components -* Web framework: [Macaron](http://go-macaron.com/) +* Web framework: [Chi](http://github.com/go-chi/chi) * ORM: [XORM](https://xorm.io) * UI components: * [Semantic UI](http://semantic-ui.com/) diff --git a/docs/content/page/index.fr-fr.md b/docs/content/page/index.fr-fr.md index 4bbf08c4d..17e22e8b5 100755 --- a/docs/content/page/index.fr-fr.md +++ b/docs/content/page/index.fr-fr.md @@ -254,7 +254,7 @@ Le but de ce projet est de fournir de la manière la plus simple, la plus rapide ## Composants -* Framework web : [Macaron](http://go-macaron.com/) +* Framework web : [Chi](http://github.com/go-chi/chi) * ORM: [XORM](https://xorm.io) * Interface graphique : * [Semantic UI](http://semantic-ui.com/) diff --git a/docs/content/page/index.zh-cn.md b/docs/content/page/index.zh-cn.md index 453715a84..cb6a1da79 100644 --- a/docs/content/page/index.zh-cn.md +++ b/docs/content/page/index.zh-cn.md @@ -47,7 +47,7 @@ Gitea的首要目标是创建一个极易安装,运行非常快速,安装和 ## 组件 -* Web框架: [Macaron](http://go-macaron.com/) +* Web框架: [Chi](http://github.com/go-chi/chi) * ORM: [XORM](https://xorm.io) * UI组件: * [Semantic UI](http://semantic-ui.com/) diff --git a/docs/content/page/index.zh-tw.md b/docs/content/page/index.zh-tw.md index 4c7979f41..86c182a7d 100644 --- a/docs/content/page/index.zh-tw.md +++ b/docs/content/page/index.zh-tw.md @@ -47,7 +47,7 @@ Gitea 的首要目標是建立一個容易安裝,運行快速,安装和使 ## 元件 -* Web 框架: [Macaron](http://go-macaron.com/) +* Web 框架: [Chi](http://github.com/go-chi/chi) * ORM: [XORM](https://xorm.io) * UI 元件: * [Semantic UI](http://semantic-ui.com/) diff --git a/go.mod b/go.mod index 5d7fba0b7..ad10dadd2 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,12 @@ go 1.14 require ( code.gitea.io/gitea-vet v0.2.1 code.gitea.io/sdk/gitea v0.13.1 + gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c + gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e + gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee gitea.com/lunny/levelqueue v0.3.0 - gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b - gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b - gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca - gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4 - gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439 - gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5 - gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60 - gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a - gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804 - gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee - gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 + github.com/NYTimes/gziphandler v1.1.1 github.com/PuerkitoBio/goquery v1.5.1 github.com/RoaringBitmap/roaring v0.5.5 // indirect github.com/alecthomas/chroma v0.8.2 @@ -36,6 +29,7 @@ require ( github.com/gliderlabs/ssh v0.3.1 github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect github.com/go-chi/chi v1.5.1 + github.com/go-chi/cors v1.1.1 github.com/go-enry/go-enry/v2 v2.6.0 github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-git/v5 v5.2.0 diff --git a/go.sum b/go.sum index 458d0ab60..79cba8d26 100644 --- a/go.sum +++ b/go.sum @@ -40,44 +40,16 @@ code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFj code.gitea.io/sdk/gitea v0.13.1 h1:Y7bpH2iO6Q0KhhMJfjP/LZ0AmiYITeRQlCD8b0oYqhk= code.gitea.io/sdk/gitea v0.13.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c h1:NTtrGYjR40WUdkCdn26Y5LGFT52rIkFPkjmtgCAyiTs= +gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c/go.mod h1:9bGA9dIsrz+wVQKH1DzvxuAvrudHaQ8Wx8hLme/GVGQ= +gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e h1:zgPGaf3kXP0cVm9J0l8ZA2+XDzILYATg0CXbihR6N+o= +gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= +gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+MlGolcXO8seYY2+EY5g7vZPB17CQ= +gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog= gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws= gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0= gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= -gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727 h1:ZF2Bd6rqVlwhIDhYiS0uGYcT+GaVNGjuKVJkTNqWMIs= -gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY= -gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ= -gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b/go.mod h1:Cxadig6POWpPYYSfg23E7jo35Yf0yvsdC1lifoKWmPo= -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/cache v0.0.0-20200924044943-905232fba10b h1:2ZE0JE3bKVBcP1VTrWeE1jqWwCAMIzfOQm1U9EGbBKU= -gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b/go.mod h1:W5hKG8T1GBfypp5CRQlgoJU4figIL0jhx02y4XA/NOA= -gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca h1:f5P41nXmXd/YOh8f6098Q0F1Y0QfpyRPSSIkni2XH4Q= -gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca/go.mod h1:J5h3N+1nKTXtU1x4GxexaQKgAz8UiWecNwi/CfX7CtQ= -gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4 h1:e2rAFDejB0qN8OrY4xP4XSu8/yT6QmWxDZpB3J7r2GU= -gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4/go.mod h1:rtOK4J20kpMD9XcNsnO5YA843YSTe/MUMbDj/TJ/Q7A= -gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439 h1:88c34YM29a1GlWLrLBaG/GTT2htDdJz1u3n9+lmPolg= -gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439/go.mod h1:IsQPHx73HnnqFBYiVHjg87q4XBZyGXXu77xANukvZuk= -gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5 h1:6rbhThlqfOb+sSmhrsVFz3bZoAeoloe7TZqyeiPbbWI= -gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5/go.mod h1:z8vCjuhqDfvzPUJDowGqbsgoeYBvDbl95S5k6y43Pxo= -gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60 h1:tNWNe5HBIlsfapFMtT4twTbXQmInRQWmdWNi8Di1ct0= -gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60/go.mod h1:g5ope1b+iWhBdHzAn6EJ9u9Gp3FRESxpG+CDf7HYc/A= -gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= -gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw= -gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= -gitea.com/macaron/macaron v1.5.0 h1:TvWEcHw1/zaHlo0GTuKEukLh3A99+QsU2mjBrXLXjVQ= -gitea.com/macaron/macaron v1.5.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= -gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804 h1:yUiJVZKzdXsBe2tumTAXHBZa1qPGoGXM3fBG4RJ5fQg= -gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= -gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= -gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee h1:8/N3a56RXRJ66nnep0z+T7oHCB0bY6lpvtjv9Y9FPhE= -gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee/go.mod h1:5tJCkDbrwpGv+MQUSIZSOW0wFrkh0exsonJgOvBs1Dw= -gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14mefLzGluqV7w2mGU3u+iZU+jCeWk= -gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/6543-forks/go-gogs-client v0.0.0-20210116182316-f2f8bc0ea9cc h1:FLylYVXDwK+YtrmXYnv2Q1Y5lQ9TU1Xp5F2vndIOTb4= @@ -92,6 +64,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= @@ -106,7 +80,6 @@ github.com/RoaringBitmap/roaring v0.5.5 h1:naNqvO1mNnghk2UvcsqnzHDBn9DRbCIRy94Gm github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= @@ -233,19 +206,13 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 h1:uNLXQ6QO1TocD8BaN/KkRki0Xw0brCM1PKl/ZA5pgfs= github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= -github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= -github.com/couchbase/gomemcached v0.1.0 h1:whUde87n8CScx8ckMp2En5liqAlcuG3aKy/BQeBPu84= -github.com/couchbase/gomemcached v0.1.0/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= github.com/couchbase/gomemcached v0.1.1 h1:xCS8ZglJDhrlQg3jmK7Rn1V8f7bPjXABLC05CgLQauc= github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= -github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4= github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw= github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= -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/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -331,6 +298,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w= github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= +github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw= +github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= github.com/go-enry/go-enry/v2 v2.6.0 h1:nbGWQBpO+D+cJuRxNgSDFnFY9QWz3QM/CeZxU7VAH20= github.com/go-enry/go-enry/v2 v2.6.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= @@ -548,7 +517,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -700,7 +668,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= @@ -995,7 +962,6 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 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/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= @@ -1068,7 +1034,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= -github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c h1:679/gJXwrsHC3RATr0YYjZvDMJPYN7W9FGSGNoLmKxM= github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs= @@ -1171,9 +1136,6 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1535,12 +1497,10 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go index b8e513958..551a9bb75 100644 --- a/integrations/api_helper_for_declarative_test.go +++ b/integrations/api_helper_for_declarative_test.go @@ -14,7 +14,7 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/queue" api "code.gitea.io/gitea/modules/structs" diff --git a/integrations/api_pull_test.go b/integrations/api_pull_test.go index 61daf917f..369f4ce31 100644 --- a/integrations/api_pull_test.go +++ b/integrations/api_pull_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" issue_service "code.gitea.io/gitea/services/issue" diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 8328b014d..870d7d0e6 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -131,7 +131,7 @@ func TestAPIGetReleaseByTag(t *testing.T) { tag := "v1.1" - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/", + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, tag) req := NewRequestf(t, "GET", urlStr) @@ -144,7 +144,7 @@ func TestAPIGetReleaseByTag(t *testing.T) { nonexistingtag := "nonexistingtag" - urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/", + urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, nonexistingtag) req = NewRequestf(t, "GET", urlStr) @@ -163,7 +163,7 @@ func TestAPIDeleteTagByName(t *testing.T) { session := loginUser(t, owner.LowerName) token := getTokenForLoggedInUser(t, session) - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag/?token=%s", + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag?token=%s", owner.Name, repo.Name, token) req := NewRequestf(t, http.MethodDelete, urlStr) @@ -171,7 +171,7 @@ func TestAPIDeleteTagByName(t *testing.T) { // Make sure that actual releases can't be deleted outright createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") - urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag/?token=%s", + urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag?token=%s", owner.Name, repo.Name, token) req = NewRequestf(t, http.MethodDelete, urlStr) diff --git a/integrations/create_no_session_test.go b/integrations/create_no_session_test.go index ae0d9f812..89682e95c 100644 --- a/integrations/create_no_session_test.go +++ b/integrations/create_no_session_test.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/routes" - "gitea.com/macaron/session" + "gitea.com/go-chi/session" "github.com/stretchr/testify/assert" ) @@ -58,9 +58,7 @@ func TestSessionFileCreation(t *testing.T) { oldSessionConfig := setting.SessionConfig.ProviderConfig defer func() { setting.SessionConfig.ProviderConfig = oldSessionConfig - c = routes.NewChi() - c.Mount("/", routes.NormalRoutes()) - routes.DelegateToMacaron(c) + c = routes.NormalRoutes() }() var config session.Options @@ -84,9 +82,7 @@ func TestSessionFileCreation(t *testing.T) { setting.SessionConfig.ProviderConfig = string(newConfigBytes) - c = routes.NewChi() - c.Mount("/", routes.NormalRoutes()) - routes.DelegateToMacaron(c) + c = routes.NormalRoutes() t.Run("NoSessionOnViewIssue", func(t *testing.T) { defer PrintCurrentTest(t)() diff --git a/integrations/integration_test.go b/integrations/integration_test.go index f3b2644c7..ee005c087 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -31,15 +31,15 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers/routes" "github.com/PuerkitoBio/goquery" - "github.com/go-chi/chi" "github.com/stretchr/testify/assert" ) -var c chi.Router +var c *web.Route type NilResponseRecorder struct { httptest.ResponseRecorder @@ -66,9 +66,7 @@ func TestMain(m *testing.M) { defer cancel() initIntegrationTest() - c = routes.NewChi() - c.Mount("/", routes.NormalRoutes()) - routes.DelegateToMacaron(c) + c = routes.NormalRoutes() // integration test settings... if setting.Cfg != nil { @@ -387,6 +385,9 @@ func NewRequestWithJSON(t testing.TB, method, urlStr string, v interface{}) *htt func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *http.Request { t.Helper() + if !strings.HasPrefix(urlStr, "http") && !strings.HasPrefix(urlStr, "/") { + urlStr = "/" + urlStr + } request, err := http.NewRequest(method, urlStr, body) assert.NoError(t, err) request.RequestURI = urlStr diff --git a/integrations/lfs_getobject_test.go b/integrations/lfs_getobject_test.go index 180182dd4..f364349ef 100644 --- a/integrations/lfs_getobject_test.go +++ b/integrations/lfs_getobject_test.go @@ -19,8 +19,8 @@ import ( "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/routers/routes" - "gitea.com/macaron/gzip" gzipp "github.com/klauspost/compress/gzip" "github.com/stretchr/testify/assert" ) @@ -121,7 +121,7 @@ func TestGetLFSLarge(t *testing.T) { t.Skip() return } - content := make([]byte, gzip.MinSize*10) + content := make([]byte, routes.GzipMinSize*10) for i := range content { content[i] = byte(i % 256) } @@ -137,7 +137,7 @@ func TestGetLFSGzip(t *testing.T) { t.Skip() return } - b := make([]byte, gzip.MinSize*10) + b := make([]byte, routes.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } @@ -158,7 +158,7 @@ func TestGetLFSZip(t *testing.T) { t.Skip() return } - b := make([]byte, gzip.MinSize*10) + b := make([]byte, routes.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } diff --git a/integrations/links_test.go b/integrations/links_test.go index 2c674c104..e16b688c8 100644 --- a/integrations/links_test.go +++ b/integrations/links_test.go @@ -32,7 +32,6 @@ func TestLinksNoLogin(t *testing.T) { "/user/login", "/user/forgot_password", "/api/swagger", - "/api/v1/swagger", "/user2/repo1", "/user2/repo1/projects", "/user2/repo1/projects/1", @@ -53,6 +52,7 @@ func TestRedirectsNoLogin(t *testing.T) { "/user2/repo1/src/master/file.txt": "/user2/repo1/src/branch/master/file.txt", "/user2/repo1/src/master/directory/file.txt": "/user2/repo1/src/branch/master/directory/file.txt", "/user/avatar/Ghost/-1": "/img/avatar_default.png", + "/api/v1/swagger": "/api/swagger", } for link, redirectLink := range redirects { req := NewRequest(t, "GET", link) @@ -86,7 +86,6 @@ func testLinksAsUser(userName string, t *testing.T) { "/", "/user/forgot_password", "/api/swagger", - "/api/v1/swagger", "/issues", "/issues?type=your_repositories&repos=[0]&sort=&state=open", "/issues?type=assigned&repos=[0]&sort=&state=open", diff --git a/modules/auth/sso/interface.go b/modules/auth/sso/interface.go index c957fad02..7efe79a69 100644 --- a/modules/auth/sso/interface.go +++ b/modules/auth/sso/interface.go @@ -8,19 +8,15 @@ import ( "net/http" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/middlewares" + "code.gitea.io/gitea/modules/session" ) // DataStore represents a data store -type DataStore interface { - GetData() map[string]interface{} -} +type DataStore middlewares.DataStore // SessionStore represents a session store -type SessionStore interface { - Get(interface{}) interface{} - Set(interface{}, interface{}) error - Delete(interface{}) error -} +type SessionStore session.Store // SingleSignOn represents a SSO authentication method (plugin) for HTTP requests. type SingleSignOn interface { diff --git a/modules/auth/sso/oauth2.go b/modules/auth/sso/oauth2.go index fc22e2728..c3f6f08fb 100644 --- a/modules/auth/sso/oauth2.go +++ b/modules/auth/sso/oauth2.go @@ -62,6 +62,8 @@ func (o *OAuth2) Free() error { // userIDFromToken returns the user id corresponding to the OAuth token. func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { + _ = req.ParseForm() + // Check access token. tokenSHA := req.Form.Get("token") if len(tokenSHA) == 0 { diff --git a/modules/cache/cache.go b/modules/cache/cache.go index 42227f928..3f8885ee3 100644 --- a/modules/cache/cache.go +++ b/modules/cache/cache.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/modules/setting" - mc "gitea.com/macaron/cache" + mc "gitea.com/go-chi/cache" - _ "gitea.com/macaron/cache/memcache" // memcache plugin for cache + _ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache ) var ( @@ -20,7 +20,7 @@ var ( ) func newCache(cacheConfig setting.Cache) (mc.Cache, error) { - return mc.NewCacher(cacheConfig.Adapter, mc.Options{ + return mc.NewCacher(mc.Options{ Adapter: cacheConfig.Adapter, AdapterConfig: cacheConfig.Conn, Interval: cacheConfig.Interval, diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go index 96e865a38..3cb0292e2 100644 --- a/modules/cache/cache_redis.go +++ b/modules/cache/cache_redis.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/modules/nosql" - "gitea.com/macaron/cache" + "gitea.com/go-chi/cache" "github.com/go-redis/redis/v7" "github.com/unknwon/com" ) diff --git a/modules/context/api.go b/modules/context/api.go index 7771ec1b1..cf6dc265c 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -6,18 +6,21 @@ package context import ( + "context" "fmt" + "html" "net/http" "net/url" "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" - "gitea.com/macaron/csrf" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/session" ) // APIContext is a specific macaron context for API service @@ -91,7 +94,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) { if status == http.StatusInternalServerError { log.ErrorWithSkip(1, "%s: %s", title, message) - if macaron.Env == macaron.PROD && !(ctx.User != nil && ctx.User.IsAdmin) { + if setting.IsProd() && !(ctx.User != nil && ctx.User.IsAdmin) { message = "" } } @@ -108,7 +111,7 @@ func (ctx *APIContext) InternalServerError(err error) { log.ErrorWithSkip(1, "InternalServerError: %v", err) var message string - if macaron.Env != macaron.PROD || (ctx.User != nil && ctx.User.IsAdmin) { + if !setting.IsProd() || (ctx.User != nil && ctx.User.IsAdmin) { message = err.Error() } @@ -118,6 +121,20 @@ func (ctx *APIContext) InternalServerError(err error) { }) } +var ( + apiContextKey interface{} = "default_api_context" +) + +// WithAPIContext set up api context in request +func WithAPIContext(req *http.Request, ctx *APIContext) *http.Request { + return req.WithContext(context.WithValue(req.Context(), apiContextKey, ctx)) +} + +// GetAPIContext returns a context for API routes +func GetAPIContext(req *http.Request) *APIContext { + return req.Context().Value(apiContextKey).(*APIContext) +} + func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string { page := NewPagination(total, pageSize, curPage, 0) paginater := page.Paginater @@ -172,7 +189,7 @@ func (ctx *APIContext) RequireCSRF() { headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName()) formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName()) if len(headerToken) > 0 || len(formValueToken) > 0 { - csrf.Validate(ctx.Context.Context, ctx.csrf) + Validate(ctx.Context, ctx.csrf) } else { ctx.Context.Error(401, "Missing CSRF token.") } @@ -201,42 +218,91 @@ func (ctx *APIContext) CheckForOTP() { } // APIContexter returns apicontext as macaron middleware -func APIContexter() macaron.Handler { - return func(c *Context) { - ctx := &APIContext{ - Context: c, - } - c.Map(ctx) +func APIContexter() func(http.Handler) http.Handler { + var csrfOpts = getCsrfOpts() + + return func(next http.Handler) http.Handler { + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + var locale = middlewares.Locale(w, req) + var ctx = APIContext{ + Context: &Context{ + Resp: NewResponse(w), + Data: map[string]interface{}{}, + Locale: locale, + Session: session.GetSession(req), + Repo: &Repository{ + PullRequest: &PullRequest{}, + }, + Org: &Organization{}, + }, + Org: &APIOrganization{}, + } + + ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) + ctx.csrf = Csrfer(csrfOpts, ctx.Context) + + // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. + if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { + if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size + ctx.InternalServerError(err) + return + } + } + + // Get user from session if logged in. + ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req, ctx.Resp, &ctx, ctx.Session) + if ctx.User != nil { + ctx.IsSigned = true + ctx.Data["IsSigned"] = ctx.IsSigned + ctx.Data["SignedUser"] = ctx.User + ctx.Data["SignedUserID"] = ctx.User.ID + ctx.Data["SignedUserName"] = ctx.User.Name + ctx.Data["IsAdmin"] = ctx.User.IsAdmin + } else { + ctx.Data["SignedUserID"] = int64(0) + ctx.Data["SignedUserName"] = "" + } + + ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) + + next.ServeHTTP(ctx.Resp, ctx.Req) + }) } } // ReferencesGitRepo injects the GitRepo into the Context -func ReferencesGitRepo(allowEmpty bool) macaron.Handler { - return func(ctx *APIContext) { - // Empty repository does not have reference information. - if !allowEmpty && ctx.Repo.Repository.IsEmpty { - return - } - - // For API calls. - if ctx.Repo.GitRepo == nil { - repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - ctx.Error(500, "RepoRef Invalid repo "+repoPath, err) +func ReferencesGitRepo(allowEmpty bool) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := GetAPIContext(req) + // Empty repository does not have reference information. + if !allowEmpty && ctx.Repo.Repository.IsEmpty { return } - ctx.Repo.GitRepo = gitRepo - // We opened it, we should close it - defer func() { - // If it's been set to nil then assume someone else has closed it. - if ctx.Repo.GitRepo != nil { - ctx.Repo.GitRepo.Close() + + // For API calls. + if ctx.Repo.GitRepo == nil { + repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) + gitRepo, err := git.OpenRepository(repoPath) + if err != nil { + ctx.Error(500, "RepoRef Invalid repo "+repoPath, err) + return } - }() - } + ctx.Repo.GitRepo = gitRepo + // We opened it, we should close it + defer func() { + // If it's been set to nil then assume someone else has closed it. + if ctx.Repo.GitRepo != nil { + ctx.Repo.GitRepo.Close() + } + }() + } - ctx.Next() + next.ServeHTTP(w, req) + }) } } @@ -266,8 +332,9 @@ func (ctx *APIContext) NotFound(objs ...interface{}) { } // RepoRefForAPI handles repository reference names when the ref name is not explicitly given -func RepoRefForAPI() macaron.Handler { - return func(ctx *APIContext) { +func RepoRefForAPI(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := GetAPIContext(req) // Empty repository does not have reference information. if ctx.Repo.Repository.IsEmpty { return @@ -319,6 +386,6 @@ func RepoRefForAPI() macaron.Handler { return } - ctx.Next() - } + next.ServeHTTP(w, req) + }) } diff --git a/modules/context/auth.go b/modules/context/auth.go index 02248384e..8be6ed190 100644 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -7,12 +7,8 @@ package context import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - - "gitea.com/macaron/csrf" - "gitea.com/macaron/macaron" ) // ToggleOptions contains required or check options @@ -24,42 +20,23 @@ type ToggleOptions struct { } // Toggle returns toggle options as middleware -func Toggle(options *ToggleOptions) macaron.Handler { +func Toggle(options *ToggleOptions) func(ctx *Context) { return func(ctx *Context) { - isAPIPath := auth.IsAPIPath(ctx.Req.URL.Path) - // Check prohibit login users. if ctx.IsSigned { if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - if isAPIPath { - ctx.JSON(403, map[string]string{ - "message": "This account is not activated.", - }) - return - } ctx.HTML(200, "user/auth/activate") return - } else if !ctx.User.IsActive || ctx.User.ProhibitLogin { + } + if !ctx.User.IsActive || ctx.User.ProhibitLogin { log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - if isAPIPath { - ctx.JSON(403, map[string]string{ - "message": "This account is prohibited from signing in, please contact your site administrator.", - }) - return - } ctx.HTML(200, "user/auth/prohibit_login") return } if ctx.User.MustChangePassword { - if isAPIPath { - ctx.JSON(403, map[string]string{ - "message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", - }) - return - } if ctx.Req.URL.Path != "/user/settings/change_password" { ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" @@ -82,8 +59,8 @@ func Toggle(options *ToggleOptions) macaron.Handler { return } - if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" && !auth.IsAPIPath(ctx.Req.URL.Path) { - csrf.Validate(ctx.Context, ctx.csrf) + if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" { + Validate(ctx, ctx.csrf) if ctx.Written() { return } @@ -91,13 +68,6 @@ func Toggle(options *ToggleOptions) macaron.Handler { if options.SignInRequired { if !ctx.IsSigned { - // Restrict API calls with error message. - if isAPIPath { - ctx.JSON(403, map[string]string{ - "message": "Only signed in user is allowed to call APIs.", - }) - return - } if ctx.Req.URL.Path != "/user/events" { ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) } @@ -108,19 +78,88 @@ func Toggle(options *ToggleOptions) macaron.Handler { ctx.HTML(200, "user/auth/activate") return } - if ctx.IsSigned && isAPIPath && ctx.IsBasicAuth { + } + + // Redirect to log in page if auto-signin info is provided and has not signed in. + if !options.SignOutRequired && !ctx.IsSigned && + len(ctx.GetCookie(setting.CookieUserName)) > 0 { + if ctx.Req.URL.Path != "/user/events" { + ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) + } + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } + + if options.AdminRequired { + if !ctx.User.IsAdmin { + ctx.Error(403) + return + } + ctx.Data["PageIsAdmin"] = true + } + } +} + +// ToggleAPI returns toggle options as middleware +func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) { + return func(ctx *APIContext) { + // Check prohibit login users. + if ctx.IsSigned { + if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.JSON(403, map[string]string{ + "message": "This account is not activated.", + }) + return + } + if !ctx.User.IsActive || ctx.User.ProhibitLogin { + log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.JSON(403, map[string]string{ + "message": "This account is prohibited from signing in, please contact your site administrator.", + }) + return + } + + if ctx.User.MustChangePassword { + ctx.JSON(403, map[string]string{ + "message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", + }) + return + } + } + + // Redirect to dashboard if user tries to visit any non-login page. + if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { + ctx.Redirect(setting.AppSubURL + "/") + return + } + + if options.SignInRequired { + if !ctx.IsSigned { + // Restrict API calls with error message. + ctx.JSON(403, map[string]string{ + "message": "Only signed in user is allowed to call APIs.", + }) + return + } else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(200, "user/auth/activate") + return + } + if ctx.IsSigned && ctx.IsBasicAuth { twofa, err := models.GetTwoFactorByUID(ctx.User.ID) if err != nil { if models.IsErrTwoFactorNotEnrolled(err) { return // No 2FA enrollment for this user } - ctx.Error(500) + ctx.InternalServerError(err) return } otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") ok, err := twofa.ValidateTOTP(otpHeader) if err != nil { - ctx.Error(500) + ctx.InternalServerError(err) return } if !ok { @@ -132,19 +171,11 @@ func Toggle(options *ToggleOptions) macaron.Handler { } } - // Redirect to log in page if auto-signin info is provided and has not signed in. - if !options.SignOutRequired && !ctx.IsSigned && !isAPIPath && - len(ctx.GetCookie(setting.CookieUserName)) > 0 { - if ctx.Req.URL.Path != "/user/events" { - ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) - } - ctx.Redirect(setting.AppSubURL + "/user/login") - return - } - if options.AdminRequired { if !ctx.User.IsAdmin { - ctx.Error(403) + ctx.JSON(403, map[string]string{ + "message": "You have no permission to request for this.", + }) return } ctx.Data["PageIsAdmin"] = true diff --git a/modules/context/captcha.go b/modules/context/captcha.go new file mode 100644 index 000000000..956380ed7 --- /dev/null +++ b/modules/context/captcha.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "sync" + + "code.gitea.io/gitea/modules/setting" + + "gitea.com/go-chi/captcha" +) + +var imageCaptchaOnce sync.Once +var cpt *captcha.Captcha + +// GetImageCaptcha returns global image captcha +func GetImageCaptcha() *captcha.Captcha { + imageCaptchaOnce.Do(func() { + cpt = captcha.NewCaptcha(captcha.Options{ + SubURL: setting.AppSubURL, + }) + }) + return cpt +} diff --git a/modules/context/context.go b/modules/context/context.go index e4121649a..630129b8c 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -6,37 +6,55 @@ package context import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" "html" "html/template" "io" "net/http" "net/url" "path" + "strconv" "strings" "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" - "gitea.com/macaron/cache" - "gitea.com/macaron/csrf" - "gitea.com/macaron/i18n" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "gitea.com/go-chi/cache" + "gitea.com/go-chi/session" + "github.com/go-chi/chi" "github.com/unknwon/com" + "github.com/unknwon/i18n" + "github.com/unrolled/render" + "golang.org/x/crypto/pbkdf2" ) +// Render represents a template render +type Render interface { + TemplateLookup(tmpl string) *template.Template + HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...render.HTMLOptions) error +} + // Context represents context of a request. type Context struct { - *macaron.Context + Resp ResponseWriter + Req *http.Request + Data map[string]interface{} + Render Render + translation.Locale Cache cache.Cache - csrf csrf.CSRF - Flash *session.Flash + csrf CSRF + Flash *middlewares.Flash Session session.Store Link string // current request URL @@ -163,13 +181,22 @@ func (ctx *Context) RedirectToFirst(location ...string) { // HTML calls Context.HTML and converts template name to string. func (ctx *Context) HTML(status int, name base.TplName) { log.Debug("Template: %s", name) - ctx.Context.HTML(status, string(name)) + if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil { + ctx.ServerError("Render failed", err) + } +} + +// HTMLString render content to a string but not http.ResponseWriter +func (ctx *Context) HTMLString(name string, data interface{}) (string, error) { + var buf strings.Builder + err := ctx.Render.HTML(&buf, 200, string(name), data) + return buf.String(), err } // RenderWithErr used for page has form validation but need to prompt error to users. func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) { if form != nil { - auth.AssignForm(form, ctx.Data) + middlewares.AssignForm(form, ctx.Data) } ctx.Flash.ErrorMsg = msg ctx.Data["Flash"] = ctx.Flash @@ -184,7 +211,7 @@ func (ctx *Context) NotFound(title string, err error) { func (ctx *Context) notFoundInternal(title string, err error) { if err != nil { log.ErrorWithSkip(2, "%s: %v", title, err) - if macaron.Env != macaron.PROD { + if !setting.IsProd() { ctx.Data["ErrorMsg"] = err } } @@ -203,7 +230,7 @@ func (ctx *Context) ServerError(title string, err error) { func (ctx *Context) serverErrorInternal(title string, err error) { if err != nil { log.ErrorWithSkip(2, "%s: %v", title, err) - if macaron.Env != macaron.PROD { + if !setting.IsProd() { ctx.Data["ErrorMsg"] = err } } @@ -224,6 +251,44 @@ func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool, ctx.serverErrorInternal(title, err) } +// Header returns a header +func (ctx *Context) Header() http.Header { + return ctx.Resp.Header() +} + +// FIXME: We should differ Query and Form, currently we just use form as query +// Currently to be compatible with macaron, we keep it. + +// Query returns request form as string with default +func (ctx *Context) Query(key string, defaults ...string) string { + return (*Forms)(ctx.Req).MustString(key, defaults...) +} + +// QueryTrim returns request form as string with default and trimmed spaces +func (ctx *Context) QueryTrim(key string, defaults ...string) string { + return (*Forms)(ctx.Req).MustTrimmed(key, defaults...) +} + +// QueryStrings returns request form as strings with default +func (ctx *Context) QueryStrings(key string, defaults ...[]string) []string { + return (*Forms)(ctx.Req).MustStrings(key, defaults...) +} + +// QueryInt returns request form as int with default +func (ctx *Context) QueryInt(key string, defaults ...int) int { + return (*Forms)(ctx.Req).MustInt(key, defaults...) +} + +// QueryInt64 returns request form as int64 with default +func (ctx *Context) QueryInt64(key string, defaults ...int64) int64 { + return (*Forms)(ctx.Req).MustInt64(key, defaults...) +} + +// QueryBool returns request form as bool with default +func (ctx *Context) QueryBool(key string, defaults ...bool) bool { + return (*Forms)(ctx.Req).MustBool(key, defaults...) +} + // HandleText handles HTTP status code func (ctx *Context) HandleText(status int, title string) { if (status/100 == 4) || (status/100 == 5) { @@ -249,66 +314,324 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa ctx.Resp.Header().Set("Cache-Control", "must-revalidate") ctx.Resp.Header().Set("Pragma", "public") ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r) + http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r) +} + +// PlainText render content as plain text +func (ctx *Context) PlainText(status int, bs []byte) { + ctx.Resp.WriteHeader(status) + ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf8") + if _, err := ctx.Resp.Write(bs); err != nil { + ctx.ServerError("Render JSON failed", err) + } +} + +// 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, file) +} + +// Error returned an error to web browser +func (ctx *Context) Error(status int, contents ...string) { + var v = http.StatusText(status) + if len(contents) > 0 { + v = contents[0] + } + http.Error(ctx.Resp, v, status) +} + +// JSON render content as JSON +func (ctx *Context) JSON(status int, content interface{}) { + ctx.Resp.WriteHeader(status) + ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf8") + if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil { + ctx.ServerError("Render JSON failed", err) + } +} + +// Redirect redirect the request +func (ctx *Context) Redirect(location string, status ...int) { + code := http.StatusFound + if len(status) == 1 { + code = status[0] + } + + http.Redirect(ctx.Resp, ctx.Req, location, code) +} + +// SetCookie set cookies to web browser +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { + middlewares.SetCookie(ctx.Resp, name, value, others...) +} + +// GetCookie returns given cookie value from request header. +func (ctx *Context) GetCookie(name string) string { + return middlewares.GetCookie(ctx.Req, name) +} + +// 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 +} + +// 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...) +} + +// GetCookieInt returns cookie result in int type. +func (ctx *Context) GetCookieInt(name string) int { + r, _ := strconv.Atoi(ctx.GetCookie(name)) + return r +} + +// GetCookieInt64 returns cookie result in int64 type. +func (ctx *Context) GetCookieInt64(name string) int64 { + r, _ := strconv.ParseInt(ctx.GetCookie(name), 10, 64) + return r +} + +// GetCookieFloat64 returns cookie result in float64 type. +func (ctx *Context) GetCookieFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64) + return v +} + +// RemoteAddr returns the client machie ip address +func (ctx *Context) RemoteAddr() string { + return ctx.Req.RemoteAddr +} + +// Params returns the param on route +func (ctx *Context) Params(p string) string { + s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":"))) + return s +} + +// ParamsInt64 returns the param on route as int64 +func (ctx *Context) ParamsInt64(p string) int64 { + v, _ := strconv.ParseInt(ctx.Params(p), 10, 64) + return v +} + +// SetParams set params into routes +func (ctx *Context) SetParams(k, v string) { + chiCtx := chi.RouteContext(ctx.Req.Context()) + chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v)) +} + +// Write writes data to webbrowser +func (ctx *Context) Write(bs []byte) (int, error) { + return ctx.Resp.Write(bs) +} + +// Written returns true if there are something sent to web browser +func (ctx *Context) Written() bool { + return ctx.Resp.Status() > 0 +} + +// Status writes status code +func (ctx *Context) Status(status int) { + ctx.Resp.WriteHeader(status) +} + +// Handler represents a custom handler +type Handler func(*Context) + +// enumerate all content +var ( + contextKey interface{} = "default_context" +) + +// WithContext set up install context in request +func WithContext(req *http.Request, ctx *Context) *http.Request { + return req.WithContext(context.WithValue(req.Context(), contextKey, ctx)) +} + +// GetContext retrieves install context from request +func GetContext(req *http.Request) *Context { + return req.Context().Value(contextKey).(*Context) +} + +func getCsrfOpts() CsrfOptions { + return CsrfOptions{ + Secret: setting.SecretKey, + Cookie: setting.CSRFCookieName, + SetCookie: true, + Secure: setting.SessionConfig.Secure, + CookieHTTPOnly: setting.CSRFCookieHTTPOnly, + Header: "X-Csrf-Token", + CookieDomain: setting.SessionConfig.Domain, + CookiePath: setting.SessionConfig.CookiePath, + } } // Contexter initializes a classic context for a request. -func Contexter() macaron.Handler { - return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) { - ctx := &Context{ - Context: c, - Cache: cache, - csrf: x, - Flash: f, - Session: sess, - Link: setting.AppSubURL + strings.TrimSuffix(c.Req.URL.EscapedPath(), "/"), - Repo: &Repository{ - PullRequest: &PullRequest{}, - }, - Org: &Organization{}, +func Contexter() func(next http.Handler) http.Handler { + rnd := templates.HTMLRenderer() + + var c cache.Cache + var err error + if setting.CacheService.Enabled { + c, err = cache.NewCacher(cache.Options{ + Adapter: setting.CacheService.Adapter, + AdapterConfig: setting.CacheService.Conn, + Interval: setting.CacheService.Interval, + }) + if err != nil { + panic(err) } - ctx.Data["Language"] = ctx.Locale.Language() - c.Data["Link"] = ctx.Link - ctx.Data["CurrentURL"] = setting.AppSubURL + c.Req.URL.RequestURI() - ctx.Data["PageStartTime"] = time.Now() - // Quick responses appropriate go-get meta with status 200 - // regardless of if user have access to the repository, - // or the repository does not exist at all. - // This is particular a workaround for "go get" command which does not respect - // .netrc file. - if ctx.Query("go-get") == "1" { - ownerName := c.Params(":username") - repoName := c.Params(":reponame") - trimmedRepoName := strings.TrimSuffix(repoName, ".git") - - if ownerName == "" || trimmedRepoName == "" { - _, _ = c.Write([]byte(` + } + + var csrfOpts = getCsrfOpts() + //var flashEncryptionKey, _ = NewSecret() + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + var locale = middlewares.Locale(resp, req) + var startTime = time.Now() + var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/") + var ctx = Context{ + Resp: NewResponse(resp), + Cache: c, + Locale: locale, + Link: link, + Render: rnd, + Session: session.GetSession(req), + Repo: &Repository{ + PullRequest: &PullRequest{}, + }, + Org: &Organization{}, + Data: map[string]interface{}{ + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "PageStartTime": startTime, + "TmplLoadTimes": func() string { + return time.Since(startTime).String() + }, + "Link": link, + }, + } + + ctx.Req = WithContext(req, &ctx) + ctx.csrf = Csrfer(csrfOpts, &ctx) + + // Get flash. + flashCookie := ctx.GetCookie("macaron_flash") + vals, _ := url.ParseQuery(flashCookie) + if len(vals) > 0 { + f := &middlewares.Flash{ + DataStore: &ctx, + Values: vals, + ErrorMsg: vals.Get("error"), + SuccessMsg: vals.Get("success"), + InfoMsg: vals.Get("info"), + WarningMsg: vals.Get("warning"), + } + ctx.Data["Flash"] = f + } + + f := &middlewares.Flash{ + DataStore: &ctx, + Values: url.Values{}, + ErrorMsg: "", + WarningMsg: "", + InfoMsg: "", + SuccessMsg: "", + } + ctx.Resp.Before(func(resp ResponseWriter) { + if flash := f.Encode(); len(flash) > 0 { + if err == nil { + middlewares.SetCookie(resp, "macaron_flash", flash, 0, + setting.SessionConfig.CookiePath, + middlewares.Domain(setting.SessionConfig.Domain), + middlewares.HTTPOnly(true), + middlewares.Secure(setting.SessionConfig.Secure), + //middlewares.SameSite(opt.SameSite), FIXME: we need a samesite config + ) + return + } + } + + ctx.SetCookie("macaron_flash", "", -1, + setting.SessionConfig.CookiePath, + middlewares.Domain(setting.SessionConfig.Domain), + middlewares.HTTPOnly(true), + middlewares.Secure(setting.SessionConfig.Secure), + //middlewares.SameSite(), FIXME: we need a samesite config + ) + }) + + ctx.Flash = f + + // Quick responses appropriate go-get meta with status 200 + // regardless of if user have access to the repository, + // or the repository does not exist at all. + // This is particular a workaround for "go get" command which does not respect + // .netrc file. + if ctx.Query("go-get") == "1" { + ownerName := ctx.Params(":username") + repoName := ctx.Params(":reponame") + trimmedRepoName := strings.TrimSuffix(repoName, ".git") + + if ownerName == "" || trimmedRepoName == "" { + _, _ = ctx.Write([]byte(` invalid import path `)) - c.WriteHeader(400) - return - } - branchName := "master" - - repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) - if err == nil && len(repo.DefaultBranch) > 0 { - branchName = repo.DefaultBranch - } - prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName)) - - appURL, _ := url.Parse(setting.AppURL) - - insecure := "" - if appURL.Scheme == string(setting.HTTP) { - insecure = "--insecure " - } - c.Header().Set("Content-Type", "text/html") - c.WriteHeader(http.StatusOK) - _, _ = c.Write([]byte(com.Expand(` + ctx.Status(400) + return + } + branchName := "master" + + repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) + if err == nil && len(repo.DefaultBranch) > 0 { + branchName = repo.DefaultBranch + } + prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName)) + + appURL, _ := url.Parse(setting.AppURL) + + insecure := "" + if appURL.Scheme == string(setting.HTTP) { + insecure = "--insecure " + } + ctx.Header().Set("Content-Type", "text/html") + ctx.Status(http.StatusOK) + _, _ = ctx.Write([]byte(com.Expand(` @@ -319,60 +642,72 @@ func Contexter() macaron.Handler { `, map[string]string{ - "GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName), - "CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName), - "GoDocDirectory": prefix + "{/dir}", - "GoDocFile": prefix + "{/dir}/{file}#L{line}", - "Insecure": insecure, - }))) - return - } - - // Get user from session if logged in. - ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req.Request, c.Resp, ctx, ctx.Session) - - if ctx.User != nil { - ctx.IsSigned = true - ctx.Data["IsSigned"] = ctx.IsSigned - ctx.Data["SignedUser"] = ctx.User - ctx.Data["SignedUserID"] = ctx.User.ID - ctx.Data["SignedUserName"] = ctx.User.Name - ctx.Data["IsAdmin"] = ctx.User.IsAdmin - } else { - ctx.Data["SignedUserID"] = int64(0) - ctx.Data["SignedUserName"] = "" - } - - // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. - if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { - if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size - ctx.ServerError("ParseMultipartForm", err) + "GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName), + "CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName), + "GoDocDirectory": prefix + "{/dir}", + "GoDocFile": prefix + "{/dir}/{file}#L{line}", + "Insecure": insecure, + }))) return } - } - - ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) - - ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken()) - ctx.Data["CsrfTokenHtml"] = template.HTML(``) - log.Debug("Session ID: %s", sess.ID()) - log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"]) - - ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome - ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore - ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations - ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton - ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage - ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding - ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion + // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. + if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { + if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size + ctx.ServerError("ParseMultipartForm", err) + return + } + } - ctx.Data["EnableSwagger"] = setting.API.EnableSwagger - ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn - ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations + // Get user from session if logged in. + ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req, ctx.Resp, &ctx, ctx.Session) + + if ctx.User != nil { + ctx.IsSigned = true + ctx.Data["IsSigned"] = ctx.IsSigned + ctx.Data["SignedUser"] = ctx.User + ctx.Data["SignedUserID"] = ctx.User.ID + ctx.Data["SignedUserName"] = ctx.User.Name + ctx.Data["IsAdmin"] = ctx.User.IsAdmin + } else { + ctx.Data["SignedUserID"] = int64(0) + ctx.Data["SignedUserName"] = "" + } - ctx.Data["ManifestData"] = setting.ManifestData + ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) + ctx.Data["CsrfTokenHtml"] = template.HTML(``) + log.Debug("Session ID: %s", ctx.Session.ID()) + log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"]) + + ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome + ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore + ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations + + ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton + ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage + ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding + ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion + + ctx.Data["EnableSwagger"] = setting.API.EnableSwagger + ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn + ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations + + ctx.Data["ManifestData"] = setting.ManifestData + + ctx.Data["i18n"] = locale + ctx.Data["Tr"] = i18n.Tr + ctx.Data["Lang"] = locale.Language() + ctx.Data["AllLangs"] = translation.AllLangs() + for _, lang := range translation.AllLangs() { + if lang.Lang == locale.Language() { + ctx.Data["LangName"] = lang.Name + break + } + } - c.Map(ctx) + next.ServeHTTP(ctx.Resp, ctx.Req) + }) } } diff --git a/vendor/gitea.com/macaron/csrf/csrf.go b/modules/context/csrf.go similarity index 69% rename from vendor/gitea.com/macaron/csrf/csrf.go rename to modules/context/csrf.go index 66f5b40a8..4a26664bf 100644 --- a/vendor/gitea.com/macaron/csrf/csrf.go +++ b/modules/context/csrf.go @@ -1,5 +1,6 @@ // Copyright 2013 Martini Authors // Copyright 2014 The Macaron Authors +// Copyright 2021 The Gitea 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 @@ -13,24 +14,17 @@ // 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 +// a middleware that generates and validates CSRF tokens. + +package context 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. @@ -42,7 +36,7 @@ type CSRF interface { // Return cookie path GetCookiePath() string // Return the flag value used for the csrf token. - GetCookieHttpOnly() bool + GetCookieHTTPOnly() bool // Return the token. GetToken() string // Validate by token. @@ -63,7 +57,7 @@ type csrf struct { //Cookie path CookiePath string // Cookie HttpOnly flag value used for the csrf token. - CookieHttpOnly bool + CookieHTTPOnly bool // Token generated to pass via header, cookie, or hidden form value. Token string // This value must be unique per user. @@ -94,9 +88,9 @@ 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 +// 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 @@ -115,8 +109,8 @@ func (c *csrf) Error(w http.ResponseWriter) { c.ErrorFunc(w) } -// Options maintains options to manage behavior of Generate. -type Options struct { +// CsrfOptions maintains options to manage behavior of Generate. +type CsrfOptions struct { // The global secret value used to generate Tokens. Secret string // HTTP header used to set and get token. @@ -129,11 +123,13 @@ type Options struct { CookieDomain string // Cookie path. CookiePath string - CookieHttpOnly bool + CookieHTTPOnly bool + // SameSite set the cookie SameSite type + SameSite http.SameSite // Key used for getting the unique ID per user. SessionKey string - // oldSeesionKey saves old value corresponding to SessionKey. - oldSeesionKey string + // oldSessionKey saves old value corresponding to SessionKey. + oldSessionKey string // If true, send token via X-CSRFToken header. SetHeader bool // If true, send token via _csrf cookie. @@ -144,10 +140,12 @@ type Options struct { Origin bool // The function called when Validate fails. ErrorFunc func(w http.ResponseWriter) + // Cookie life time. Default is 0 + CookieLifeTime int } -func prepareOptions(options []Options) Options { - var opt Options +func prepareOptions(options []CsrfOptions) CsrfOptions { + var opt CsrfOptions if len(options) > 0 { opt = options[0] } @@ -171,7 +169,7 @@ func prepareOptions(options []Options) Options { if len(opt.SessionKey) == 0 { opt.SessionKey = "uid" } - opt.oldSeesionKey = "_old_" + opt.SessionKey + opt.oldSessionKey = "_old_" + opt.SessionKey if opt.ErrorFunc == nil { opt.ErrorFunc = func(w http.ResponseWriter) { http.Error(w, "Invalid csrf token.", http.StatusBadRequest) @@ -181,73 +179,73 @@ func prepareOptions(options []Options) Options { return opt } -// Generate maps CSRF to each request. If this request is a Get request, it will generate a new 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 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)) +func Csrfer(opt CsrfOptions, ctx *Context) CSRF { + opt = prepareOptions([]CsrfOptions{opt}) + 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, + } - if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 { - return - } + if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 { + return x + } - x.ID = "0" - uid := sess.Get(opt.SessionKey) - if uid != nil { - x.ID = com.ToStr(uid) - } + x.ID = "0" + uid := ctx.Session.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) + needsNew := false + oldUID := ctx.Session.Get(opt.oldSessionKey) + if oldUID == nil || oldUID.(string) != x.ID { + needsNew = true + _ = ctx.Session.Set(opt.oldSessionKey, 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 { - // 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 - } + 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 needsNew { + // FIXME: actionId. + x.Token = GenerateToken(x.Secret, x.ID, "POST") + if opt.SetCookie { + var expires interface{} + if opt.CookieLifeTime == 0 { + expires = time.Now().AddDate(0, 0, 1) } - } - - if opt.SetHeader { - ctx.Resp.Header().Add(opt.Header, x.Token) + ctx.SetCookie(opt.Cookie, x.Token, opt.CookieLifeTime, opt.CookiePath, opt.CookieDomain, opt.Secure, opt.CookieHTTPOnly, expires, + func(c *http.Cookie) { + c.SameSite = opt.SameSite + }, + ) } } -} -// 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...) + if opt.SetHeader { + ctx.Resp.Header().Add(opt.Header, x.Token) + } + return x } // 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) { +func Validate(ctx *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()) diff --git a/modules/context/form.go b/modules/context/form.go new file mode 100644 index 000000000..c7b76c614 --- /dev/null +++ b/modules/context/form.go @@ -0,0 +1,227 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "errors" + "net/http" + "net/url" + "strconv" + "strings" + "text/template" + + "code.gitea.io/gitea/modules/log" +) + +// Forms a new enhancement of http.Request +type Forms http.Request + +// Values returns http.Request values +func (f *Forms) Values() url.Values { + return (*http.Request)(f).Form +} + +// String returns request form as string +func (f *Forms) String(key string) (string, error) { + return (*http.Request)(f).FormValue(key), nil +} + +// Trimmed returns request form as string with trimed spaces left and right +func (f *Forms) Trimmed(key string) (string, error) { + return strings.TrimSpace((*http.Request)(f).FormValue(key)), nil +} + +// Strings returns request form as strings +func (f *Forms) Strings(key string) ([]string, error) { + if (*http.Request)(f).Form == nil { + if err := (*http.Request)(f).ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + } + if v, ok := (*http.Request)(f).Form[key]; ok { + return v, nil + } + return nil, errors.New("not exist") +} + +// Escape returns request form as escaped string +func (f *Forms) Escape(key string) (string, error) { + return template.HTMLEscapeString((*http.Request)(f).FormValue(key)), nil +} + +// Int returns request form as int +func (f *Forms) Int(key string) (int, error) { + return strconv.Atoi((*http.Request)(f).FormValue(key)) +} + +// Int32 returns request form as int32 +func (f *Forms) Int32(key string) (int32, error) { + v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 32) + return int32(v), err +} + +// Int64 returns request form as int64 +func (f *Forms) Int64(key string) (int64, error) { + return strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 64) +} + +// Uint returns request form as uint +func (f *Forms) Uint(key string) (uint, error) { + v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64) + return uint(v), err +} + +// Uint32 returns request form as uint32 +func (f *Forms) Uint32(key string) (uint32, error) { + v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 32) + return uint32(v), err +} + +// Uint64 returns request form as uint64 +func (f *Forms) Uint64(key string) (uint64, error) { + return strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64) +} + +// Bool returns request form as bool +func (f *Forms) Bool(key string) (bool, error) { + return strconv.ParseBool((*http.Request)(f).FormValue(key)) +} + +// Float32 returns request form as float32 +func (f *Forms) Float32(key string) (float32, error) { + v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 64) + return float32(v), err +} + +// Float64 returns request form as float64 +func (f *Forms) Float64(key string) (float64, error) { + return strconv.ParseFloat((*http.Request)(f).FormValue(key), 64) +} + +// MustString returns request form as string with default +func (f *Forms) MustString(key string, defaults ...string) string { + if v := (*http.Request)(f).FormValue(key); len(v) > 0 { + return v + } + if len(defaults) > 0 { + return defaults[0] + } + return "" +} + +// MustTrimmed returns request form as string with default +func (f *Forms) MustTrimmed(key string, defaults ...string) string { + return strings.TrimSpace(f.MustString(key, defaults...)) +} + +// MustStrings returns request form as strings with default +func (f *Forms) MustStrings(key string, defaults ...[]string) []string { + if (*http.Request)(f).Form == nil { + if err := (*http.Request)(f).ParseMultipartForm(32 << 20); err != nil { + log.Error("ParseMultipartForm: %v", err) + return []string{} + } + } + + if v, ok := (*http.Request)(f).Form[key]; ok { + return v + } + if len(defaults) > 0 { + return defaults[0] + } + return []string{} +} + +// MustEscape returns request form as escaped string with default +func (f *Forms) MustEscape(key string, defaults ...string) string { + if v := (*http.Request)(f).FormValue(key); len(v) > 0 { + return template.HTMLEscapeString(v) + } + if len(defaults) > 0 { + return defaults[0] + } + return "" +} + +// MustInt returns request form as int with default +func (f *Forms) MustInt(key string, defaults ...int) int { + v, err := strconv.Atoi((*http.Request)(f).FormValue(key)) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return v +} + +// MustInt32 returns request form as int32 with default +func (f *Forms) MustInt32(key string, defaults ...int32) int32 { + v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 32) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return int32(v) +} + +// MustInt64 returns request form as int64 with default +func (f *Forms) MustInt64(key string, defaults ...int64) int64 { + v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 64) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return v +} + +// MustUint returns request form as uint with default +func (f *Forms) MustUint(key string, defaults ...uint) uint { + v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return uint(v) +} + +// MustUint32 returns request form as uint32 with default +func (f *Forms) MustUint32(key string, defaults ...uint32) uint32 { + v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 32) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return uint32(v) +} + +// MustUint64 returns request form as uint64 with default +func (f *Forms) MustUint64(key string, defaults ...uint64) uint64 { + v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return v +} + +// MustFloat32 returns request form as float32 with default +func (f *Forms) MustFloat32(key string, defaults ...float32) float32 { + v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 32) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return float32(v) +} + +// MustFloat64 returns request form as float64 with default +func (f *Forms) MustFloat64(key string, defaults ...float64) float64 { + v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 64) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return v +} + +// MustBool returns request form as bool with default +func (f *Forms) MustBool(key string, defaults ...bool) bool { + v, err := strconv.ParseBool((*http.Request)(f).FormValue(key)) + if len(defaults) > 0 && err != nil { + return defaults[0] + } + return v +} diff --git a/modules/context/org.go b/modules/context/org.go index f61a39c66..83d385a1e 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -10,8 +10,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" - - "gitea.com/macaron/macaron" ) // Organization contains organization context @@ -173,7 +171,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { } // OrgAssignment returns a macaron middleware to handle organization assignment -func OrgAssignment(args ...bool) macaron.Handler { +func OrgAssignment(args ...bool) func(ctx *Context) { return func(ctx *Context) { HandleOrgAssignment(ctx, args...) } diff --git a/modules/context/permission.go b/modules/context/permission.go index 151be9f83..6fb8237e2 100644 --- a/modules/context/permission.go +++ b/modules/context/permission.go @@ -7,12 +7,10 @@ package context import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" - - "gitea.com/macaron/macaron" ) // RequireRepoAdmin returns a macaron middleware for requiring repository admin permission -func RequireRepoAdmin() macaron.Handler { +func RequireRepoAdmin() func(ctx *Context) { return func(ctx *Context) { if !ctx.IsSigned || !ctx.Repo.IsAdmin() { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) @@ -22,7 +20,7 @@ func RequireRepoAdmin() macaron.Handler { } // RequireRepoWriter returns a macaron middleware for requiring repository write to the specify unitType -func RequireRepoWriter(unitType models.UnitType) macaron.Handler { +func RequireRepoWriter(unitType models.UnitType) func(ctx *Context) { return func(ctx *Context) { if !ctx.Repo.CanWrite(unitType) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) @@ -32,7 +30,7 @@ func RequireRepoWriter(unitType models.UnitType) macaron.Handler { } // RequireRepoWriterOr returns a macaron middleware for requiring repository write to one of the unit permission -func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler { +func RequireRepoWriterOr(unitTypes ...models.UnitType) func(ctx *Context) { return func(ctx *Context) { for _, unitType := range unitTypes { if ctx.Repo.CanWrite(unitType) { @@ -44,7 +42,7 @@ func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler { } // RequireRepoReader returns a macaron middleware for requiring repository read to the specify unitType -func RequireRepoReader(unitType models.UnitType) macaron.Handler { +func RequireRepoReader(unitType models.UnitType) func(ctx *Context) { return func(ctx *Context) { if !ctx.Repo.CanRead(unitType) { if log.IsTrace() { @@ -70,7 +68,7 @@ func RequireRepoReader(unitType models.UnitType) macaron.Handler { } // RequireRepoReaderOr returns a macaron middleware for requiring repository write to one of the unit permission -func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler { +func RequireRepoReaderOr(unitTypes ...models.UnitType) func(ctx *Context) { return func(ctx *Context) { for _, unitType := range unitTypes { if ctx.Repo.CanRead(unitType) { diff --git a/modules/context/private.go b/modules/context/private.go new file mode 100644 index 000000000..a24610005 --- /dev/null +++ b/modules/context/private.go @@ -0,0 +1,45 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "context" + "net/http" +) + +// PrivateContext represents a context for private routes +type PrivateContext struct { + *Context +} + +var ( + privateContextKey interface{} = "default_private_context" +) + +// WithPrivateContext set up private context in request +func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request { + return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx)) +} + +// GetPrivateContext returns a context for Private routes +func GetPrivateContext(req *http.Request) *PrivateContext { + return req.Context().Value(privateContextKey).(*PrivateContext) +} + +// PrivateContexter returns apicontext as macaron middleware +func PrivateContexter() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := &PrivateContext{ + Context: &Context{ + Resp: NewResponse(w), + Data: map[string]interface{}{}, + }, + } + ctx.Req = WithPrivateContext(req, ctx) + next.ServeHTTP(ctx.Resp, ctx.Req) + }) + } +} diff --git a/modules/context/repo.go b/modules/context/repo.go index 63cb02dc0..79192267f 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -8,6 +8,7 @@ package context import ( "fmt" "io/ioutil" + "net/http" "net/url" "path" "strings" @@ -21,7 +22,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - "gitea.com/macaron/macaron" "github.com/editorconfig/editorconfig-core-go/v2" "github.com/unknwon/com" ) @@ -81,7 +81,7 @@ func (r *Repository) CanCreateBranch() bool { } // RepoMustNotBeArchived checks if a repo is archived -func RepoMustNotBeArchived() macaron.Handler { +func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { if ctx.Repo.Repository.IsArchived { ctx.NotFound("IsArchived", fmt.Errorf(ctx.Tr("repo.archive.title"))) @@ -374,7 +374,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) { } // RepoIDAssignment returns a macaron handler which assigns the repo to the context. -func RepoIDAssignment() macaron.Handler { +func RepoIDAssignment() func(ctx *Context) { return func(ctx *Context) { repoID := ctx.ParamsInt64(":repoid") @@ -394,223 +394,220 @@ func RepoIDAssignment() macaron.Handler { } // RepoAssignment returns a macaron to handle repository assignment -func RepoAssignment() macaron.Handler { - return func(ctx *Context) { - var ( - owner *models.User - err error - ) - - userName := ctx.Params(":username") - repoName := ctx.Params(":reponame") +func RepoAssignment() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + var ( + owner *models.User + err error + ctx = GetContext(req) + ) + + userName := ctx.Params(":username") + repoName := ctx.Params(":reponame") + repoName = strings.TrimSuffix(repoName, ".git") + + // Check if the user is the same as the repository owner + if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { + owner = ctx.User + } else { + owner, err = models.GetUserByName(userName) + if err != nil { + if models.IsErrUserNotExist(err) { + if ctx.Query("go-get") == "1" { + EarlyResponseForGoGetMeta(ctx) + return + } + ctx.NotFound("GetUserByName", nil) + } else { + ctx.ServerError("GetUserByName", err) + } + return + } + } + ctx.Repo.Owner = owner + ctx.Data["Username"] = ctx.Repo.Owner.Name - // Check if the user is the same as the repository owner - if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { - owner = ctx.User - } else { - owner, err = models.GetUserByName(userName) + // Get repository. + repo, err := models.GetRepositoryByName(owner.ID, repoName) if err != nil { - if models.IsErrUserNotExist(err) { - redirectUserID, err := models.LookupUserRedirect(userName) + if models.IsErrRepoNotExist(err) { + redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName) if err == nil { - RedirectToUser(ctx, userName, redirectUserID) - } else if models.IsErrUserRedirectNotExist(err) { + RedirectToRepo(ctx, redirectRepoID) + } else if models.IsErrRepoRedirectNotExist(err) { if ctx.Query("go-get") == "1" { EarlyResponseForGoGetMeta(ctx) return } - ctx.NotFound("GetUserByName", nil) + ctx.NotFound("GetRepositoryByName", nil) } else { - ctx.ServerError("LookupUserRedirect", err) + ctx.ServerError("LookupRepoRedirect", err) } } else { - ctx.ServerError("GetUserByName", err) + ctx.ServerError("GetRepositoryByName", err) } return } - } - ctx.Repo.Owner = owner - ctx.Data["Username"] = ctx.Repo.Owner.Name + repo.Owner = owner - // Get repository. - repo, err := models.GetRepositoryByName(owner.ID, repoName) - if err != nil { - if models.IsErrRepoNotExist(err) { - redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName) - if err == nil { - RedirectToRepo(ctx, redirectRepoID) - } else if models.IsErrRepoRedirectNotExist(err) { - if ctx.Query("go-get") == "1" { - EarlyResponseForGoGetMeta(ctx) - return - } - ctx.NotFound("GetRepositoryByName", nil) - } else { - ctx.ServerError("LookupRepoRedirect", err) - } - } else { - ctx.ServerError("GetRepositoryByName", err) + repoAssignment(ctx, repo) + if ctx.Written() { + return } - return - } - repo.Owner = owner - repoAssignment(ctx, repo) - if ctx.Written() { - return - } + ctx.Repo.RepoLink = repo.Link() + ctx.Data["RepoLink"] = ctx.Repo.RepoLink + ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name - ctx.Repo.RepoLink = repo.Link() - ctx.Data["RepoLink"] = ctx.Repo.RepoLink - ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name + unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker) + if err == nil { + ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL + } - unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker) - if err == nil { - ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL - } + ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ + IncludeTags: true, + }) + if err != nil { + ctx.ServerError("GetReleaseCountByRepoID", err) + return + } + ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{}) + if err != nil { + ctx.ServerError("GetReleaseCountByRepoID", err) + return + } - ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ - IncludeTags: true, - }) - if err != nil { - ctx.ServerError("GetReleaseCountByRepoID", err) - return - } - ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{}) - if err != nil { - ctx.ServerError("GetReleaseCountByRepoID", err) - return - } + ctx.Data["Title"] = owner.Name + "/" + repo.Name + ctx.Data["Repository"] = repo + ctx.Data["Owner"] = ctx.Repo.Repository.Owner + ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner() + ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin() + ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization() + ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode) + ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues) + ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests) + + if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil { + ctx.ServerError("CanUserFork", err) + return + } - ctx.Data["Title"] = owner.Name + "/" + repo.Name - ctx.Data["Repository"] = repo - ctx.Data["Owner"] = ctx.Repo.Repository.Owner - ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner() - ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin() - ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization() - ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode) - ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues) - ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests) - - if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil { - ctx.ServerError("CanUserFork", err) - return - } + ctx.Data["DisableSSH"] = setting.SSH.Disabled + ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous + ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit + ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["CloneLink"] = repo.CloneLink() + ctx.Data["WikiCloneLink"] = repo.WikiCloneLink() - ctx.Data["DisableSSH"] = setting.SSH.Disabled - ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous - ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit - ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["CloneLink"] = repo.CloneLink() - ctx.Data["WikiCloneLink"] = repo.WikiCloneLink() + if ctx.IsSigned { + ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID) + ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID) + } - if ctx.IsSigned { - ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID) - ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID) - } + if repo.IsFork { + RetrieveBaseRepo(ctx, repo) + if ctx.Written() { + return + } + } - if repo.IsFork { - RetrieveBaseRepo(ctx, repo) - if ctx.Written() { - return + if repo.IsGenerated() { + RetrieveTemplateRepo(ctx, repo) + if ctx.Written() { + return + } } - } - if repo.IsGenerated() { - RetrieveTemplateRepo(ctx, repo) - if ctx.Written() { + // Disable everything when the repo is being created + if ctx.Repo.Repository.IsBeingCreated() { + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch return } - } - - // Disable everything when the repo is being created - if ctx.Repo.Repository.IsBeingCreated() { - ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch - return - } - gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName)) - if err != nil { - ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err) - return - } - ctx.Repo.GitRepo = gitRepo - - // We opened it, we should close it - defer func() { - // If it's been set to nil then assume someone else has closed it. - if ctx.Repo.GitRepo != nil { - ctx.Repo.GitRepo.Close() + gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName)) + if err != nil { + ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err) + return } - }() + ctx.Repo.GitRepo = gitRepo - // Stop at this point when the repo is empty. - if ctx.Repo.Repository.IsEmpty { - ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch - ctx.Next() - return - } + // We opened it, we should close it + defer func() { + // If it's been set to nil then assume someone else has closed it. + if ctx.Repo.GitRepo != nil { + ctx.Repo.GitRepo.Close() + } + }() - tags, err := ctx.Repo.GitRepo.GetTags() - if err != nil { - ctx.ServerError("GetTags", err) - return - } - ctx.Data["Tags"] = tags + // Stop at this point when the repo is empty. + if ctx.Repo.Repository.IsEmpty { + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + next.ServeHTTP(w, req) + return + } - brs, err := ctx.Repo.GitRepo.GetBranches() - if err != nil { - ctx.ServerError("GetBranches", err) - return - } - ctx.Data["Branches"] = brs - ctx.Data["BranchesCount"] = len(brs) - - ctx.Data["TagName"] = ctx.Repo.TagName - - // If not branch selected, try default one. - // If default branch doesn't exists, fall back to some other branch. - if len(ctx.Repo.BranchName) == 0 { - if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { - ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch - } else if len(brs) > 0 { - ctx.Repo.BranchName = brs[0] + tags, err := ctx.Repo.GitRepo.GetTags() + if err != nil { + ctx.ServerError("GetTags", err) + return } - } - ctx.Data["BranchName"] = ctx.Repo.BranchName - ctx.Data["CommitID"] = ctx.Repo.CommitID - - // People who have push access or have forked repository can propose a new pull request. - canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) - canCompare := false - - // Pull request is allowed if this is a fork repository - // and base repository accepts pull requests. - if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() { - canCompare = true - ctx.Data["BaseRepo"] = repo.BaseRepo - ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo - ctx.Repo.PullRequest.Allowed = canPush - ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName - } else if repo.AllowsPulls() { - // Or, this is repository accepts pull requests between branches. - canCompare = true - ctx.Data["BaseRepo"] = repo - ctx.Repo.PullRequest.BaseRepo = repo - ctx.Repo.PullRequest.Allowed = canPush - ctx.Repo.PullRequest.SameRepo = true - ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName - } - ctx.Data["CanCompareOrPull"] = canCompare - ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest + ctx.Data["Tags"] = tags - if ctx.Query("go-get") == "1" { - ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name) - prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName) - ctx.Data["GoDocDirectory"] = prefix + "{/dir}" - ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}" - } - ctx.Next() + brs, err := ctx.Repo.GitRepo.GetBranches() + if err != nil { + ctx.ServerError("GetBranches", err) + return + } + ctx.Data["Branches"] = brs + ctx.Data["BranchesCount"] = len(brs) + + ctx.Data["TagName"] = ctx.Repo.TagName + + // If not branch selected, try default one. + // If default branch doesn't exists, fall back to some other branch. + if len(ctx.Repo.BranchName) == 0 { + if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { + ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch + } else if len(brs) > 0 { + ctx.Repo.BranchName = brs[0] + } + } + ctx.Data["BranchName"] = ctx.Repo.BranchName + ctx.Data["CommitID"] = ctx.Repo.CommitID + + // People who have push access or have forked repository can propose a new pull request. + canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) + canCompare := false + + // Pull request is allowed if this is a fork repository + // and base repository accepts pull requests. + if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() { + canCompare = true + ctx.Data["BaseRepo"] = repo.BaseRepo + ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo + ctx.Repo.PullRequest.Allowed = canPush + ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName + } else if repo.AllowsPulls() { + // Or, this is repository accepts pull requests between branches. + canCompare = true + ctx.Data["BaseRepo"] = repo + ctx.Repo.PullRequest.BaseRepo = repo + ctx.Repo.PullRequest.Allowed = canPush + ctx.Repo.PullRequest.SameRepo = true + ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName + } + ctx.Data["CanCompareOrPull"] = canCompare + ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest + + if ctx.Query("go-get") == "1" { + ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name) + prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName) + ctx.Data["GoDocDirectory"] = prefix + "{/dir}" + ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}" + } + next.ServeHTTP(w, req) + }) } } @@ -636,7 +633,7 @@ const ( // RepoRef handles repository reference names when the ref name is not // explicitly given -func RepoRef() macaron.Handler { +func RepoRef() func(http.Handler) http.Handler { // since no ref name is explicitly specified, ok to just use branch return RepoRefByType(RepoRefBranch) } @@ -715,132 +712,135 @@ func getRefName(ctx *Context, pathType RepoRefType) string { // RepoRefByType handles repository reference name for a specific type // of repository reference -func RepoRefByType(refType RepoRefType) macaron.Handler { - return func(ctx *Context) { - // Empty repository does not have reference information. - if ctx.Repo.Repository.IsEmpty { - return - } - - var ( - refName string - err error - ) - - if ctx.Repo.GitRepo == nil { - repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - ctx.Repo.GitRepo, err = git.OpenRepository(repoPath) - if err != nil { - ctx.ServerError("RepoRef Invalid repo "+repoPath, err) +func RepoRefByType(refType RepoRefType) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := GetContext(req) + // Empty repository does not have reference information. + if ctx.Repo.Repository.IsEmpty { return } - // We opened it, we should close it - defer func() { - // If it's been set to nil then assume someone else has closed it. - if ctx.Repo.GitRepo != nil { - ctx.Repo.GitRepo.Close() - } - }() - } - // Get default branch. - if len(ctx.Params("*")) == 0 { - refName = ctx.Repo.Repository.DefaultBranch - ctx.Repo.BranchName = refName - if !ctx.Repo.GitRepo.IsBranchExist(refName) { - brs, err := ctx.Repo.GitRepo.GetBranches() + var ( + refName string + err error + ) + + if ctx.Repo.GitRepo == nil { + repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) + ctx.Repo.GitRepo, err = git.OpenRepository(repoPath) if err != nil { - ctx.ServerError("GetBranches", err) - return - } else if len(brs) == 0 { - err = fmt.Errorf("No branches in non-empty repository %s", - ctx.Repo.GitRepo.Path) - ctx.ServerError("GetBranches", err) + ctx.ServerError("RepoRef Invalid repo "+repoPath, err) return } - refName = brs[0] - } - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) - if err != nil { - ctx.ServerError("GetBranchCommit", err) - return + // We opened it, we should close it + defer func() { + // If it's been set to nil then assume someone else has closed it. + if ctx.Repo.GitRepo != nil { + ctx.Repo.GitRepo.Close() + } + }() } - ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - ctx.Repo.IsViewBranch = true - - } else { - refName = getRefName(ctx, refType) - ctx.Repo.BranchName = refName - if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) { - ctx.Repo.IsViewBranch = true + // Get default branch. + if len(ctx.Params("*")) == 0 { + refName = ctx.Repo.Repository.DefaultBranch + ctx.Repo.BranchName = refName + if !ctx.Repo.GitRepo.IsBranchExist(refName) { + brs, err := ctx.Repo.GitRepo.GetBranches() + if err != nil { + ctx.ServerError("GetBranches", err) + return + } else if len(brs) == 0 { + err = fmt.Errorf("No branches in non-empty repository %s", + ctx.Repo.GitRepo.Path) + ctx.ServerError("GetBranches", err) + return + } + refName = brs[0] + } ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) if err != nil { ctx.ServerError("GetBranchCommit", err) return } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() + ctx.Repo.IsViewBranch = true - } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) { - ctx.Repo.IsViewTag = true - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) - if err != nil { - ctx.ServerError("GetTagCommit", err) + } else { + refName = getRefName(ctx, refType) + ctx.Repo.BranchName = refName + if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) { + ctx.Repo.IsViewBranch = true + + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) + if err != nil { + ctx.ServerError("GetBranchCommit", err) + return + } + ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() + + } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) { + ctx.Repo.IsViewTag = true + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) + if err != nil { + ctx.ServerError("GetTagCommit", err) + return + } + ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() + } else if len(refName) >= 7 && len(refName) <= 40 { + ctx.Repo.IsViewCommit = true + ctx.Repo.CommitID = refName + + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) + if err != nil { + ctx.NotFound("GetCommit", err) + return + } + // If short commit ID add canonical link header + if len(refName) < 40 { + ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"", + util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1)))) + } + } else { + ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) return } - ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if len(refName) >= 7 && len(refName) <= 40 { - ctx.Repo.IsViewCommit = true - ctx.Repo.CommitID = refName - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) - if err != nil { - ctx.NotFound("GetCommit", err) + if refType == RepoRefLegacy { + // redirect from old URL scheme to new URL scheme + ctx.Redirect(path.Join( + setting.AppSubURL, + strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")), + ctx.Repo.BranchNameSubURL(), + ctx.Repo.TreePath)) return } - // If short commit ID add canonical link header - if len(refName) < 40 { - ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"", - util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1)))) - } - } else { - ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) - return } - if refType == RepoRefLegacy { - // redirect from old URL scheme to new URL scheme - ctx.Redirect(path.Join( - setting.AppSubURL, - strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")), - ctx.Repo.BranchNameSubURL(), - ctx.Repo.TreePath)) + ctx.Data["BranchName"] = ctx.Repo.BranchName + ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() + ctx.Data["CommitID"] = ctx.Repo.CommitID + ctx.Data["TreePath"] = ctx.Repo.TreePath + ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch + ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag + ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit + ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() + + ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() + if err != nil { + ctx.ServerError("GetCommitsCount", err) return } - } + ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount - ctx.Data["BranchName"] = ctx.Repo.BranchName - ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() - ctx.Data["CommitID"] = ctx.Repo.CommitID - ctx.Data["TreePath"] = ctx.Repo.TreePath - ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch - ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag - ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit - ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() - - ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() - if err != nil { - ctx.ServerError("GetCommitsCount", err) - return - } - ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount - - ctx.Next() + next.ServeHTTP(w, req) + }) } } // GitHookService checks if repository Git hooks service has been enabled. -func GitHookService() macaron.Handler { +func GitHookService() func(ctx *Context) { return func(ctx *Context) { if !ctx.User.CanEditGitHook() { ctx.NotFound("GitHookService", nil) @@ -850,7 +850,7 @@ func GitHookService() macaron.Handler { } // UnitTypes returns a macaron middleware to set unit types to context variables. -func UnitTypes() macaron.Handler { +func UnitTypes() func(ctx *Context) { return func(ctx *Context) { ctx.Data["UnitTypeCode"] = models.UnitTypeCode ctx.Data["UnitTypeIssues"] = models.UnitTypeIssues diff --git a/modules/context/response.go b/modules/context/response.go index 549bd30ee..1881ec7b3 100644 --- a/modules/context/response.go +++ b/modules/context/response.go @@ -11,6 +11,7 @@ type ResponseWriter interface { http.ResponseWriter Flush() Status() int + Before(func(ResponseWriter)) } var ( @@ -20,11 +21,19 @@ var ( // Response represents a response type Response struct { http.ResponseWriter - status int + status int + befores []func(ResponseWriter) + beforeExecuted bool } // Write writes bytes to HTTP endpoint func (r *Response) Write(bs []byte) (int, error) { + if !r.beforeExecuted { + for _, before := range r.befores { + before(r) + } + r.beforeExecuted = true + } size, err := r.ResponseWriter.Write(bs) if err != nil { return 0, err @@ -37,6 +46,12 @@ func (r *Response) Write(bs []byte) (int, error) { // WriteHeader write status code func (r *Response) WriteHeader(statusCode int) { + if !r.beforeExecuted { + for _, before := range r.befores { + before(r) + } + r.beforeExecuted = true + } r.status = statusCode r.ResponseWriter.WriteHeader(statusCode) } @@ -53,10 +68,20 @@ func (r *Response) Status() int { return r.status } +// 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. +func (r *Response) Before(f func(ResponseWriter)) { + r.befores = append(r.befores, f) +} + // NewResponse creates a response func NewResponse(resp http.ResponseWriter) *Response { if v, ok := resp.(*Response); ok { return v } - return &Response{resp, 0} + return &Response{ + ResponseWriter: resp, + status: 0, + befores: make([]func(ResponseWriter), 0), + } } diff --git a/modules/context/secret.go b/modules/context/secret.go new file mode 100644 index 000000000..fcb488d21 --- /dev/null +++ b/modules/context/secret.go @@ -0,0 +1,100 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "errors" + "io" +) + +// NewSecret creates a new secret +func NewSecret() (string, error) { + return NewSecretWithLength(32) +} + +// NewSecretWithLength creates a new secret for a given length +func NewSecretWithLength(length int64) (string, error) { + return randomString(length) +} + +func randomBytes(len int64) ([]byte, error) { + b := make([]byte, len) + if _, err := rand.Read(b); err != nil { + return nil, err + } + return b, nil +} + +func randomString(len int64) (string, error) { + b, err := randomBytes(len) + return base64.URLEncoding.EncodeToString(b), err +} + +// AesEncrypt encrypts text and given key with AES. +func AesEncrypt(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + b := base64.StdEncoding.EncodeToString(text) + ciphertext := make([]byte, aes.BlockSize+len(b)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) + return ciphertext, nil +} + +// AesDecrypt decrypts text and given key with AES. +func AesDecrypt(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + if len(text) < aes.BlockSize { + return nil, errors.New("ciphertext too short") + } + iv := text[:aes.BlockSize] + text = text[aes.BlockSize:] + cfb := cipher.NewCFBDecrypter(block, iv) + cfb.XORKeyStream(text, text) + data, err := base64.StdEncoding.DecodeString(string(text)) + if err != nil { + return nil, err + } + return data, nil +} + +// EncryptSecret encrypts a string with given key into a hex string +func EncryptSecret(key string, str string) (string, error) { + keyHash := sha256.Sum256([]byte(key)) + plaintext := []byte(str) + ciphertext, err := AesEncrypt(keyHash[:], plaintext) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +// DecryptSecret decrypts a previously encrypted hex string +func DecryptSecret(key string, cipherhex string) (string, error) { + keyHash := sha256.Sum256([]byte(key)) + ciphertext, err := base64.StdEncoding.DecodeString(cipherhex) + if err != nil { + return "", err + } + plaintext, err := AesDecrypt(keyHash[:], ciphertext) + if err != nil { + return "", err + } + return string(plaintext), nil +} diff --git a/vendor/gitea.com/macaron/csrf/xsrf.go b/modules/context/xsrf.go similarity index 90% rename from vendor/gitea.com/macaron/csrf/xsrf.go rename to modules/context/xsrf.go index 7f31894f9..10e63a418 100644 --- a/vendor/gitea.com/macaron/csrf/xsrf.go +++ b/modules/context/xsrf.go @@ -1,5 +1,6 @@ // Copyright 2012 Google Inc. All Rights Reserved. // Copyright 2014 The Macaron Authors +// Copyright 2020 The Gitea Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,7 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package csrf +package context import ( "bytes" @@ -27,13 +28,13 @@ import ( "time" ) -// The duration that XSRF tokens are valid. +// Timeout represents 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 +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) + return strings.ReplaceAll(s, ":", "_") } // GenerateToken returns a URL-safe secure XSRF token that expires in 24 hours. @@ -53,7 +54,7 @@ func generateTokenAtTime(key, userID, actionID string, now time.Time) string { return base64.RawURLEncoding.EncodeToString([]byte(tok)) } -// Valid returns true if token is a valid, unexpired token returned by Generate. +// ValidToken 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()) } @@ -78,7 +79,7 @@ func validTokenAtTime(token, key, userID, actionID string, now time.Time) bool { issueTime := time.Unix(0, nanos) // Check that the token is not expired. - if now.Sub(issueTime) >= TIMEOUT { + if now.Sub(issueTime) >= Timeout { return false } diff --git a/modules/context/xsrf_test.go b/modules/context/xsrf_test.go new file mode 100644 index 000000000..c0c711bf0 --- /dev/null +++ b/modules/context/xsrf_test.go @@ -0,0 +1,90 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Copyright 2014 The Macaron Authors +// Copyright 2020 The Gitea 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 context + +import ( + "encoding/base64" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +const ( + key = "quay" + userID = "12345678" + actionID = "POST /form" +) + +var ( + now = time.Now() + oneMinuteFromNow = now.Add(1 * time.Minute) +) + +func Test_ValidToken(t *testing.T) { + t.Run("Validate token", func(t *testing.T) { + tok := generateTokenAtTime(key, userID, actionID, now) + assert.True(t, validTokenAtTime(tok, key, userID, actionID, oneMinuteFromNow)) + assert.True(t, validTokenAtTime(tok, key, userID, actionID, now.Add(Timeout-1*time.Nanosecond))) + assert.True(t, validTokenAtTime(tok, key, userID, actionID, now.Add(-1*time.Minute))) + }) +} + +// Test_SeparatorReplacement tests that separators are being correctly substituted +func Test_SeparatorReplacement(t *testing.T) { + t.Run("Test two separator replacements", func(t *testing.T) { + assert.NotEqual(t, generateTokenAtTime("foo:bar", "baz", "wah", now), + generateTokenAtTime("foo", "bar:baz", "wah", now)) + }) +} + +func Test_InvalidToken(t *testing.T) { + t.Run("Test invalid tokens", func(t *testing.T) { + invalidTokenTests := []struct { + name, key, userID, actionID string + t time.Time + }{ + {"Bad key", "foobar", userID, actionID, oneMinuteFromNow}, + {"Bad userID", key, "foobar", actionID, oneMinuteFromNow}, + {"Bad actionID", key, userID, "foobar", oneMinuteFromNow}, + {"Expired", key, userID, actionID, now.Add(Timeout)}, + {"More than 1 minute from the future", key, userID, actionID, now.Add(-1*time.Nanosecond - 1*time.Minute)}, + } + + tok := generateTokenAtTime(key, userID, actionID, now) + for _, itt := range invalidTokenTests { + assert.False(t, validTokenAtTime(tok, itt.key, itt.userID, itt.actionID, itt.t)) + } + }) +} + +// Test_ValidateBadData primarily tests that no unexpected panics are triggered during parsing +func Test_ValidateBadData(t *testing.T) { + t.Run("Validate bad data", func(t *testing.T) { + badDataTests := []struct { + name, tok string + }{ + {"Invalid Base64", "ASDab24(@)$*=="}, + {"No delimiter", base64.URLEncoding.EncodeToString([]byte("foobar12345678"))}, + {"Invalid time", base64.URLEncoding.EncodeToString([]byte("foobar:foobar"))}, + } + + for _, bdt := range badDataTests { + assert.False(t, validTokenAtTime(bdt.tok, key, userID, actionID, oneMinuteFromNow)) + } + }) +} diff --git a/modules/auth/admin.go b/modules/forms/admin.go similarity index 70% rename from modules/auth/admin.go rename to modules/forms/admin.go index f2d026355..09ad420e1 100644 --- a/modules/auth/admin.go +++ b/modules/forms/admin.go @@ -2,11 +2,15 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" + + "gitea.com/go-chi/binding" ) // AdminCreateUserForm form for admin to create user @@ -21,8 +25,9 @@ type AdminCreateUserForm struct { } // Validate validates form fields -func (f *AdminCreateUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AdminEditUserForm form for admin to create user @@ -47,8 +52,9 @@ type AdminEditUserForm struct { } // Validate validates form fields -func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AdminDashboardForm form for admin dashboard operations @@ -58,6 +64,7 @@ type AdminDashboardForm struct { } // Validate validates form fields -func (f *AdminDashboardForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/auth_form.go b/modules/forms/auth_form.go similarity index 87% rename from modules/auth/auth_form.go rename to modules/forms/auth_form.go index e348b01e9..10d0f8295 100644 --- a/modules/auth/auth_form.go +++ b/modules/forms/auth_form.go @@ -2,11 +2,15 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" + + "gitea.com/go-chi/binding" ) // AuthenticationForm form for authentication @@ -65,6 +69,7 @@ type AuthenticationForm struct { } // Validate validates fields -func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/org.go b/modules/forms/org.go similarity index 76% rename from modules/auth/org.go rename to modules/forms/org.go index 20e2b0999..513f80768 100644 --- a/modules/auth/org.go +++ b/modules/forms/org.go @@ -3,14 +3,17 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( + "net/http" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/structs" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" ) // ________ .__ __ .__ @@ -28,8 +31,9 @@ type CreateOrgForm struct { } // Validate validates the fields -func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // UpdateOrgSettingForm form for updating organization settings @@ -45,8 +49,9 @@ type UpdateOrgSettingForm struct { } // Validate validates the fields -func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ___________ @@ -67,6 +72,7 @@ type CreateTeamForm struct { } // Validate validates the fields -func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/repo_branch_form.go b/modules/forms/repo_branch_form.go similarity index 52% rename from modules/auth/repo_branch_form.go rename to modules/forms/repo_branch_form.go index a4baabe35..afb7f8d4f 100644 --- a/modules/auth/repo_branch_form.go +++ b/modules/forms/repo_branch_form.go @@ -2,11 +2,15 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" + + "gitea.com/go-chi/binding" ) // NewBranchForm form for creating a new branch @@ -15,6 +19,7 @@ type NewBranchForm struct { } // Validate validates the fields -func (f *NewBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/repo_form.go b/modules/forms/repo_form.go similarity index 78% rename from modules/auth/repo_form.go rename to modules/forms/repo_form.go index 78b2197a2..4a478c7d3 100644 --- a/modules/auth/repo_form.go +++ b/modules/forms/repo_form.go @@ -3,21 +3,23 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( + "net/http" "net/url" "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/utils" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" ) // _______________________________________ _________.______________________ _______________.___. @@ -52,8 +54,9 @@ type CreateRepoForm struct { } // Validate validates the fields -func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // MigrateRepoForm form for migrating repository @@ -82,8 +85,9 @@ type MigrateRepoForm struct { } // Validate validates the fields -func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ParseRemoteAddr checks if given remote address is valid, @@ -166,8 +170,9 @@ type RepoSettingForm struct { } // Validate validates the fields -func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // __________ .__ @@ -202,8 +207,9 @@ type ProtectBranchForm struct { } // Validate validates the fields -func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // __ __ ___. .__ .__ __ @@ -263,8 +269,9 @@ type NewWebhookForm struct { } // Validate validates the fields -func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewGogshookForm form for creating gogs hook @@ -276,8 +283,9 @@ type NewGogshookForm struct { } // Validate validates the fields -func (f *NewGogshookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewSlackHookForm form for creating slack hook @@ -291,8 +299,9 @@ type NewSlackHookForm struct { } // Validate validates the fields -func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // HasInvalidChannel validates the channel name is in the right format @@ -309,8 +318,9 @@ type NewDiscordHookForm struct { } // Validate validates the fields -func (f *NewDiscordHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewDingtalkHookForm form for creating dingtalk hook @@ -320,8 +330,9 @@ type NewDingtalkHookForm struct { } // Validate validates the fields -func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewTelegramHookForm form for creating telegram hook @@ -332,8 +343,9 @@ type NewTelegramHookForm struct { } // Validate validates the fields -func (f *NewTelegramHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewMatrixHookForm form for creating Matrix hook @@ -346,8 +358,9 @@ type NewMatrixHookForm struct { } // Validate validates the fields -func (f *NewMatrixHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewMSTeamsHookForm form for creating MS Teams hook @@ -357,8 +370,9 @@ type NewMSTeamsHookForm struct { } // Validate validates the fields -func (f *NewMSTeamsHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewFeishuHookForm form for creating feishu hook @@ -368,8 +382,9 @@ type NewFeishuHookForm struct { } // Validate validates the fields -func (f *NewFeishuHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // .___ @@ -393,8 +408,9 @@ type CreateIssueForm struct { } // Validate validates the fields -func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // CreateCommentForm form for creating comment @@ -405,8 +421,9 @@ type CreateCommentForm struct { } // Validate validates the fields -func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ReactionForm form for adding and removing reaction @@ -415,8 +432,9 @@ type ReactionForm struct { } // Validate validates the fields -func (f *ReactionForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // IssueLockForm form for locking an issue @@ -425,8 +443,9 @@ type IssueLockForm struct { } // Validate validates the fields -func (i *IssueLockForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, i, ctx.Locale) +func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, i, ctx.Locale) } // HasValidReason checks to make sure that the reason submitted in @@ -489,8 +508,9 @@ type CreateMilestoneForm struct { } // Validate validates the fields -func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // .____ ___. .__ @@ -509,8 +529,9 @@ type CreateLabelForm struct { } // Validate validates the fields -func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // InitializeLabelsForm form for initializing labels @@ -519,8 +540,9 @@ type InitializeLabelsForm struct { } // Validate validates the fields -func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // __________ .__ .__ __________ __ @@ -542,8 +564,9 @@ type MergePullRequestForm struct { } // Validate validates the fields -func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // CodeCommentForm form for adding code comments for PRs @@ -559,8 +582,9 @@ type CodeCommentForm struct { } // Validate validates the fields -func (f *CodeCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // SubmitReviewForm for submitting a finished code review @@ -571,8 +595,9 @@ type SubmitReviewForm struct { } // Validate validates the fields -func (f *SubmitReviewForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ReviewType will return the corresponding reviewtype for type @@ -616,8 +641,9 @@ type NewReleaseForm struct { } // Validate validates the fields -func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // EditReleaseForm form for changing release @@ -630,8 +656,9 @@ type EditReleaseForm struct { } // Validate validates the fields -func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // __ __.__ __ .__ @@ -650,8 +677,9 @@ type NewWikiForm struct { // Validate validates the fields // FIXME: use code generation to generate this method. -func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ___________ .___.__ __ @@ -673,8 +701,9 @@ type EditRepoFileForm struct { } // Validate validates the fields -func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // EditPreviewDiffForm form for changing preview diff @@ -683,8 +712,9 @@ type EditPreviewDiffForm struct { } // Validate validates the fields -func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ____ ___ .__ .___ @@ -706,8 +736,9 @@ type UploadRepoFileForm struct { } // Validate validates the fields -func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // RemoveUploadFileForm form for removing uploaded file @@ -716,8 +747,9 @@ type RemoveUploadFileForm struct { } // Validate validates the fields -func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ________ .__ __ @@ -737,8 +769,9 @@ type DeleteRepoFileForm struct { } // Validate validates the fields -func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ___________.__ ___________ __ @@ -755,8 +788,9 @@ type AddTimeManuallyForm struct { } // Validate validates the fields -func (f *AddTimeManuallyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // SaveTopicForm form for save topics for repository @@ -770,6 +804,7 @@ type DeadlineForm struct { } // Validate validates the fields -func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/repo_form_test.go b/modules/forms/repo_form_test.go similarity index 99% rename from modules/auth/repo_form_test.go rename to modules/forms/repo_form_test.go index 6bad5d50b..4f65d59ca 100644 --- a/modules/auth/repo_form_test.go +++ b/modules/forms/repo_form_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( "testing" diff --git a/modules/auth/user_form.go b/modules/forms/user_form.go similarity index 71% rename from modules/auth/user_form.go rename to modules/forms/user_form.go index b94b8e0a4..e3090f9ae 100644 --- a/modules/auth/user_form.go +++ b/modules/forms/user_form.go @@ -3,16 +3,18 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( "mime/multipart" + "net/http" "strings" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" ) // InstallForm form for installation page @@ -65,8 +67,9 @@ type InstallForm struct { } // Validate validates the fields -func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // _____ ____ _________________ ___ @@ -87,8 +90,9 @@ type RegisterForm struct { } // Validate validates the fields -func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // IsEmailDomainWhitelisted validates that the email address @@ -124,8 +128,9 @@ type MustChangePasswordForm struct { } // Validate validates the fields -func (f *MustChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // SignInForm form for signing in with user/password @@ -137,8 +142,9 @@ type SignInForm struct { } // Validate validates the fields -func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AuthorizationForm form for authorizing oauth2 clients @@ -156,8 +162,9 @@ type AuthorizationForm struct { } // Validate validates the fields -func (f *AuthorizationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // GrantApplicationForm form for authorizing oauth2 clients @@ -170,8 +177,9 @@ type GrantApplicationForm struct { } // Validate validates the fields -func (f *GrantApplicationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AccessTokenForm for issuing access tokens from authorization codes or refresh tokens @@ -188,8 +196,9 @@ type AccessTokenForm struct { } // Validate validates the fields -func (f *AccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // __________________________________________.___ _______ ________ _________ @@ -212,8 +221,9 @@ type UpdateProfileForm struct { } // Validate validates the fields -func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // Avatar types @@ -231,8 +241,9 @@ type AvatarForm struct { } // Validate validates the fields -func (f *AvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AddEmailForm form for adding new email @@ -241,8 +252,9 @@ type AddEmailForm struct { } // Validate validates the fields -func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // UpdateThemeForm form for updating a users' theme @@ -251,8 +263,9 @@ type UpdateThemeForm struct { } // Validate validates the field -func (f *UpdateThemeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // IsThemeExists checks if the theme is a theme available in the config. @@ -277,8 +290,9 @@ type ChangePasswordForm struct { } // Validate validates the fields -func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AddOpenIDForm is for changing openid uri @@ -287,8 +301,9 @@ type AddOpenIDForm struct { } // Validate validates the fields -func (f *AddOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // AddKeyForm form for adding SSH/GPG key @@ -300,8 +315,9 @@ type AddKeyForm struct { } // Validate validates the fields -func (f *AddKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // NewAccessTokenForm form for creating access token @@ -310,8 +326,9 @@ type NewAccessTokenForm struct { } // Validate validates the fields -func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // EditOAuth2ApplicationForm form for editing oauth2 applications @@ -321,8 +338,9 @@ type EditOAuth2ApplicationForm struct { } // Validate validates the fields -func (f *EditOAuth2ApplicationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // TwoFactorAuthForm for logging in with 2FA token. @@ -331,8 +349,9 @@ type TwoFactorAuthForm struct { } // Validate validates the fields -func (f *TwoFactorAuthForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // TwoFactorScratchAuthForm for logging in with 2FA scratch token. @@ -341,8 +360,9 @@ type TwoFactorScratchAuthForm struct { } // Validate validates the fields -func (f *TwoFactorScratchAuthForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // U2FRegistrationForm for reserving an U2F name @@ -351,8 +371,9 @@ type U2FRegistrationForm struct { } // Validate validates the fields -func (f *U2FRegistrationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *U2FRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // U2FDeleteForm for deleting U2F keys @@ -361,6 +382,7 @@ type U2FDeleteForm struct { } // Validate validates the fields -func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *U2FDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/user_form_auth_openid.go b/modules/forms/user_form_auth_openid.go similarity index 58% rename from modules/auth/user_form_auth_openid.go rename to modules/forms/user_form_auth_openid.go index 841dbd840..06601d7e1 100644 --- a/modules/auth/user_form_auth_openid.go +++ b/modules/forms/user_form_auth_openid.go @@ -2,11 +2,14 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" + "gitea.com/go-chi/binding" ) // SignInOpenIDForm form for signing in with OpenID @@ -16,8 +19,9 @@ type SignInOpenIDForm struct { } // Validate validates the fields -func (f *SignInOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // SignUpOpenIDForm form for signin up with OpenID @@ -29,8 +33,9 @@ type SignUpOpenIDForm struct { } // Validate validates the fields -func (f *SignUpOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } // ConnectOpenIDForm form for connecting an existing account to an OpenID URI @@ -40,6 +45,7 @@ type ConnectOpenIDForm struct { } // Validate validates the fields -func (f *ConnectOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) +func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middlewares.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/modules/auth/user_form_test.go b/modules/forms/user_form_test.go similarity index 98% rename from modules/auth/user_form_test.go rename to modules/forms/user_form_test.go index 084174622..6e0518789 100644 --- a/modules/auth/user_form_test.go +++ b/modules/forms/user_form_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package forms import ( "testing" diff --git a/modules/lfs/locks.go b/modules/lfs/locks.go index a529afe1b..cf62492c7 100644 --- a/modules/lfs/locks.go +++ b/modules/lfs/locks.go @@ -182,7 +182,7 @@ func PostLockHandler(ctx *context.Context) { } var req api.LFSLockRequest - bodyReader := ctx.Req.Body().ReadCloser() + bodyReader := ctx.Req.Body defer bodyReader.Close() dec := json.NewDecoder(bodyReader) if err := dec.Decode(&req); err != nil { @@ -317,7 +317,7 @@ func UnLockHandler(ctx *context.Context) { } var req api.LFSLockDeleteRequest - bodyReader := ctx.Req.Body().ReadCloser() + bodyReader := ctx.Req.Body defer bodyReader.Close() dec := json.NewDecoder(bodyReader) if err := dec.Decode(&req); err != nil { diff --git a/modules/lfs/server.go b/modules/lfs/server.go index 226bcbf55..be21a4de8 100644 --- a/modules/lfs/server.go +++ b/modules/lfs/server.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" - "gitea.com/macaron/macaron" "github.com/dgrijalva/jwt-go" ) @@ -413,8 +412,8 @@ func PutHandler(ctx *context.Context) { } contentStore := &ContentStore{ObjectStorage: storage.LFS} - defer ctx.Req.Request.Body.Close() - if err := contentStore.Put(meta, ctx.Req.Request.Body); err != nil { + defer ctx.Req.Body.Close() + if err := contentStore.Put(meta, ctx.Req.Body); err != nil { // Put will log the error itself ctx.Resp.WriteHeader(500) if err == errSizeMismatch || err == errHashMismatch { @@ -513,7 +512,7 @@ func Represent(rv *RequestVars, meta *models.LFSMetaObject, download, upload boo // MetaMatcher provides a mux.MatcherFunc that only allows requests that contain // an Accept header with the metaMediaType -func MetaMatcher(r macaron.Request) bool { +func MetaMatcher(r *http.Request) bool { mediaParts := strings.Split(r.Header.Get("Accept"), ";") mt := mediaParts[0] return mt == metaMediaType @@ -530,7 +529,7 @@ func unpack(ctx *context.Context) *RequestVars { if r.Method == "POST" { // Maybe also check if +json var p RequestVars - bodyReader := r.Body().ReadCloser() + bodyReader := r.Body defer bodyReader.Close() dec := json.NewDecoder(bodyReader) err := dec.Decode(&p) @@ -553,7 +552,7 @@ func unpackbatch(ctx *context.Context) *BatchVars { r := ctx.Req var bv BatchVars - bodyReader := r.Body().ReadCloser() + bodyReader := r.Body defer bodyReader.Close() dec := json.NewDecoder(bodyReader) err := dec.Decode(&bv) @@ -586,7 +585,7 @@ func writeStatus(ctx *context.Context, status int) { logRequest(ctx.Req, status) } -func logRequest(r macaron.Request, status int) { +func logRequest(r *http.Request, status int) { log.Debug("LFS request - Method: %s, URL: %s, Status %d", r.Method, r.URL, status) } diff --git a/modules/auth/auth.go b/modules/middlewares/binding.go similarity index 92% rename from modules/auth/auth.go rename to modules/middlewares/binding.go index 1f4b9ec5b..1dabdbb62 100644 --- a/modules/auth/auth.go +++ b/modules/middlewares/binding.go @@ -3,24 +3,19 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package auth +package middlewares import ( "reflect" "strings" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/validation" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" "github.com/unknwon/com" ) -// IsAPIPath if URL is an api path -func IsAPIPath(url string) bool { - return strings.HasPrefix(url, "/api/") -} - // Form form binding interface type Form interface { binding.Validator @@ -35,7 +30,7 @@ func AssignForm(form interface{}, data map[string]interface{}) { typ := reflect.TypeOf(form) val := reflect.ValueOf(form) - if typ.Kind() == reflect.Ptr { + for typ.Kind() == reflect.Ptr { typ = typ.Elem() val = val.Elem() } @@ -84,7 +79,8 @@ func GetInclude(field reflect.StructField) string { return getRuleBody(field, "Include(") } -func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors { +// Validate validate TODO: +func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors { if errs.Len() == 0 { return errs } diff --git a/modules/middlewares/cookie.go b/modules/middlewares/cookie.go index 80d0e3b45..d18541833 100644 --- a/modules/middlewares/cookie.go +++ b/modules/middlewares/cookie.go @@ -1,3 +1,4 @@ +// Copyright 2020 The Macaron Authors // Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -12,6 +13,56 @@ import ( "code.gitea.io/gitea/modules/setting" ) +// MaxAge sets the maximum age for a provided cookie +func MaxAge(maxAge int) func(*http.Cookie) { + return func(c *http.Cookie) { + c.MaxAge = maxAge + } +} + +// Path sets the path for a provided cookie +func Path(path string) func(*http.Cookie) { + return func(c *http.Cookie) { + c.Path = path + } +} + +// Domain sets the domain for a provided cookie +func Domain(domain string) func(*http.Cookie) { + return func(c *http.Cookie) { + c.Domain = domain + } +} + +// Secure sets the secure setting for a provided cookie +func Secure(secure bool) func(*http.Cookie) { + return func(c *http.Cookie) { + c.Secure = secure + } +} + +// HTTPOnly sets the HttpOnly setting for a provided cookie +func HTTPOnly(httpOnly bool) func(*http.Cookie) { + return func(c *http.Cookie) { + c.HttpOnly = httpOnly + } +} + +// Expires sets the expires and rawexpires for a provided cookie +func Expires(expires time.Time) func(*http.Cookie) { + return func(c *http.Cookie) { + c.Expires = expires + c.RawExpires = expires.Format(time.UnixDate) + } +} + +// SameSite sets the SameSite for a provided cookie +func SameSite(sameSite http.SameSite) func(*http.Cookie) { + return func(c *http.Cookie) { + c.SameSite = sameSite + } +} + // NewCookie creates a cookie func NewCookie(name, value string, maxAge int) *http.Cookie { return &http.Cookie{ @@ -102,3 +153,13 @@ func SetCookie(resp http.ResponseWriter, name string, value string, others ...in resp.Header().Add("Set-Cookie", cookie.String()) } + +// GetCookie returns given cookie value from request header. +func GetCookie(req *http.Request, name string) string { + cookie, err := req.Cookie(name) + if err != nil { + return "" + } + val, _ := url.QueryUnescape(cookie.Value) + return val +} diff --git a/modules/middlewares/data.go b/modules/middlewares/data.go new file mode 100644 index 000000000..269028936 --- /dev/null +++ b/modules/middlewares/data.go @@ -0,0 +1,10 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package middlewares + +// DataStore represents a data store +type DataStore interface { + GetData() map[string]interface{} +} diff --git a/modules/middlewares/flash.go b/modules/middlewares/flash.go new file mode 100644 index 000000000..38217288e --- /dev/null +++ b/modules/middlewares/flash.go @@ -0,0 +1,65 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package middlewares + +import "net/url" + +// flashes enumerates all the flash types +const ( + SuccessFlash = "SuccessMsg" + ErrorFlash = "ErrorMsg" + WarnFlash = "WarningMsg" + InfoFlash = "InfoMsg" +) + +var ( + // FlashNow FIXME: + FlashNow bool +) + +// Flash represents a one time data transfer between two requests. +type Flash struct { + DataStore + url.Values + ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string +} + +func (f *Flash) set(name, msg string, current ...bool) { + isShow := false + if (len(current) == 0 && FlashNow) || + (len(current) > 0 && current[0]) { + isShow = true + } + + if isShow { + f.GetData()["Flash"] = f + } else { + f.Set(name, msg) + } +} + +// Error sets error message +func (f *Flash) Error(msg string, current ...bool) { + f.ErrorMsg = msg + f.set("error", msg, current...) +} + +// Warning sets warning message +func (f *Flash) Warning(msg string, current ...bool) { + f.WarningMsg = msg + f.set("warning", msg, current...) +} + +// Info sets info message +func (f *Flash) Info(msg string, current ...bool) { + f.InfoMsg = msg + f.set("info", msg, current...) +} + +// Success sets success message +func (f *Flash) Success(msg string, current ...bool) { + f.SuccessMsg = msg + f.set("success", msg, current...) +} diff --git a/modules/middlewares/locale.go b/modules/middlewares/locale.go index 98af890cf..7cfba81bd 100644 --- a/modules/middlewares/locale.go +++ b/modules/middlewares/locale.go @@ -23,12 +23,14 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale { // 2. Get language information from cookies. if len(lang) == 0 { ck, _ := req.Cookie("lang") - lang = ck.Value - hasCookie = true + if ck != nil { + lang = ck.Value + hasCookie = true + } } // Check again in case someone modify by purpose. - if !i18n.IsExist(lang) { + if lang != "" && !i18n.IsExist(lang) { lang = "" hasCookie = false } diff --git a/modules/middlewares/redis.go b/modules/middlewares/redis.go deleted file mode 100644 index ced1c1ee8..000000000 --- a/modules/middlewares/redis.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2013 Beego Authors -// Copyright 2014 The Macaron Authors -// Copyright 2020 The Gitea Authors. All rights reserved. -// -// 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 middlewares - -import ( - "fmt" - "sync" - "time" - - "code.gitea.io/gitea/modules/nosql" - - "gitea.com/go-chi/session" - "github.com/go-redis/redis/v7" -) - -// RedisStore represents a redis session store implementation. -// TODO: copied from modules/session/redis.go and should remove that one until macaron removed. -type RedisStore struct { - c redis.UniversalClient - 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.UniversalClient, 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.UniversalClient - 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 - } - - uri := nosql.ToRedisURI(configs) - - for k, v := range uri.Query() { - switch k { - case "prefix": - p.prefix = v[0] - } - } - - p.c = nosql.GetManager().GetRedisClient(uri.String()) - 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/modules/middlewares/virtual.go b/modules/middlewares/virtual.go deleted file mode 100644 index 70d780d65..000000000 --- a/modules/middlewares/virtual.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package middlewares - -import ( - "encoding/json" - "fmt" - "sync" - - "gitea.com/go-chi/session" - couchbase "gitea.com/go-chi/session/couchbase" - memcache "gitea.com/go-chi/session/memcache" - mysql "gitea.com/go-chi/session/mysql" - postgres "gitea.com/go-chi/session/postgres" -) - -// VirtualSessionProvider represents a shadowed session provider implementation. -// TODO: copied from modules/session/redis.go and should remove that one until macaron removed. -type VirtualSessionProvider struct { - lock sync.RWMutex - provider session.Provider -} - -// Init initializes the cookie session provider with given root path. -func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error { - var opts session.Options - if err := json.Unmarshal([]byte(config), &opts); err != nil { - return err - } - // Note that these options are unprepared so we can't just use NewManager here. - // Nor can we access the provider map in session. - // So we will just have to do this by hand. - // This is only slightly more wrong than modules/setting/session.go:23 - switch opts.Provider { - case "memory": - o.provider = &session.MemProvider{} - case "file": - o.provider = &session.FileProvider{} - case "redis": - o.provider = &RedisProvider{} - case "mysql": - o.provider = &mysql.MysqlProvider{} - case "postgres": - o.provider = &postgres.PostgresProvider{} - case "couchbase": - o.provider = &couchbase.CouchbaseProvider{} - case "memcache": - o.provider = &memcache.MemcacheProvider{} - default: - return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider) - } - return o.provider.Init(gclifetime, opts.ProviderConfig) -} - -// Read returns raw session store by session ID. -func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) { - o.lock.RLock() - defer o.lock.RUnlock() - if o.provider.Exist(sid) { - return o.provider.Read(sid) - } - kv := make(map[interface{}]interface{}) - kv["_old_uid"] = "0" - return NewVirtualStore(o, sid, kv), nil -} - -// Exist returns true if session with given ID exists. -func (o *VirtualSessionProvider) Exist(sid string) bool { - return true -} - -// Destroy deletes a session by session ID. -func (o *VirtualSessionProvider) Destroy(sid string) error { - o.lock.Lock() - defer o.lock.Unlock() - return o.provider.Destroy(sid) -} - -// Regenerate regenerates a session store from old session ID to new one. -func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - o.lock.Lock() - defer o.lock.Unlock() - return o.provider.Regenerate(oldsid, sid) -} - -// Count counts and returns number of sessions. -func (o *VirtualSessionProvider) Count() int { - o.lock.RLock() - defer o.lock.RUnlock() - return o.provider.Count() -} - -// GC calls GC to clean expired sessions. -func (o *VirtualSessionProvider) GC() { - o.provider.GC() -} - -func init() { - session.Register("VirtualSession", &VirtualSessionProvider{}) -} - -// VirtualStore represents a virtual session store implementation. -type VirtualStore struct { - p *VirtualSessionProvider - sid string - lock sync.RWMutex - data map[interface{}]interface{} - released bool -} - -// NewVirtualStore creates and returns a virtual session store. -func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore { - return &VirtualStore{ - p: p, - sid: sid, - data: kv, - } -} - -// Set sets value to given key in session. -func (s *VirtualStore) 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 *VirtualStore) Get(key interface{}) interface{} { - s.lock.RLock() - defer s.lock.RUnlock() - - return s.data[key] -} - -// Delete delete a key from session. -func (s *VirtualStore) Delete(key interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - delete(s.data, key) - return nil -} - -// ID returns current session ID. -func (s *VirtualStore) ID() string { - return s.sid -} - -// Release releases resource and save data to provider. -func (s *VirtualStore) Release() error { - s.lock.Lock() - defer s.lock.Unlock() - // Now need to lock the provider - s.p.lock.Lock() - defer s.p.lock.Unlock() - if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) { - // Now ensure that we don't exist! - realProvider := s.p.provider - - if !s.released && realProvider.Exist(s.sid) { - // This is an error! - return fmt.Errorf("new sid '%s' already exists", s.sid) - } - realStore, err := realProvider.Read(s.sid) - if err != nil { - return err - } - if err := realStore.Flush(); err != nil { - return err - } - for key, value := range s.data { - if err := realStore.Set(key, value); err != nil { - return err - } - } - err = realStore.Release() - if err == nil { - s.released = true - } - return err - } - return nil -} - -// Flush deletes all session data. -func (s *VirtualStore) Flush() error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = make(map[interface{}]interface{}) - return nil -} diff --git a/modules/session/redis.go b/modules/session/redis.go index c88ebd576..55e7a8516 100644 --- a/modules/session/redis.go +++ b/modules/session/redis.go @@ -23,7 +23,7 @@ import ( "code.gitea.io/gitea/modules/nosql" - "gitea.com/macaron/session" + "gitea.com/go-chi/session" "github.com/go-redis/redis/v7" ) diff --git a/modules/session/store.go b/modules/session/store.go new file mode 100644 index 000000000..529187d3b --- /dev/null +++ b/modules/session/store.go @@ -0,0 +1,12 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package session + +// Store represents a session store +type Store interface { + Get(interface{}) interface{} + Set(interface{}, interface{}) error + Delete(interface{}) error +} diff --git a/modules/session/virtual.go b/modules/session/virtual.go index 1139cfe89..3da499d71 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -9,12 +9,11 @@ import ( "fmt" "sync" - "gitea.com/macaron/session" - couchbase "gitea.com/macaron/session/couchbase" - memcache "gitea.com/macaron/session/memcache" - mysql "gitea.com/macaron/session/mysql" - nodb "gitea.com/macaron/session/nodb" - postgres "gitea.com/macaron/session/postgres" + "gitea.com/go-chi/session" + couchbase "gitea.com/go-chi/session/couchbase" + memcache "gitea.com/go-chi/session/memcache" + mysql "gitea.com/go-chi/session/mysql" + postgres "gitea.com/go-chi/session/postgres" ) // VirtualSessionProvider represents a shadowed session provider implementation. @@ -48,8 +47,6 @@ func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error { o.provider = &couchbase.CouchbaseProvider{} case "memcache": o.provider = &memcache.MemcacheProvider{} - case "nodb": - o.provider = &nodb.NodbProvider{} default: return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider) } diff --git a/modules/setting/log.go b/modules/setting/log.go index 35bf021ac..daa449a5c 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -254,17 +254,6 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription return &description } -func newMacaronLogService() { - options := newDefaultLogOptions() - options.filename = filepath.Join(LogRootPath, "macaron.log") - options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000) - - Cfg.Section("log").Key("MACARON").MustString("file") - if RedirectMacaronLog { - generateNamedLogger("macaron", options) - } -} - func newAccessLogService() { EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false) AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString( @@ -360,7 +349,6 @@ func RestartLogsWithPIDSuffix() { // NewLogServices creates all the log services func NewLogServices(disableConsole bool) { newLogService() - newMacaronLogService() newRouterLogService() newAccessLogService() NewXORMLogService(disableConsole) diff --git a/modules/setting/session.go b/modules/setting/session.go index bd51c420a..222c246e1 100644 --- a/modules/setting/session.go +++ b/modules/setting/session.go @@ -41,7 +41,7 @@ var ( func newSessionService() { sec := Cfg.Section("session") SessionConfig.Provider = sec.Key("PROVIDER").In("memory", - []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "nodb"}) + []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache"}) SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ") if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) { SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig) diff --git a/modules/templates/base.go b/modules/templates/base.go index a9b6b2737..ff31c1289 100644 --- a/modules/templates/base.go +++ b/modules/templates/base.go @@ -12,6 +12,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + + "github.com/unrolled/render" ) // Vars represents variables to be render in golang templates @@ -80,3 +82,15 @@ func getDirAssetNames(dir string) []string { } return tmpls } + +// HTMLRenderer returns a render. +func HTMLRenderer() *render.Render { + return render.New(render.Options{ + Extensions: []string{".tmpl"}, + Directory: "templates", + Funcs: NewFuncMap(), + Asset: GetAsset, + AssetNames: GetAssetNames, + IsDevelopment: !setting.IsProd(), + }) +} diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index f7f05e9b7..160e4e05f 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -18,8 +18,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - - "gitea.com/macaron/macaron" ) var ( @@ -46,29 +44,6 @@ func GetAssetNames() []string { return append(tmpls, tmpls2...) } -// HTMLRenderer implements the macaron handler for serving HTML templates. -func HTMLRenderer() macaron.Handler { - return macaron.Renderer(macaron.RenderOptions{ - Funcs: NewFuncMap(), - Directory: path.Join(setting.StaticRootPath, "templates"), - AppendDirectories: []string{ - path.Join(setting.CustomPath, "templates"), - }, - }) -} - -// JSONRenderer implements the macaron handler for serving JSON templates. -func JSONRenderer() macaron.Handler { - return macaron.Renderer(macaron.RenderOptions{ - Funcs: NewFuncMap(), - Directory: path.Join(setting.StaticRootPath, "templates"), - AppendDirectories: []string{ - path.Join(setting.CustomPath, "templates"), - }, - HTMLContentType: "application/json", - }) -} - // Mailer provides the templates required for sending notification mails. func Mailer() (*texttmpl.Template, *template.Template) { for _, funcs := range NewTextFuncMap() { diff --git a/modules/templates/static.go b/modules/templates/static.go index 1dd3d217f..7f95d77ad 100644 --- a/modules/templates/static.go +++ b/modules/templates/static.go @@ -7,10 +7,7 @@ package templates import ( - "bytes" - "fmt" "html/template" - "io" "io/ioutil" "os" "path" @@ -21,8 +18,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - - "gitea.com/macaron/macaron" ) var ( @@ -30,24 +25,6 @@ var ( bodyTemplates = template.New("") ) -type templateFileSystem struct { - files []macaron.TemplateFile -} - -func (templates templateFileSystem) ListFiles() []macaron.TemplateFile { - return templates.files -} - -func (templates templateFileSystem) Get(name string) (io.Reader, error) { - for i := range templates.files { - if templates.files[i].Name()+templates.files[i].Ext() == name { - return bytes.NewReader(templates.files[i].Data()), nil - } - } - - return nil, fmt.Errorf("file '%s' not found", name) -} - // GetAsset get a special asset, only for chi func GetAsset(name string) ([]byte, error) { bs, err := ioutil.ReadFile(filepath.Join(setting.CustomPath, name)) @@ -72,95 +49,6 @@ func GetAssetNames() []string { return append(tmpls, customTmpls...) } -func NewTemplateFileSystem() templateFileSystem { - fs := templateFileSystem{} - fs.files = make([]macaron.TemplateFile, 0, 10) - - for _, assetPath := range AssetNames() { - if strings.HasPrefix(assetPath, "mail/") { - continue - } - - if !strings.HasSuffix(assetPath, ".tmpl") { - continue - } - - content, err := Asset(assetPath) - - if err != nil { - log.Warn("Failed to read embedded %s template. %v", assetPath, err) - continue - } - - fs.files = append(fs.files, macaron.NewTplFile( - strings.TrimSuffix( - assetPath, - ".tmpl", - ), - content, - ".tmpl", - )) - } - - customDir := path.Join(setting.CustomPath, "templates") - isDir, err := util.IsDir(customDir) - if err != nil { - log.Warn("Unable to check if templates dir %s is a directory. Error: %v", customDir, err) - } - if isDir { - files, err := util.StatDir(customDir) - - if err != nil { - log.Warn("Failed to read %s templates dir. %v", customDir, err) - } else { - for _, filePath := range files { - if strings.HasPrefix(filePath, "mail/") { - continue - } - - if !strings.HasSuffix(filePath, ".tmpl") { - continue - } - - content, err := ioutil.ReadFile(path.Join(customDir, filePath)) - - if err != nil { - log.Warn("Failed to read custom %s template. %v", filePath, err) - continue - } - - fs.files = append(fs.files, macaron.NewTplFile( - strings.TrimSuffix( - filePath, - ".tmpl", - ), - content, - ".tmpl", - )) - } - } - } - - return fs -} - -// HTMLRenderer implements the macaron handler for serving HTML templates. -func HTMLRenderer() macaron.Handler { - return macaron.Renderer(macaron.RenderOptions{ - Funcs: NewFuncMap(), - TemplateFileSystem: NewTemplateFileSystem(), - }) -} - -// JSONRenderer implements the macaron handler for serving JSON templates. -func JSONRenderer() macaron.Handler { - return macaron.Renderer(macaron.RenderOptions{ - Funcs: NewFuncMap(), - TemplateFileSystem: NewTemplateFileSystem(), - HTMLContentType: "application/json", - }) -} - // Mailer provides the templates required for sending notification mails. func Mailer() (*texttmpl.Template, *template.Template) { for _, funcs := range NewTextFuncMap() { diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index 874d7db19..e219a8e56 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -5,6 +5,9 @@ package test import ( + scontext "context" + "html/template" + "io" "net/http" "net/http/httptest" "net/url" @@ -13,32 +16,37 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/middlewares" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "github.com/go-chi/chi" "github.com/stretchr/testify/assert" + "github.com/unrolled/render" ) // MockContext mock context for unit tests func MockContext(t *testing.T, path string) *context.Context { - var macaronContext macaron.Context - macaronContext.ReplaceAllParams(macaron.Params{}) - macaronContext.Locale = &mockLocale{} + var resp = &mockResponseWriter{} + var ctx = context.Context{ + Render: &mockRender{}, + Data: make(map[string]interface{}), + Flash: &middlewares.Flash{ + Values: make(url.Values), + }, + Resp: context.NewResponse(resp), + Locale: &mockLocale{}, + } + requestURL, err := url.Parse(path) assert.NoError(t, err) - macaronContext.Req = macaron.Request{Request: &http.Request{ + var req = &http.Request{ URL: requestURL, Form: url.Values{}, - }} - macaronContext.Resp = &mockResponseWriter{} - macaronContext.Render = &mockRender{ResponseWriter: macaronContext.Resp} - macaronContext.Data = map[string]interface{}{} - return &context.Context{ - Context: &macaronContext, - Flash: &session.Flash{ - Values: make(url.Values), - }, } + + chiCtx := chi.NewRouteContext() + req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx)) + ctx.Req = context.WithContext(req, &ctx) + return &ctx } // LoadRepo load a repo into a test context. @@ -113,77 +121,20 @@ func (rw *mockResponseWriter) Size() int { return rw.size } -func (rw *mockResponseWriter) Before(b macaron.BeforeFunc) { - b(rw) -} - func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error { return nil } type mockRender struct { - http.ResponseWriter -} - -func (tr *mockRender) SetResponseWriter(rw http.ResponseWriter) { - tr.ResponseWriter = rw -} - -func (tr *mockRender) JSON(status int, _ interface{}) { - tr.Status(status) -} - -func (tr *mockRender) JSONString(interface{}) (string, error) { - return "", nil -} - -func (tr *mockRender) RawData(status int, _ []byte) { - tr.Status(status) -} - -func (tr *mockRender) PlainText(status int, _ []byte) { - tr.Status(status) -} - -func (tr *mockRender) HTML(status int, _ string, _ interface{}, _ ...macaron.HTMLOptions) { - tr.Status(status) -} - -func (tr *mockRender) HTMLSet(status int, _ string, _ string, _ interface{}, _ ...macaron.HTMLOptions) { - tr.Status(status) -} - -func (tr *mockRender) HTMLSetString(string, string, interface{}, ...macaron.HTMLOptions) (string, error) { - return "", nil -} - -func (tr *mockRender) HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error) { - return "", nil -} - -func (tr *mockRender) HTMLSetBytes(string, string, interface{}, ...macaron.HTMLOptions) ([]byte, error) { - return nil, nil -} - -func (tr *mockRender) HTMLBytes(string, interface{}, ...macaron.HTMLOptions) ([]byte, error) { - return nil, nil -} - -func (tr *mockRender) XML(status int, _ interface{}) { - tr.Status(status) -} - -func (tr *mockRender) Error(status int, _ ...string) { - tr.Status(status) } -func (tr *mockRender) Status(status int) { - tr.ResponseWriter.WriteHeader(status) -} - -func (tr *mockRender) SetTemplatePath(string, string) { +func (tr *mockRender) TemplateLookup(tmpl string) *template.Template { + return nil } -func (tr *mockRender) HasTemplateSet(string) bool { - return true +func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}, _ ...render.HTMLOptions) error { + if resp, ok := w.(http.ResponseWriter); ok { + resp.WriteHeader(status) + } + return nil } diff --git a/modules/timeutil/since_test.go b/modules/timeutil/since_test.go index 65d481a6a..5710e91db 100644 --- a/modules/timeutil/since_test.go +++ b/modules/timeutil/since_test.go @@ -10,8 +10,8 @@ import ( "time" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" - macaroni18n "gitea.com/macaron/i18n" "github.com/stretchr/testify/assert" "github.com/unknwon/i18n" ) @@ -27,13 +27,11 @@ const ( ) func TestMain(m *testing.M) { + setting.StaticRootPath = "../../" + setting.Names = []string{"english"} + setting.Langs = []string{"en-US"} // setup - macaroni18n.I18n(macaroni18n.Options{ - Directory: "../../options/locale/", - DefaultLang: "en-US", - Langs: []string{"en-US"}, - Names: []string{"english"}, - }) + translation.InitLocales() BaseDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) // run the tests diff --git a/modules/translation/translation.go b/modules/translation/translation.go index e39bf8b21..94a93a40a 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -9,7 +9,6 @@ import ( "code.gitea.io/gitea/modules/options" "code.gitea.io/gitea/modules/setting" - macaron_i18n "gitea.com/macaron/i18n" "github.com/unknwon/i18n" "golang.org/x/text/language" ) @@ -20,49 +19,57 @@ type Locale interface { Tr(string, ...interface{}) string } +// LangType represents a lang type +type LangType struct { + Lang, Name string +} + var ( - matcher language.Matcher + matcher language.Matcher + allLangs []LangType ) +// AllLangs returns all supported langauages +func AllLangs() []LangType { + return allLangs +} + // InitLocales loads the locales func InitLocales() { localeNames, err := options.Dir("locale") - if err != nil { log.Fatal("Failed to list locale files: %v", err) } - localFiles := make(map[string][]byte) + localFiles := make(map[string][]byte) for _, name := range localeNames { localFiles[name], err = options.Locale(name) - if err != nil { log.Fatal("Failed to load %s locale file. %v", name, err) } } // These codes will be used once macaron removed - /*tags := make([]language.Tag, len(setting.Langs)) + tags := make([]language.Tag, len(setting.Langs)) for i, lang := range setting.Langs { tags[i] = language.Raw.Make(lang) } + matcher = language.NewMatcher(tags) - for i, name := range setting.Names { - i18n.SetMessage(setting.Langs[i], localFiles[name]) + for i := range setting.Names { + key := "locale_" + setting.Langs[i] + ".ini" + if err := i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil { + log.Fatal("Failed to set messages to %s: %v", setting.Langs[i], err) + } + } + i18n.SetDefaultLang("en-US") + + allLangs = make([]LangType, 0, i18n.Count()-1) + langs := i18n.ListLangs() + names := i18n.ListLangDescs() + for i, v := range langs { + allLangs = append(allLangs, LangType{v, names[i]}) } - i18n.SetDefaultLang("en-US")*/ - - // To be compatible with macaron, we now have to use macaron i18n, once macaron - // removed, we can use i18n directly - macaron_i18n.I18n(macaron_i18n.Options{ - SubURL: setting.AppSubURL, - Files: localFiles, - Langs: setting.Langs, - Names: setting.Names, - DefaultLang: "en-US", - Redirect: false, - CookieDomain: setting.SessionConfig.Domain, - }) } // Match matches accept languages diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 1c67878ea..5cfd994d2 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" "github.com/gobwas/glob" ) diff --git a/modules/validation/binding_test.go b/modules/validation/binding_test.go index 9fc9a6db0..e0daba89e 100644 --- a/modules/validation/binding_test.go +++ b/modules/validation/binding_test.go @@ -9,8 +9,8 @@ import ( "net/http/httptest" "testing" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" + "github.com/go-chi/chi" "github.com/stretchr/testify/assert" ) @@ -34,9 +34,10 @@ type ( func performValidationTest(t *testing.T, testCase validationTestCase) { httpRecorder := httptest.NewRecorder() - m := macaron.Classic() + m := chi.NewRouter() - m.Post(testRoute, binding.Validate(testCase.data), func(actual binding.Errors) { + m.Post(testRoute, func(resp http.ResponseWriter, req *http.Request) { + actual := binding.Validate(req, testCase.data) // see https://github.com/stretchr/testify/issues/435 if actual == nil { actual = binding.Errors{} @@ -49,7 +50,7 @@ func performValidationTest(t *testing.T, testCase validationTestCase) { if err != nil { panic(err) } - + req.Header.Add("Content-Type", "x-www-form-urlencoded") m.ServeHTTP(httpRecorder, req) switch httpRecorder.Code { diff --git a/modules/validation/glob_pattern_test.go b/modules/validation/glob_pattern_test.go index 26775167b..cbaed7e66 100644 --- a/modules/validation/glob_pattern_test.go +++ b/modules/validation/glob_pattern_test.go @@ -7,7 +7,7 @@ package validation import ( "testing" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" "github.com/gobwas/glob" ) diff --git a/modules/validation/refname_test.go b/modules/validation/refname_test.go index 521a83fa0..974d95656 100644 --- a/modules/validation/refname_test.go +++ b/modules/validation/refname_test.go @@ -7,7 +7,7 @@ package validation import ( "testing" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" ) var gitRefNameValidationTestCases = []validationTestCase{ diff --git a/modules/validation/validurl_test.go b/modules/validation/validurl_test.go index aed7406c0..3cb620660 100644 --- a/modules/validation/validurl_test.go +++ b/modules/validation/validurl_test.go @@ -7,7 +7,7 @@ package validation import ( "testing" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" ) var urlValidationTestCases = []validationTestCase{ diff --git a/modules/web/route.go b/modules/web/route.go new file mode 100644 index 000000000..701b3beed --- /dev/null +++ b/modules/web/route.go @@ -0,0 +1,322 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package web + +import ( + "fmt" + "net/http" + "reflect" + "strings" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/middlewares" + + "gitea.com/go-chi/binding" + "github.com/go-chi/chi" +) + +// Wrap converts all kinds of routes to standard library one +func Wrap(handlers ...interface{}) http.HandlerFunc { + if len(handlers) == 0 { + panic("No handlers found") + } + + for _, handler := range handlers { + switch t := handler.(type) { + case http.HandlerFunc, func(http.ResponseWriter, *http.Request), + func(ctx *context.Context), + func(*context.APIContext), + func(*context.PrivateContext), + func(http.Handler) http.Handler: + default: + panic(fmt.Sprintf("Unsupported handler type: %#v", t)) + } + } + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + for i := 0; i < len(handlers); i++ { + handler := handlers[i] + switch t := handler.(type) { + case http.HandlerFunc: + t(resp, req) + if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 { + return + } + case func(http.ResponseWriter, *http.Request): + t(resp, req) + if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 { + return + } + case func(ctx *context.Context): + ctx := context.GetContext(req) + t(ctx) + if ctx.Written() { + return + } + case func(*context.APIContext): + ctx := context.GetAPIContext(req) + t(ctx) + if ctx.Written() { + return + } + case func(*context.PrivateContext): + ctx := context.GetPrivateContext(req) + t(ctx) + if ctx.Written() { + return + } + case func(http.Handler) http.Handler: + var next = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) + t(next).ServeHTTP(resp, req) + if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 { + return + } + default: + panic(fmt.Sprintf("Unsupported handler type: %#v", t)) + } + } + }) +} + +// Middle wrap a context function as a chi middleware +func Middle(f func(ctx *context.Context)) func(netx http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + ctx := context.GetContext(req) + f(ctx) + if ctx.Written() { + return + } + next.ServeHTTP(ctx.Resp, ctx.Req) + }) + } +} + +// MiddleAPI wrap a context function as a chi middleware +func MiddleAPI(f func(ctx *context.APIContext)) func(netx http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + ctx := context.GetAPIContext(req) + f(ctx) + if ctx.Written() { + return + } + next.ServeHTTP(ctx.Resp, ctx.Req) + }) + } +} + +// Bind binding an obj to a handler +func Bind(obj interface{}) http.HandlerFunc { + var tp = reflect.TypeOf(obj) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + if tp.Kind() != reflect.Struct { + panic("Only structs are allowed to bind") + } + return Wrap(func(ctx *context.Context) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + binding.Bind(ctx.Req, theObj) + SetForm(ctx, theObj) + middlewares.AssignForm(theObj, ctx.Data) + }) +} + +// SetForm set the form object +func SetForm(data middlewares.DataStore, obj interface{}) { + data.GetData()["__form"] = obj +} + +// GetForm returns the validate form information +func GetForm(data middlewares.DataStore) interface{} { + return data.GetData()["__form"] +} + +// Route defines a route based on chi's router +type Route struct { + R chi.Router + curGroupPrefix string + curMiddlewares []interface{} +} + +// NewRoute creates a new route +func NewRoute() *Route { + r := chi.NewRouter() + return &Route{ + R: r, + curGroupPrefix: "", + curMiddlewares: []interface{}{}, + } +} + +// Use supports two middlewares +func (r *Route) Use(middlewares ...interface{}) { + if r.curGroupPrefix != "" { + r.curMiddlewares = append(r.curMiddlewares, middlewares...) + } else { + for _, middle := range middlewares { + switch t := middle.(type) { + case func(http.Handler) http.Handler: + r.R.Use(t) + case func(*context.Context): + r.R.Use(Middle(t)) + case func(*context.APIContext): + r.R.Use(MiddleAPI(t)) + default: + panic(fmt.Sprintf("Unsupported middleware type: %#v", t)) + } + } + } +} + +// Group mounts a sub-Router along a `pattern` string. +func (r *Route) Group(pattern string, fn func(), middlewares ...interface{}) { + var previousGroupPrefix = r.curGroupPrefix + var previousMiddlewares = r.curMiddlewares + r.curGroupPrefix += pattern + r.curMiddlewares = append(r.curMiddlewares, middlewares...) + + fn() + + r.curGroupPrefix = previousGroupPrefix + r.curMiddlewares = previousMiddlewares +} + +func (r *Route) getPattern(pattern string) string { + newPattern := r.curGroupPrefix + pattern + if !strings.HasPrefix(newPattern, "/") { + newPattern = "/" + newPattern + } + if newPattern == "/" { + return newPattern + } + return strings.TrimSuffix(newPattern, "/") +} + +// Mount attaches another Route along ./pattern/* +func (r *Route) Mount(pattern string, subR *Route) { + var middlewares = make([]interface{}, len(r.curMiddlewares)) + copy(middlewares, r.curMiddlewares) + subR.Use(middlewares...) + r.R.Mount(r.getPattern(pattern), subR.R) +} + +// Any delegate requests for all methods +func (r *Route) Any(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.HandleFunc(r.getPattern(pattern), Wrap(middlewares...)) +} + +// Route delegate special methods +func (r *Route) Route(pattern string, methods string, h ...interface{}) { + p := r.getPattern(pattern) + ms := strings.Split(methods, ",") + var middlewares = r.getMiddlewares(h) + for _, method := range ms { + r.R.MethodFunc(strings.TrimSpace(method), p, Wrap(middlewares...)) + } +} + +// Delete delegate delete method +func (r *Route) Delete(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Delete(r.getPattern(pattern), Wrap(middlewares...)) +} + +func (r *Route) getMiddlewares(h []interface{}) []interface{} { + var middlewares = make([]interface{}, len(r.curMiddlewares), len(r.curMiddlewares)+len(h)) + copy(middlewares, r.curMiddlewares) + middlewares = append(middlewares, h...) + return middlewares +} + +// Get delegate get method +func (r *Route) Get(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Get(r.getPattern(pattern), Wrap(middlewares...)) +} + +// Head delegate head method +func (r *Route) Head(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Head(r.getPattern(pattern), Wrap(middlewares...)) +} + +// Post delegate post method +func (r *Route) Post(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Post(r.getPattern(pattern), Wrap(middlewares...)) +} + +// Put delegate put method +func (r *Route) Put(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Put(r.getPattern(pattern), Wrap(middlewares...)) +} + +// Patch delegate patch method +func (r *Route) Patch(pattern string, h ...interface{}) { + var middlewares = r.getMiddlewares(h) + r.R.Patch(r.getPattern(pattern), Wrap(middlewares...)) +} + +// ServeHTTP implements http.Handler +func (r *Route) ServeHTTP(w http.ResponseWriter, req *http.Request) { + r.R.ServeHTTP(w, req) +} + +// NotFound defines a handler to respond whenever a route could +// not be found. +func (r *Route) NotFound(h http.HandlerFunc) { + r.R.NotFound(h) +} + +// MethodNotAllowed defines a handler to respond whenever a method is +// not allowed. +func (r *Route) MethodNotAllowed(h http.HandlerFunc) { + r.R.MethodNotAllowed(h) +} + +// Combo deletegate requests to Combo +func (r *Route) Combo(pattern string, h ...interface{}) *Combo { + return &Combo{r, pattern, h} +} + +// Combo represents a tiny group routes with same pattern +type Combo struct { + r *Route + pattern string + h []interface{} +} + +// Get deletegate Get method +func (c *Combo) Get(h ...interface{}) *Combo { + c.r.Get(c.pattern, append(c.h, h...)...) + return c +} + +// Post deletegate Post method +func (c *Combo) Post(h ...interface{}) *Combo { + c.r.Post(c.pattern, append(c.h, h...)...) + return c +} + +// Delete deletegate Delete method +func (c *Combo) Delete(h ...interface{}) *Combo { + c.r.Delete(c.pattern, append(c.h, h...)...) + return c +} + +// Put deletegate Put method +func (c *Combo) Put(h ...interface{}) *Combo { + c.r.Put(c.pattern, append(c.h, h...)...) + return c +} + +// Patch deletegate Patch method +func (c *Combo) Patch(h ...interface{}) *Combo { + c.r.Patch(c.pattern, append(c.h, h...)...) + return c +} diff --git a/modules/web/route_test.go b/modules/web/route_test.go new file mode 100644 index 000000000..03955f0f2 --- /dev/null +++ b/modules/web/route_test.go @@ -0,0 +1,169 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package web + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" +) + +func TestRoute1(t *testing.T) { + buff := bytes.NewBufferString("") + recorder := httptest.NewRecorder() + recorder.Body = buff + + r := NewRoute() + r.Get("/{username}/{reponame}/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { + username := chi.URLParam(req, "username") + assert.EqualValues(t, "gitea", username) + reponame := chi.URLParam(req, "reponame") + assert.EqualValues(t, "gitea", reponame) + tp := chi.URLParam(req, "type") + assert.EqualValues(t, "issues", tp) + }) + + req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) +} + +func TestRoute2(t *testing.T) { + buff := bytes.NewBufferString("") + recorder := httptest.NewRecorder() + recorder.Body = buff + + var route int + + r := NewRoute() + r.Group("/{username}/{reponame}", func() { + r.Group("", func() { + r.Get("/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { + username := chi.URLParam(req, "username") + assert.EqualValues(t, "gitea", username) + reponame := chi.URLParam(req, "reponame") + assert.EqualValues(t, "gitea", reponame) + tp := chi.URLParam(req, "type") + assert.EqualValues(t, "issues", tp) + route = 0 + }) + + r.Get("/{type:issues|pulls}/{index}", func(resp http.ResponseWriter, req *http.Request) { + username := chi.URLParam(req, "username") + assert.EqualValues(t, "gitea", username) + reponame := chi.URLParam(req, "reponame") + assert.EqualValues(t, "gitea", reponame) + tp := chi.URLParam(req, "type") + assert.EqualValues(t, "issues", tp) + index := chi.URLParam(req, "index") + assert.EqualValues(t, "1", index) + route = 1 + }) + }, func(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(200) + }) + + r.Group("/issues/{index}", func() { + r.Get("/view", func(resp http.ResponseWriter, req *http.Request) { + username := chi.URLParam(req, "username") + assert.EqualValues(t, "gitea", username) + reponame := chi.URLParam(req, "reponame") + assert.EqualValues(t, "gitea", reponame) + index := chi.URLParam(req, "index") + assert.EqualValues(t, "1", index) + route = 2 + }) + }) + }) + + req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 0, route) + + req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 1, route) + + req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1/view", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 2, route) +} + +func TestRoute3(t *testing.T) { + buff := bytes.NewBufferString("") + recorder := httptest.NewRecorder() + recorder.Body = buff + + var route int + + m := NewRoute() + r := NewRoute() + r.Mount("/api/v1", m) + + m.Group("/repos", func() { + m.Group("/{username}/{reponame}", func() { + m.Group("/branch_protections", func() { + m.Get("", func(resp http.ResponseWriter, req *http.Request) { + route = 0 + }) + m.Post("", func(resp http.ResponseWriter, req *http.Request) { + route = 1 + }) + m.Group("/{name}", func() { + m.Get("", func(resp http.ResponseWriter, req *http.Request) { + route = 2 + }) + m.Patch("", func(resp http.ResponseWriter, req *http.Request) { + route = 3 + }) + m.Delete("", func(resp http.ResponseWriter, req *http.Request) { + route = 4 + }) + }) + }) + }) + }) + + req, err := http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 0, route) + + req, err = http.NewRequest("POST", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code, http.StatusOK) + assert.EqualValues(t, 1, route) + + req, err = http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 2, route) + + req, err = http.NewRequest("PATCH", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 3, route) + + req, err = http.NewRequest("DELETE", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.EqualValues(t, 4, route) +} diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 31b55b499..76433e6da 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1538,8 +1538,7 @@ settings.trust_model.collaborator.long=Mitarbeiter: Vertraue Signaturen von Mita settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert - ( egal ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, unabhängig ob die Signatur mit dem Committer übereinstimmt oder nicht. settings.trust_model.committer=Committer settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Gitea dazu, Gitea als Committer zu haben) -settings.trust_model.committer.desc=Gültige Signaturen von Mitwirkenden werden als "vertrauenswürdig" gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als "nicht übereinstimmend" markiert. Das führt dazu, dass Gitea auf signierten Commits, bei denen der echte Committer als Co-authored-by: oder Co-committed-by in der Beschreibung eingetragen wurde, als Committer gilt. -Der Standard Gitea Schlüssel muss auf einen User in der Datenbank zeigen. +settings.trust_model.committer.desc=Gültige Signaturen von Mitwirkenden werden als "vertrauenswürdig" gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als "nicht übereinstimmend" markiert. Das führt dazu, dass Gitea auf signierten Commits, bei denen der echte Committer als Co-authored-by: oder Co-committed-by in der Beschreibung eingetragen wurde, als Committer gilt. Der Standard Gitea Schlüssel muss auf einen User in der Datenbank zeigen. settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, wenn die Signatur mit dem Committer übereinstimmt als "nicht übereinstimmend". Dies zwingt Gitea als Committer bei signierten Commits mit dem tatsächlichen Committer als Co-Authored-By: und Co-Committed-By: Trailer im Commit. Der Standard-Gitea-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen. diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 4180076b0..250bc5da5 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -16,20 +16,20 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/cron" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "gitea.com/go-chi/session" ) const ( @@ -132,7 +132,8 @@ func Dashboard(ctx *context.Context) { } // DashboardPost run an admin operation -func DashboardPost(ctx *context.Context, form auth.AdminDashboardForm) { +func DashboardPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminDashboardForm) ctx.Data["Title"] = ctx.Tr("admin.dashboard") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminDashboard"] = true @@ -239,7 +240,7 @@ func Config(ctx *context.Context) { ctx.Data["OfflineMode"] = setting.OfflineMode ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["RunUser"] = setting.RunUser - ctx.Data["RunMode"] = strings.Title(macaron.Env) + ctx.Data["RunMode"] = strings.Title(setting.RunMode) if version, err := git.LocalVersion(); err == nil { ctx.Data["GitVersion"] = version.Original() } diff --git a/routers/admin/auths.go b/routers/admin/auths.go index 7a9d28637..12d0a2ccf 100644 --- a/routers/admin/auths.go +++ b/routers/admin/auths.go @@ -10,15 +10,16 @@ import ( "regexp" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/ldap" "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "xorm.io/xorm/convert" ) @@ -206,7 +207,8 @@ func parseSSPIConfig(ctx *context.Context, form auth.AuthenticationForm) (*model } // NewAuthSourcePost response for adding an auth source -func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) { +func NewAuthSourcePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AuthenticationForm) ctx.Data["Title"] = ctx.Tr("admin.auths.new") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminAuthentications"] = true @@ -312,7 +314,8 @@ func EditAuthSource(ctx *context.Context) { } // EditAuthSourcePost response for editing auth source -func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) { +func EditAuthSourcePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AuthenticationForm) ctx.Data["Title"] = ctx.Tr("admin.auths.edit") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminAuthentications"] = true diff --git a/routers/admin/users.go b/routers/admin/users.go index 74fce9a10..2d40a883a 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -11,12 +11,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" router_user_setting "code.gitea.io/gitea/routers/user/setting" "code.gitea.io/gitea/services/mailer" @@ -63,7 +64,8 @@ func NewUser(ctx *context.Context) { } // NewUserPost response for adding a new user -func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) { +func NewUserPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminCreateUserForm) ctx.Data["Title"] = ctx.Tr("admin.users.new_account") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true @@ -214,7 +216,8 @@ func EditUser(ctx *context.Context) { } // EditUserPost response for editting user -func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) { +func EditUserPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminEditUserForm) ctx.Data["Title"] = ctx.Tr("admin.users.edit_account") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true diff --git a/routers/admin/users_test.go b/routers/admin/users_test.go index a282507f5..bd00bb2bf 100644 --- a/routers/admin/users_test.go +++ b/routers/admin/users_test.go @@ -8,8 +8,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -39,7 +40,8 @@ func TestNewUserPost_MustChangePassword(t *testing.T) { MustChangePassword: true, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.SuccessMsg) @@ -76,7 +78,8 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) { MustChangePassword: false, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.SuccessMsg) @@ -113,7 +116,8 @@ func TestNewUserPost_InvalidEmail(t *testing.T) { MustChangePassword: false, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index 0fd9e17f4..1356276f0 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -13,12 +13,13 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) // CreateOrg api for create organization -func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { +func CreateOrg(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/orgs admin adminCreateOrg // --- // summary: Create an organization @@ -43,7 +44,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateOrgOption) u := user.GetUserByParams(ctx) if ctx.Written() { return diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go index 73c7d740f..467f8a22f 100644 --- a/routers/api/v1/admin/repo.go +++ b/routers/api/v1/admin/repo.go @@ -7,12 +7,13 @@ package admin import ( "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/user" ) // CreateRepo api for creating a repository -func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) { +func CreateRepo(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/repos admin adminCreateRepo // --- // summary: Create a repository on behalf of a user @@ -41,11 +42,11 @@ func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) { // "$ref": "#/responses/error" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateRepoOption) owner := user.GetUserByParams(ctx) if ctx.Written() { return } - repo.CreateUserRepo(ctx, owner, form) + repo.CreateUserRepo(ctx, owner, *form) } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 670baf020..f148710c7 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/mailer" @@ -42,7 +43,7 @@ func parseLoginSource(ctx *context.APIContext, u *models.User, sourceID int64, l } // CreateUser create a user -func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { +func CreateUser(ctx *context.APIContext) { // swagger:operation POST /admin/users admin adminCreateUser // --- // summary: Create a user @@ -64,7 +65,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateUserOption) u := &models.User{ Name: form.Username, FullName: form.FullName, @@ -119,7 +120,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { } // EditUser api for modifying a user's information -func EditUser(ctx *context.APIContext, form api.EditUserOption) { +func EditUser(ctx *context.APIContext) { // swagger:operation PATCH /admin/users/{username} admin adminEditUser // --- // summary: Edit an existing user @@ -144,7 +145,7 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditUserOption) u := user.GetUserByParams(ctx) if ctx.Written() { return @@ -283,7 +284,7 @@ func DeleteUser(ctx *context.APIContext) { } // CreatePublicKey api for creating a public key to a user -func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey // --- // summary: Add a public key on behalf of a user @@ -308,12 +309,12 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateKeyOption) u := user.GetUserByParams(ctx) if ctx.Written() { return } - user.CreateUserPublicKey(ctx, form, u.ID) + user.CreateUserPublicKey(ctx, *form, u.ID) } // DeleteUserPublicKey api for deleting a user's public key diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 876f48ca5..593551d77 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -66,14 +66,16 @@ package v1 import ( "net/http" + "reflect" "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/admin" "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/api/v1/notify" @@ -83,11 +85,12 @@ import ( _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "code.gitea.io/gitea/routers/api/v1/user" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" + "gitea.com/go-chi/session" + "github.com/go-chi/cors" ) -func sudo() macaron.Handler { +func sudo() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { sudo := ctx.Query("sudo") if len(sudo) == 0 { @@ -117,10 +120,10 @@ func sudo() macaron.Handler { } } -func repoAssignment() macaron.Handler { +func repoAssignment() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - userName := ctx.Params(":username") - repoName := ctx.Params(":reponame") + userName := ctx.Params("username") + repoName := ctx.Params("reponame") var ( owner *models.User @@ -184,7 +187,7 @@ func repoAssignment() macaron.Handler { } // Contexter middleware already checks token for user sign in process. -func reqToken() macaron.Handler { +func reqToken() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if true == ctx.Data["IsApiToken"] { return @@ -201,7 +204,7 @@ func reqToken() macaron.Handler { } } -func reqBasicAuth() macaron.Handler { +func reqBasicAuth() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Context.IsBasicAuth { ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "basic auth required") @@ -212,7 +215,7 @@ func reqBasicAuth() macaron.Handler { } // reqSiteAdmin user should be the site admin -func reqSiteAdmin() macaron.Handler { +func reqSiteAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin") @@ -222,7 +225,7 @@ func reqSiteAdmin() macaron.Handler { } // reqOwner user should be the owner of the repo or site admin. -func reqOwner() macaron.Handler { +func reqOwner() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo") @@ -232,7 +235,7 @@ func reqOwner() macaron.Handler { } // reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin -func reqAdmin() macaron.Handler { +func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository") @@ -242,7 +245,7 @@ func reqAdmin() macaron.Handler { } // reqRepoWriter user should have a permission to write to a repo, or be a site admin -func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler { +func reqRepoWriter(unitTypes ...models.UnitType) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") @@ -252,7 +255,7 @@ func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler { } // reqRepoReader user should have specific read permission or be a repo admin or a site admin -func reqRepoReader(unitType models.UnitType) macaron.Handler { +func reqRepoReader(unitType models.UnitType) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoReaderSpecific(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") @@ -262,7 +265,7 @@ func reqRepoReader(unitType models.UnitType) macaron.Handler { } // reqAnyRepoReader user should have any permission to read repository or permissions of site admin -func reqAnyRepoReader() macaron.Handler { +func reqAnyRepoReader() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoReaderAny() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin") @@ -272,7 +275,7 @@ func reqAnyRepoReader() macaron.Handler { } // reqOrgOwnership user should be an organization owner, or a site admin -func reqOrgOwnership() macaron.Handler { +func reqOrgOwnership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -304,7 +307,7 @@ func reqOrgOwnership() macaron.Handler { } // reqTeamMembership user should be an team member, or a site admin -func reqTeamMembership() macaron.Handler { +func reqTeamMembership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -341,7 +344,7 @@ func reqTeamMembership() macaron.Handler { } // reqOrgMembership user should be an organization member, or a site admin -func reqOrgMembership() macaron.Handler { +func reqOrgMembership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -371,7 +374,7 @@ func reqOrgMembership() macaron.Handler { } } -func reqGitHook() macaron.Handler { +func reqGitHook() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.User.CanEditGitHook() { ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks") @@ -380,7 +383,7 @@ func reqGitHook() macaron.Handler { } } -func orgAssignment(args ...bool) macaron.Handler { +func orgAssignment(args ...bool) func(ctx *context.APIContext) { var ( assignOrg bool assignTeam bool @@ -500,13 +503,6 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) { } } -func mustEnableUserHeatmap(ctx *context.APIContext) { - if !setting.Service.EnableUserHeatmap { - ctx.NotFound() - return - } -} - func mustNotBeArchived(ctx *context.APIContext) { if ctx.Repo.Repository.IsArchived { ctx.NotFound() @@ -514,18 +510,59 @@ func mustNotBeArchived(ctx *context.APIContext) { } } -// RegisterRoutes registers all v1 APIs routes to web application. -func RegisterRoutes(m *macaron.Macaron) { - bind := binding.Bind +// bind binding an obj to a func(ctx *context.APIContext) +func bind(obj interface{}) http.HandlerFunc { + var tp = reflect.TypeOf(obj) + for tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return web.Wrap(func(ctx *context.APIContext) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + errs := binding.Bind(ctx.Req, theObj) + if len(errs) > 0 { + ctx.Error(422, "validationError", errs[0].Error()) + return + } + web.SetForm(ctx, theObj) + }) +} - if setting.API.EnableSwagger { - m.Get("/swagger", misc.Swagger) // Render V1 by default +// Routes registers all v1 APIs routes to web application. +func Routes() *web.Route { + var m = web.NewRoute() + + m.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + m.Use(securityHeaders()) + if setting.CORSConfig.Enabled { + m.Use(cors.Handler(cors.Options{ + //Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option + AllowedOrigins: setting.CORSConfig.AllowDomain, + //setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option + AllowedMethods: setting.CORSConfig.Methods, + AllowCredentials: setting.CORSConfig.AllowCredentials, + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + })) } + m.Use(context.APIContexter()) + m.Use(context.ToggleAPI(&context.ToggleOptions{ + SignInRequired: setting.Service.RequireSignInView, + })) - m.Group("/v1", func() { + m.Group("", func() { // Miscellaneous if setting.API.EnableSwagger { - m.Get("/swagger", misc.Swagger) + m.Get("/swagger", func(ctx *context.APIContext) { + ctx.Redirect("/api/swagger") + }) } m.Get("/version", misc.Version) m.Get("/signing-key.gpg", misc.SigningKey) @@ -544,7 +581,7 @@ func RegisterRoutes(m *macaron.Macaron) { Get(notify.ListNotifications). Put(notify.ReadNotifications) m.Get("/new", notify.NewAvailable) - m.Combo("/threads/:id"). + m.Combo("/threads/{id}"). Get(notify.GetThread). Patch(notify.ReadThread) }, reqToken()) @@ -553,28 +590,31 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/users", func() { m.Get("/search", user.Search) - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Get("", user.GetInfo) - m.Get("/heatmap", mustEnableUserHeatmap, user.GetUserHeatmapData) + + if setting.Service.EnableUserHeatmap { + m.Get("/heatmap", user.GetUserHeatmapData) + } m.Get("/repos", user.ListUserRepos) m.Group("/tokens", func() { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) - m.Combo("/:id").Delete(user.DeleteAccessToken) + m.Combo("/{id}").Delete(user.DeleteAccessToken) }, reqBasicAuth()) }) }) m.Group("/users", func() { - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Get("/keys", user.ListPublicKeys) m.Get("/gpg_keys", user.ListGPGKeys) m.Get("/followers", user.ListFollowers) m.Group("/following", func() { m.Get("", user.ListFollowing) - m.Get("/:target", user.CheckFollowing) + m.Get("/{target}", user.CheckFollowing) }) m.Get("/starred", user.GetStarredRepos) @@ -592,20 +632,20 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/followers", user.ListMyFollowers) m.Group("/following", func() { m.Get("", user.ListMyFollowing) - m.Combo("/:username").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow) + m.Combo("/{username}").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow) }) m.Group("/keys", func() { m.Combo("").Get(user.ListMyPublicKeys). Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) - m.Combo("/:id").Get(user.GetPublicKey). + m.Combo("/{id}").Get(user.GetPublicKey). Delete(user.DeletePublicKey) }) m.Group("/applications", func() { m.Combo("/oauth2"). Get(user.ListOauth2Applications). Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) - m.Combo("/oauth2/:id"). + m.Combo("/oauth2/{id}"). Delete(user.DeleteOauth2Application). Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). Get(user.GetOauth2Application) @@ -614,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/gpg_keys", func() { m.Combo("").Get(user.ListMyGPGKeys). Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) - m.Combo("/:id").Get(user.GetGPGKey). + m.Combo("/{id}").Get(user.GetGPGKey). Delete(user.DeleteGPGKey) }) @@ -623,7 +663,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/starred", func() { m.Get("", user.GetMyStarredRepos) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Get("", user.IsStarring) m.Put("", user.Star) m.Delete("", user.Unstar) @@ -639,9 +679,9 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqToken()) // Repositories - m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) + m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) - m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID) m.Group("/repos", func() { m.Get("/search", repo.Search) @@ -650,10 +690,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). - Patch(reqToken(), reqAdmin(), context.RepoRefForAPI(), bind(api.EditRepoOption{}), repo.Edit) + Patch(reqToken(), reqAdmin(), context.RepoRefForAPI, bind(api.EditRepoOption{}), repo.Edit) m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) m.Combo("/notifications"). Get(reqToken(), notify.ListRepoNotifications). @@ -661,15 +701,15 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/hooks", func() { m.Combo("").Get(repo.ListHooks). Post(bind(api.CreateHookOption{}), repo.CreateHook) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetHook). Patch(bind(api.EditHookOption{}), repo.EditHook). Delete(repo.DeleteHook) - m.Post("/tests", context.RepoRefForAPI(), repo.TestHook) + m.Post("/tests", context.RepoRefForAPI, repo.TestHook) }) m.Group("/git", func() { m.Combo("").Get(repo.ListGitHooks) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetGitHook). Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). Delete(repo.DeleteGitHook) @@ -678,11 +718,11 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqToken(), reqAdmin()) m.Group("/collaborators", func() { m.Get("", reqAnyRepoReader(), repo.ListCollaborators) - m.Combo("/:collaborator").Get(reqAnyRepoReader(), repo.IsCollaborator). + m.Combo("/{collaborator}").Get(reqAnyRepoReader(), repo.IsCollaborator). Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) }, reqToken()) - m.Get("/raw/*", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetRawFile) + m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetRawFile) m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork) @@ -695,7 +735,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection) - m.Group("/:name", func() { + m.Group("/{name}", func() { m.Get("", repo.GetBranchProtection) m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) m.Delete("", repo.DeleteBranchProtection) @@ -707,19 +747,19 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) - m.Combo("/:id").Get(repo.GetDeployKey). + m.Combo("/{id}").Get(repo.GetDeployKey). Delete(repo.DeleteDeploykey) }, reqToken(), reqAdmin()) m.Group("/times", func() { m.Combo("").Get(repo.ListTrackedTimesByRepository) - m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser) + m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) }, mustEnableIssues, reqToken()) m.Group("/issues", func() { m.Combo("").Get(repo.ListIssues). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) m.Group("/comments", func() { m.Get("", repo.ListRepoIssueComments) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo(""). Get(repo.GetIssueComment). Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). @@ -730,13 +770,13 @@ func RegisterRoutes(m *macaron.Macaron) { Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) }) }) - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Combo("").Get(repo.GetIssue). Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue) m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/:id", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Group("/labels", func() { @@ -744,14 +784,14 @@ func RegisterRoutes(m *macaron.Macaron) { Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). Delete(reqToken(), repo.ClearIssueLabels) - m.Delete("/:id", reqToken(), repo.DeleteIssueLabel) + m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) }) m.Group("/times", func() { m.Combo(""). Get(repo.ListTrackedTimes). Post(bind(api.AddTimeOption{}), repo.AddTime). Delete(repo.ResetIssueTime) - m.Delete("/:id", repo.DeleteTime) + m.Delete("/{id}", repo.DeleteTime) }, reqToken()) m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { @@ -762,8 +802,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/subscriptions", func() { m.Get("", repo.GetIssueSubscribers) m.Get("/check", reqToken(), repo.CheckIssueSubscription) - m.Put("/:user", reqToken(), repo.AddIssueSubscription) - m.Delete("/:user", reqToken(), repo.DelIssueSubscription) + m.Put("/{user}", reqToken(), repo.AddIssueSubscription) + m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) }) m.Combo("/reactions"). Get(repo.GetIssueReactions). @@ -774,7 +814,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) - m.Combo("/:id").Get(repo.GetLabel). + m.Combo("/{id}").Get(repo.GetLabel). Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel) }) @@ -783,7 +823,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/milestones", func() { m.Combo("").Get(repo.ListMilestones). Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) - m.Combo("/:id").Get(repo.GetMilestone). + m.Combo("/{id}").Get(repo.GetMilestone). Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone) }) @@ -797,30 +837,30 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetRelease). Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment) - m.Combo("/:asset").Get(repo.GetReleaseAttachment). + m.Combo("/{asset}").Get(repo.GetReleaseAttachment). Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment) }) }) m.Group("/tags", func() { - m.Combo("/:tag"). + m.Combo("/{tag}"). Get(repo.GetReleaseTag). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag) }) }, reqRepoReader(models.UnitTypeReleases)) m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync) - m.Get("/editorconfig/:filename", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig) + m.Get("/editorconfig/{filename}", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { - m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests). + m.Combo("").Get(repo.ListPullRequests). Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Combo("").Get(repo.GetPullRequest). Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest) m.Get(".diff", repo.DownloadPullDiff) @@ -832,7 +872,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo(""). Get(repo.ListPullReviews). Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo(""). Get(repo.GetPullReview). Delete(reqToken(), repo.DeletePullReview). @@ -847,25 +887,25 @@ func RegisterRoutes(m *macaron.Macaron) { }) }, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(false)) m.Group("/statuses", func() { - m.Combo("/:sha").Get(repo.GetCommitStatuses). + m.Combo("/{sha}").Get(repo.GetCommitStatuses). Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(models.UnitTypeCode)) m.Group("/commits", func() { m.Get("", repo.GetAllCommits) - m.Group("/:ref", func() { + m.Group("/{ref}", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) }) }, reqRepoReader(models.UnitTypeCode)) m.Group("/git", func() { m.Group("/commits", func() { - m.Get("/:sha", repo.GetSingleCommit) + m.Get("/{sha}", repo.GetSingleCommit) }) m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) - m.Get("/trees/:sha", context.RepoRefForAPI(), repo.GetTree) - m.Get("/blobs/:sha", context.RepoRefForAPI(), repo.GetBlob) - m.Get("/tags/:sha", context.RepoRefForAPI(), repo.GetTag) + m.Get("/trees/{sha}", context.RepoRefForAPI, repo.GetTree) + m.Get("/blobs/{sha}", context.RepoRefForAPI, repo.GetBlob) + m.Get("/tags/{sha}", context.RepoRefForAPI, repo.GetTag) }, reqRepoReader(models.UnitTypeCode)) m.Group("/contents", func() { m.Get("", repo.GetContentsList) @@ -880,7 +920,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/topics", func() { m.Combo("").Get(repo.ListTopics). Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) - m.Group("/:topic", func() { + m.Group("/{topic}", func() { m.Combo("").Put(reqToken(), repo.AddTopic). Delete(reqToken(), repo.DeleteTopic) }, reqAdmin()) @@ -892,10 +932,10 @@ func RegisterRoutes(m *macaron.Macaron) { // Organizations m.Get("/user/orgs", reqToken(), org.ListMyOrgs) - m.Get("/users/:username/orgs", org.ListUserOrgs) + m.Get("/users/{username}/orgs", org.ListUserOrgs) m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) m.Get("/orgs", org.GetAll) - m.Group("/orgs/:org", func() { + m.Group("/orgs/{org}", func() { m.Combo("").Get(org.Get). Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). Delete(reqToken(), reqOrgOwnership(), org.Delete) @@ -903,12 +943,12 @@ func RegisterRoutes(m *macaron.Macaron) { Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) m.Group("/members", func() { m.Get("", org.ListMembers) - m.Combo("/:username").Get(org.IsMember). + m.Combo("/{username}").Get(org.IsMember). Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) }) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) - m.Combo("/:username").Get(org.IsPublicMember). + m.Combo("/{username}").Get(org.IsPublicMember). Put(reqToken(), reqOrgMembership(), org.PublicizeMember). Delete(reqToken(), reqOrgMembership(), org.ConcealMember) }) @@ -920,56 +960,52 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/labels", func() { m.Get("", org.ListLabels) m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) - m.Combo("/:id").Get(org.GetLabel). + m.Combo("/{id}").Get(org.GetLabel). Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) }) m.Group("/hooks", func() { m.Combo("").Get(org.ListHooks). Post(bind(api.CreateHookOption{}), org.CreateHook) - m.Combo("/:id").Get(org.GetHook). + m.Combo("/{id}").Get(org.GetHook). Patch(bind(api.EditHookOption{}), org.EditHook). Delete(org.DeleteHook) }, reqToken(), reqOrgOwnership()) }, orgAssignment(true)) - m.Group("/teams/:teamid", func() { + m.Group("/teams/{teamid}", func() { m.Combo("").Get(org.GetTeam). Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). Delete(reqOrgOwnership(), org.DeleteTeam) m.Group("/members", func() { m.Get("", org.GetTeamMembers) - m.Combo("/:username"). + m.Combo("/{username}"). Get(org.GetTeamMember). Put(reqOrgOwnership(), org.AddTeamMember). Delete(reqOrgOwnership(), org.RemoveTeamMember) }) m.Group("/repos", func() { m.Get("", org.GetTeamRepos) - m.Combo("/:org/:reponame"). + m.Combo("/{org}/{reponame}"). Put(org.AddTeamRepository). Delete(org.RemoveTeamRepository) }) }, orgAssignment(false, true), reqToken(), reqTeamMembership()) - m.Any("/*", func(ctx *context.APIContext) { - ctx.NotFound() - }) - m.Group("/admin", func() { m.Group("/cron", func() { m.Get("", admin.ListCronTasks) - m.Post("/:task", admin.PostCronTask) + m.Post("/{task}", admin.PostCronTask) }) m.Get("/orgs", admin.GetAllOrgs) m.Group("/users", func() { m.Get("", admin.GetAllUsers) m.Post("", bind(api.CreateUserOption{}), admin.CreateUser) - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser). Delete(admin.DeleteUser) m.Group("/keys", func() { m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey) - m.Delete("/:id", admin.DeleteUserPublicKey) + m.Delete("/{id}", admin.DeleteUserPublicKey) }) m.Get("/orgs", org.ListUserOrgs) m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) @@ -978,23 +1014,26 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/unadopted", func() { m.Get("", admin.ListUnadoptedRepositories) - m.Post("/:username/:reponame", admin.AdoptRepository) - m.Delete("/:username/:reponame", admin.DeleteUnadoptedRepository) + m.Post("/{username}/{reponame}", admin.AdoptRepository) + m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) }, reqToken(), reqSiteAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) }) - }, securityHeaders(), context.APIContexter(), sudo()) + }, sudo()) + + return m } -func securityHeaders() macaron.Handler { - return func(ctx *macaron.Context) { - ctx.Resp.Before(func(w macaron.ResponseWriter) { +func securityHeaders() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers // http://stackoverflow.com/a/3146618/244009 - w.Header().Set("x-content-type-options", "nosniff") + resp.Header().Set("x-content-type-options", "nosniff") + next.ServeHTTP(resp, req) }) } } diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index a10c288df..571818530 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -5,6 +5,7 @@ package misc import ( + "io/ioutil" "net/http" "strings" @@ -13,12 +14,13 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "mvdan.cc/xurls/v2" ) // Markdown render markdown document to HTML -func Markdown(ctx *context.APIContext, form api.MarkdownOption) { +func Markdown(ctx *context.APIContext) { // swagger:operation POST /markdown miscellaneous renderMarkdown // --- // summary: Render a markdown document as HTML @@ -37,6 +39,8 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.MarkdownOption) + if ctx.HasAPIError() { ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) return @@ -117,7 +121,7 @@ func MarkdownRaw(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" - body, err := ctx.Req.Body().Bytes() + body, err := ioutil.ReadAll(ctx.Req.Body) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "", err) return diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go index 6c81ec8eb..3ae3165fd 100644 --- a/routers/api/v1/misc/markdown_test.go +++ b/routers/api/v1/misc/markdown_test.go @@ -15,10 +15,10 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/inject" - "gitea.com/macaron/macaron" "github.com/stretchr/testify/assert" ) @@ -26,25 +26,21 @@ const AppURL = "http://localhost:3000/" const Repo = "gogits/gogs" const AppSubURL = AppURL + Repo + "/" -func createContext(req *http.Request) (*macaron.Context, *httptest.ResponseRecorder) { +func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) { + var rnd = templates.HTMLRenderer() resp := httptest.NewRecorder() - c := &macaron.Context{ - Injector: inject.New(), - Req: macaron.Request{Request: req}, - Resp: macaron.NewResponseWriter(req.Method, resp), - Render: &macaron.DummyRender{ResponseWriter: resp}, - Data: make(map[string]interface{}), + c := &context.Context{ + Req: req, + Resp: context.NewResponse(resp), + Render: rnd, + Data: make(map[string]interface{}), } - c.Map(c) - c.Map(req) return c, resp } -func wrap(ctx *macaron.Context) *context.APIContext { +func wrap(ctx *context.Context) *context.APIContext { return &context.APIContext{ - Context: &context.Context{ - Context: ctx, - }, + Context: ctx, } } @@ -115,7 +111,8 @@ Here are some links to the most important topics. You can find the full list of for i := 0; i < len(testCases); i += 2 { options.Text = testCases[i] - Markdown(ctx, options) + web.SetForm(ctx, &options) + Markdown(ctx) assert.Equal(t, testCases[i+1], resp.Body.String()) resp.Body.Reset() } @@ -156,7 +153,8 @@ func TestAPI_RenderSimple(t *testing.T) { for i := 0; i < len(simpleCases); i += 2 { options.Text = simpleCases[i] - Markdown(ctx, options) + web.SetForm(ctx, &options) + Markdown(ctx) assert.Equal(t, simpleCases[i+1], resp.Body.String()) resp.Body.Reset() } @@ -174,7 +172,7 @@ func TestAPI_RenderRaw(t *testing.T) { ctx := wrap(m) for i := 0; i < len(simpleCases); i += 2 { - ctx.Req.Request.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i])) + ctx.Req.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i])) MarkdownRaw(ctx) assert.Equal(t, simpleCases[i+1], resp.Body.String()) resp.Body.Reset() diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index cce959bb4..ed827c48d 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -85,7 +86,7 @@ func GetHook(ctx *context.APIContext) { } // CreateHook create a hook for an organization -func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { +func CreateHook(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/hooks/ organization orgCreateHook // --- // summary: Create a hook @@ -108,15 +109,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { // "201": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.CreateHookOption) //TODO in body params - if !utils.CheckCreateHookOption(ctx, &form) { + if !utils.CheckCreateHookOption(ctx, form) { return } - utils.AddOrgHook(ctx, &form) + utils.AddOrgHook(ctx, form) } // EditHook modify a hook of a repository -func EditHook(ctx *context.APIContext, form api.EditHookOption) { +func EditHook(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook // --- // summary: Update a hook @@ -144,9 +146,11 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // "200": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.EditHookOption) + //TODO in body params hookID := ctx.ParamsInt64(":id") - utils.EditOrgHook(ctx, &form, hookID) + utils.EditOrgHook(ctx, form, hookID) } // DeleteHook delete a hook of an organization diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 9a8a4fa29..76061f163 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -52,7 +53,7 @@ func ListLabels(ctx *context.APIContext) { } // CreateLabel create a label for a repository -func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { +func CreateLabel(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/labels organization orgCreateLabel // --- // summary: Create a label for an organization @@ -75,7 +76,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { // "$ref": "#/responses/Label" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateLabelOption) form.Color = strings.Trim(form.Color, " ") if len(form.Color) == 6 { form.Color = "#" + form.Color @@ -144,7 +145,7 @@ func GetLabel(ctx *context.APIContext) { } // EditLabel modify a label for an Organization -func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { +func EditLabel(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org}/labels/{id} organization orgEditLabel // --- // summary: Update a label @@ -173,7 +174,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { // "$ref": "#/responses/Label" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditLabelOption) label, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrOrgLabelNotExist(err) { diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index ca3e10173..61e09e112 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -149,7 +150,7 @@ func GetAll(ctx *context.APIContext) { } // Create api for create organization -func Create(ctx *context.APIContext, form api.CreateOrgOption) { +func Create(ctx *context.APIContext) { // swagger:operation POST /orgs organization orgCreate // --- // summary: Create an organization @@ -169,7 +170,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateOrgOption) if !ctx.User.CanCreateOrganization() { ctx.Error(http.StatusForbidden, "Create organization not allowed", nil) return @@ -231,7 +232,7 @@ func Get(ctx *context.APIContext) { } // Edit change an organization's information -func Edit(ctx *context.APIContext, form api.EditOrgOption) { +func Edit(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org} organization orgEdit // --- // summary: Edit an organization @@ -253,7 +254,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) { // responses: // "200": // "$ref": "#/responses/Organization" - + form := web.GetForm(ctx).(*api.EditOrgOption) org := ctx.Org.Organization org.FullName = form.FullName org.Description = form.Description diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 72387dcbf..c749751ac 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -131,7 +132,7 @@ func GetTeam(ctx *context.APIContext) { } // CreateTeam api for create a team -func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { +func CreateTeam(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/teams organization orgCreateTeam // --- // summary: Create a team @@ -154,7 +155,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { // "$ref": "#/responses/Team" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateTeamOption) team := &models.Team{ OrgID: ctx.Org.Organization.ID, Name: form.Name, @@ -190,7 +191,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { } // EditTeam api for edit a team -func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { +func EditTeam(ctx *context.APIContext) { // swagger:operation PATCH /teams/{id} organization orgEditTeam // --- // summary: Edit a team @@ -212,6 +213,8 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { // "200": // "$ref": "#/responses/Team" + form := web.GetForm(ctx).(*api.EditTeamOption) + team := ctx.Org.Team if err := team.GetUnits(); err != nil { ctx.InternalServerError(err) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 9a0a552ba..790464c8b 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" ) @@ -175,7 +176,7 @@ func DeleteBranch(ctx *context.APIContext) { } // CreateBranch creates a branch for a user's repository -func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { +func CreateBranch(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch // --- // summary: Create a branch @@ -206,6 +207,7 @@ func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { // "409": // description: The branch with the same name already exists. + opt := web.GetForm(ctx).(*api.CreateBranchRepoOption) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") return @@ -395,7 +397,7 @@ func ListBranchProtections(ctx *context.APIContext) { } // CreateBranchProtection creates a branch protection for a repo -func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) { +func CreateBranchProtection(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection // --- // summary: Create a branch protections for a repository @@ -428,6 +430,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateBranchProtectionOption) repo := ctx.Repo.Repository // Currently protection must match an actual branch @@ -561,7 +564,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec } // EditBranchProtection edits a branch protection for a repo -func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) { +func EditBranchProtection(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection // --- // summary: Edit a branch protections for a repository. Only fields that are set will be changed @@ -596,7 +599,7 @@ func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtection // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditBranchProtectionOption) repo := ctx.Repo.Repository bpName := ctx.Params(":name") protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName) diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index 497255a47..a4fc1d8f1 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -111,7 +112,7 @@ func IsCollaborator(ctx *context.APIContext) { } // AddCollaborator add a collaborator to a repository -func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) { +func AddCollaborator(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator // --- // summary: Add a collaborator to a repository @@ -143,6 +144,8 @@ func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.AddCollaboratorOption) + collaborator, err := models.GetUserByName(ctx.Params(":collaborator")) if err != nil { if models.IsErrUserNotExist(err) { diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 8c877285a..a16cca0f4 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -69,7 +69,11 @@ func getCommit(ctx *context.APIContext, identifier string) { defer gitRepo.Close() commit, err := gitRepo.GetCommit(identifier) if err != nil { - ctx.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) + if git.IsErrNotExist(err) { + ctx.NotFound(identifier) + return + } + ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err) return } diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index bc85fec63..044d0fe56 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/repo" ) @@ -167,7 +168,7 @@ func canReadFiles(r *context.Repository) bool { } // CreateFile handles API call for creating a file -func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { +func CreateFile(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile // --- // summary: Create a file in a repository @@ -206,6 +207,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { // "422": // "$ref": "#/responses/error" + apiOpts := web.GetForm(ctx).(*api.CreateFileOptions) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) } @@ -253,7 +255,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { } // UpdateFile handles API call for updating a file -func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) { +func UpdateFile(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile // --- // summary: Update a file in a repository @@ -291,7 +293,7 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) { // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/error" - + apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) } @@ -377,7 +379,7 @@ func createOrUpdateFile(ctx *context.APIContext, opts *repofiles.UpdateRepoFileO } // DeleteFile Delete a fle in a repository -func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { +func DeleteFile(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/contents/{filepath} repository repoDeleteFile // --- // summary: Delete a file in a repository @@ -416,6 +418,7 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { // "404": // "$ref": "#/responses/error" + apiOpts := web.GetForm(ctx).(*api.DeleteFileOptions) if !canWriteFiles(ctx.Repo) { ctx.Error(http.StatusForbidden, "DeleteFile", models.ErrUserDoesNotHaveAccessToRepo{ UserID: ctx.User.ID, diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 24700f7a7..55fcefacc 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -65,7 +66,7 @@ func ListForks(ctx *context.APIContext) { } // CreateFork create a fork of a repo -func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { +func CreateFork(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/forks repository createFork // --- // summary: Fork a repository @@ -94,6 +95,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateForkOption) repo := ctx.Repo.Repository var forker *models.User // user/org that will own the fork if form.Organization == nil { diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go index 0c538ac6b..0b4e09cad 100644 --- a/routers/api/v1/repo/git_hook.go +++ b/routers/api/v1/repo/git_hook.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" ) // ListGitHooks list all Git hooks of a repository @@ -91,7 +92,7 @@ func GetGitHook(ctx *context.APIContext) { } // EditGitHook modify a Git hook of a repository -func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) { +func EditGitHook(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/hooks/git/{id} repository repoEditGitHook // --- // summary: Edit a Git hook in a repository @@ -123,6 +124,7 @@ func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) { // "404": // "$ref": "#/responses/notFound" + form := web.GetForm(ctx).(*api.EditGitHookOption) hookID := ctx.Params(":id") hook, err := ctx.Repo.GitRepo.GetHook(hookID) if err != nil { diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 575b1fc48..520a7a020 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/webhook" ) @@ -158,7 +159,7 @@ func TestHook(ctx *context.APIContext) { } // CreateHook create a hook for a repository -func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { +func CreateHook(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/hooks repository repoCreateHook // --- // summary: Create a hook @@ -184,14 +185,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { // responses: // "201": // "$ref": "#/responses/Hook" - if !utils.CheckCreateHookOption(ctx, &form) { + form := web.GetForm(ctx).(*api.CreateHookOption) + + if !utils.CheckCreateHookOption(ctx, form) { return } - utils.AddRepoHook(ctx, &form) + utils.AddRepoHook(ctx, form) } // EditHook modify a hook of a repository -func EditHook(ctx *context.APIContext, form api.EditHookOption) { +func EditHook(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/hooks/{id} repository repoEditHook // --- // summary: Edit a hook in a repository @@ -221,8 +224,9 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // responses: // "200": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.EditHookOption) hookID := ctx.ParamsInt64(":id") - utils.EditRepoHook(ctx, &form, hookID) + utils.EditRepoHook(ctx, form, hookID) } // DeleteHook delete a hook of a repository diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index bab8f373c..17dad9794 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -22,6 +22,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" ) @@ -448,7 +449,7 @@ func GetIssue(ctx *context.APIContext) { } // CreateIssue create an issue of a repository -func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { +func CreateIssue(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue // --- // summary: Create an issue. If using deadline only the date will be taken into account, and time of day ignored. @@ -480,7 +481,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { // "$ref": "#/responses/error" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateIssueOption) var deadlineUnix timeutil.TimeStamp if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) { deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix()) @@ -564,7 +565,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { } // EditIssue modify an issue of a repository -func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { +func EditIssue(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue // --- // summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored. @@ -603,6 +604,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { // "412": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*api.EditIssueOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -723,7 +725,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { } // UpdateIssueDeadline updates an issue deadline -func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { +func UpdateIssueDeadline(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline // --- // summary: Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored. @@ -759,7 +761,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - + form := web.GetForm(ctx).(*api.EditDeadlineOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index af69ae981..d62ca8131 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" comment_service "code.gitea.io/gitea/services/comments" ) @@ -174,7 +175,7 @@ func ListRepoIssueComments(ctx *context.APIContext) { } // CreateIssueComment create a comment for an issue -func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOption) { +func CreateIssueComment(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment // --- // summary: Add a comment to an issue @@ -208,7 +209,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti // "$ref": "#/responses/Comment" // "403": // "$ref": "#/responses/forbidden" - + form := web.GetForm(ctx).(*api.CreateIssueCommentOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) @@ -298,7 +299,7 @@ func GetIssueComment(ctx *context.APIContext) { } // EditIssueComment modify a comment of an issue -func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) { +func EditIssueComment(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment // --- // summary: Edit a comment @@ -337,11 +338,12 @@ func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) // "404": // "$ref": "#/responses/notFound" - editIssueComment(ctx, form) + form := web.GetForm(ctx).(*api.EditIssueCommentOption) + editIssueComment(ctx, *form) } // EditIssueCommentDeprecated modify a comment of an issue -func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueCommentOption) { +func EditIssueCommentDeprecated(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated // --- // summary: Edit a comment @@ -386,7 +388,8 @@ func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueComme // "404": // "$ref": "#/responses/notFound" - editIssueComment(ctx, form) + form := web.GetForm(ctx).(*api.EditIssueCommentOption) + editIssueComment(ctx, *form) } func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) { diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 8b2a1988f..d7f64b2d9 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" issue_service "code.gitea.io/gitea/services/issue" ) @@ -64,7 +65,7 @@ func ListIssueLabels(ctx *context.APIContext) { } // AddIssueLabels add labels for an issue -func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { +func AddIssueLabels(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel // --- // summary: Add a label to an issue @@ -99,7 +100,8 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // "403": // "$ref": "#/responses/forbidden" - issue, labels, err := prepareForReplaceOrAdd(ctx, form) + form := web.GetForm(ctx).(*api.IssueLabelsOption) + issue, labels, err := prepareForReplaceOrAdd(ctx, *form) if err != nil { return } @@ -190,7 +192,7 @@ func DeleteIssueLabel(ctx *context.APIContext) { } // ReplaceIssueLabels replace labels for an issue -func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { +func ReplaceIssueLabels(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels // --- // summary: Replace an issue's labels @@ -224,8 +226,8 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // "$ref": "#/responses/LabelList" // "403": // "$ref": "#/responses/forbidden" - - issue, labels, err := prepareForReplaceOrAdd(ctx, form) + form := web.GetForm(ctx).(*api.IssueLabelsOption) + issue, labels, err := prepareForReplaceOrAdd(ctx, *form) if err != nil { return } diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go index dfe618480..3994c6b94 100644 --- a/routers/api/v1/repo/issue_reaction.go +++ b/routers/api/v1/repo/issue_reaction.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -90,7 +91,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) { } // PostIssueCommentReaction add a reaction to a comment of an issue -func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) { +func PostIssueCommentReaction(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction // --- // summary: Add a reaction to a comment of an issue @@ -127,11 +128,13 @@ func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOpti // "403": // "$ref": "#/responses/forbidden" - changeIssueCommentReaction(ctx, form, true) + form := web.GetForm(ctx).(*api.EditReactionOption) + + changeIssueCommentReaction(ctx, *form, true) } // DeleteIssueCommentReaction remove a reaction from a comment of an issue -func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) { +func DeleteIssueCommentReaction(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction // --- // summary: Remove a reaction from a comment of an issue @@ -166,7 +169,9 @@ func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp // "403": // "$ref": "#/responses/forbidden" - changeIssueCommentReaction(ctx, form, false) + form := web.GetForm(ctx).(*api.EditReactionOption) + + changeIssueCommentReaction(ctx, *form, false) } func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) { @@ -304,7 +309,7 @@ func GetIssueReactions(ctx *context.APIContext) { } // PostIssueReaction add a reaction to an issue -func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { +func PostIssueReaction(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction // --- // summary: Add a reaction to an issue @@ -340,12 +345,12 @@ func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { // "$ref": "#/responses/Reaction" // "403": // "$ref": "#/responses/forbidden" - - changeIssueReaction(ctx, form, true) + form := web.GetForm(ctx).(*api.EditReactionOption) + changeIssueReaction(ctx, *form, true) } // DeleteIssueReaction remove a reaction from an issue -func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { +func DeleteIssueReaction(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction // --- // summary: Remove a reaction from an issue @@ -379,8 +384,8 @@ func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" - - changeIssueReaction(ctx, form, false) + form := web.GetForm(ctx).(*api.EditReactionOption) + changeIssueReaction(ctx, *form, false) } func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) { diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 70ce420b7..642704800 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -132,7 +133,7 @@ func ListTrackedTimes(ctx *context.APIContext) { } // AddTime add time manual to the given issue -func AddTime(ctx *context.APIContext, form api.AddTimeOption) { +func AddTime(ctx *context.APIContext) { // swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime // --- // summary: Add tracked time to a issue @@ -168,7 +169,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) { // "$ref": "#/responses/error" // "403": // "$ref": "#/responses/forbidden" - + form := web.GetForm(ctx).(*api.AddTimeOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 3e6174f62..b1b465ca1 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -185,7 +186,7 @@ func HandleAddKeyError(ctx *context.APIContext, err error) { } // CreateDeployKey create deploy key for a repository -func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreateDeployKey(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/keys repository repoCreateKey // --- // summary: Add a key to a repository @@ -214,6 +215,7 @@ func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateKeyOption) content, err := models.CheckPublicKeyString(form.Key) if err != nil { HandleCheckKeyStringError(ctx, err) diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index fef6ebf07..f71683f6e 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -108,7 +109,7 @@ func GetLabel(ctx *context.APIContext) { } // CreateLabel create a label for a repository -func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { +func CreateLabel(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/labels issue issueCreateLabel // --- // summary: Create a label @@ -137,6 +138,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateLabelOption) form.Color = strings.Trim(form.Color, " ") if len(form.Color) == 6 { form.Color = "#" + form.Color @@ -160,7 +162,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { } // EditLabel modify a label for a repository -func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { +func EditLabel(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/labels/{id} issue issueEditLabel // --- // summary: Update a label @@ -195,6 +197,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.EditLabelOption) label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrRepoLabelNotExist(err) { diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 0b829c9df..61cd12b99 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -12,9 +12,9 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migrations" @@ -24,10 +24,11 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) // Migrate migrate remote git repository to gitea -func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { +func Migrate(ctx *context.APIContext) { // swagger:operation POST /repos/migrate repository repoMigrate // --- // summary: Migrate a remote git repository @@ -48,6 +49,8 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.MigrateRepoOptions) + //get repoOwner var ( repoOwner *models.User diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index db86d0868..fc8b4efdb 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -110,7 +111,7 @@ func GetMilestone(ctx *context.APIContext) { } // CreateMilestone create a milestone for a repository -func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { +func CreateMilestone(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/milestones issue issueCreateMilestone // --- // summary: Create a milestone @@ -136,6 +137,7 @@ func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { // responses: // "201": // "$ref": "#/responses/Milestone" + form := web.GetForm(ctx).(*api.CreateMilestoneOption) if form.Deadline == nil { defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local) @@ -162,7 +164,7 @@ func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { } // EditMilestone modify a milestone for a repository by ID and if not available by name -func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { +func EditMilestone(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/milestones/{id} issue issueEditMilestone // --- // summary: Update a milestone @@ -193,7 +195,7 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { // responses: // "200": // "$ref": "#/responses/Milestone" - + form := web.GetForm(ctx).(*api.EditMilestoneOption) milestone := getMilestoneByIDOrName(ctx) if ctx.Written() { return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index b2b71180a..38dac3655 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -11,21 +11,22 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" ) // ListPullRequests returns a list of all PRs -func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) { +func ListPullRequests(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/pulls repository repoListPullRequests // --- // summary: List a repo's pull requests @@ -253,7 +254,7 @@ func DownloadPullDiffOrPatch(ctx *context.APIContext, patch bool) { } // CreatePullRequest does what it says -func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption) { +func CreatePullRequest(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls repository repoCreatePullRequest // --- // summary: Create a pull request @@ -284,6 +285,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption // "422": // "$ref": "#/responses/validationError" + form := *web.GetForm(ctx).(*api.CreatePullRequestOption) if form.Head == form.Base { ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame", "Invalid PullRequest: There are no changes between the head and the base") @@ -437,7 +439,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption } // EditPullRequest does what it says -func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { +func EditPullRequest(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest // --- // summary: Update a pull request. If using deadline only the date will be taken into account, and time of day ignored. @@ -478,6 +480,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.EditPullRequestOption) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { @@ -685,7 +688,7 @@ func IsPullRequestMerged(ctx *context.APIContext) { } // MergePullRequest merges a PR given an index -func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { +func MergePullRequest(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest // --- // summary: Merge a pull request @@ -720,6 +723,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { // "409": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*auth.MergePullRequestForm) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 9e7fd1566..d39db4c66 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -258,7 +259,7 @@ func DeletePullReview(ctx *context.APIContext) { } // CreatePullReview create a review to an pull request -func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) { +func CreatePullReview(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews repository repoCreatePullReview // --- // summary: Create a review to an pull request @@ -294,6 +295,7 @@ func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.CreatePullReviewOptions) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { @@ -373,7 +375,7 @@ func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) } // SubmitPullReview submit a pending review to an pull request -func SubmitPullReview(ctx *context.APIContext, opts api.SubmitPullReviewOptions) { +func SubmitPullReview(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoSubmitPullReview // --- // summary: Submit a pending review to an pull request @@ -415,6 +417,7 @@ func SubmitPullReview(ctx *context.APIContext, opts api.SubmitPullReviewOptions) // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.SubmitPullReviewOptions) review, pr, isWrong := prepareSingleReview(ctx) if isWrong { return @@ -542,7 +545,7 @@ func prepareSingleReview(ctx *context.APIContext) (*models.Review, *models.PullR } // CreateReviewRequests create review requests to an pull request -func CreateReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOptions) { +func CreateReviewRequests(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests // --- // summary: create review requests for a pull request @@ -577,11 +580,13 @@ func CreateReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOpt // "$ref": "#/responses/validationError" // "404": // "$ref": "#/responses/notFound" - apiReviewRequest(ctx, opts, true) + + opts := web.GetForm(ctx).(*api.PullReviewRequestOptions) + apiReviewRequest(ctx, *opts, true) } // DeleteReviewRequests delete review requests to an pull request -func DeleteReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOptions) { +func DeleteReviewRequests(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests // --- // summary: cancel review requests for a pull request @@ -616,7 +621,8 @@ func DeleteReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOpt // "$ref": "#/responses/validationError" // "404": // "$ref": "#/responses/notFound" - apiReviewRequest(ctx, opts, false) + opts := web.GetForm(ctx).(*api.PullReviewRequestOptions) + apiReviewRequest(ctx, *opts, false) } func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions, isAdd bool) { diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 358cc0114..08d92e6c0 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" releaseservice "code.gitea.io/gitea/services/release" ) @@ -124,7 +125,7 @@ func ListReleases(ctx *context.APIContext) { } // CreateRelease create a release -func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { +func CreateRelease(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/releases repository repoCreateRelease // --- // summary: Create a release @@ -154,7 +155,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { // "$ref": "#/responses/notFound" // "409": // "$ref": "#/responses/error" - + form := web.GetForm(ctx).(*api.CreateReleaseOption) rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName) if err != nil { if !models.IsErrReleaseNotExist(err) { @@ -210,7 +211,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { } // EditRelease edit a release -func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { +func EditRelease(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id} repository repoEditRelease // --- // summary: Update a release @@ -245,6 +246,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { // "404": // "$ref": "#/responses/notFound" + form := web.GetForm(ctx).(*api.EditReleaseOption) id := ctx.ParamsInt64(":id") rel, err := models.GetReleaseByID(id) if err != nil && !models.IsErrReleaseNotExist(err) { diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 51e1b160d..0a6425cdd 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/web" ) // GetReleaseAttachment gets a single attachment of the release @@ -168,7 +169,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // Get uploaded file from request - file, header, err := ctx.GetFile("attachment") + file, header, err := ctx.Req.FormFile("attachment") if err != nil { ctx.Error(http.StatusInternalServerError, "GetFile", err) return @@ -208,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // EditReleaseAttachment updates the given attachment -func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptions) { +func EditReleaseAttachment(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment // --- // summary: Edit a release attachment @@ -247,6 +248,8 @@ func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptio // "201": // "$ref": "#/responses/Attachment" + form := web.GetForm(ctx).(*api.EditAttachmentOptions) + // Check if release exists an load release releaseID := ctx.ParamsInt64(":id") attachID := ctx.ParamsInt64(":asset") diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 82d380a81..375f2418b 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -20,6 +20,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -277,7 +278,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR } // Create one repository of mine -func Create(ctx *context.APIContext, opt api.CreateRepoOption) { +func Create(ctx *context.APIContext) { // swagger:operation POST /user/repos repository user createCurrentUserRepo // --- // summary: Create a repository @@ -297,17 +298,17 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) { // description: The repository with the same name already exists. // "422": // "$ref": "#/responses/validationError" - + opt := web.GetForm(ctx).(*api.CreateRepoOption) if ctx.User.IsOrganization() { // Shouldn't reach this condition, but just in case. ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization") return } - CreateUserRepo(ctx, ctx.User, opt) + CreateUserRepo(ctx, ctx.User, *opt) } // CreateOrgRepoDeprecated create one repository of the organization -func CreateOrgRepoDeprecated(ctx *context.APIContext, opt api.CreateRepoOption) { +func CreateOrgRepoDeprecated(ctx *context.APIContext) { // swagger:operation POST /org/{org}/repos organization createOrgRepoDeprecated // --- // summary: Create a repository in an organization @@ -334,11 +335,11 @@ func CreateOrgRepoDeprecated(ctx *context.APIContext, opt api.CreateRepoOption) // "403": // "$ref": "#/responses/forbidden" - CreateOrgRepo(ctx, opt) + CreateOrgRepo(ctx) } // CreateOrgRepo create one repository of the organization -func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { +func CreateOrgRepo(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/repos organization createOrgRepo // --- // summary: Create a repository in an organization @@ -363,7 +364,7 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { // "$ref": "#/responses/notFound" // "403": // "$ref": "#/responses/forbidden" - + opt := web.GetForm(ctx).(*api.CreateRepoOption) org, err := models.GetOrgByName(ctx.Params(":org")) if err != nil { if models.IsErrOrgNotExist(err) { @@ -389,7 +390,7 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { return } } - CreateUserRepo(ctx, org, opt) + CreateUserRepo(ctx, org, *opt) } // Get one repository @@ -457,7 +458,7 @@ func GetByID(ctx *context.APIContext) { } // Edit edit repository properties -func Edit(ctx *context.APIContext, opts api.EditRepoOption) { +func Edit(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit // --- // summary: Edit a repository's properties. Only fields that are set will be changed. @@ -488,6 +489,8 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) { // "422": // "$ref": "#/responses/validationError" + opts := *web.GetForm(ctx).(*api.EditRepoOption) + if err := updateBasicProperties(ctx, opts); err != nil { return } diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 053134ec6..a1bd3e85d 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -53,7 +54,9 @@ func TestRepoEdit(t *testing.T) { Archived: &archived, } - Edit(&context.APIContext{Context: ctx, Org: nil}, opts) + var apiCtx = &context.APIContext{Context: ctx, Org: nil} + web.SetForm(apiCtx, &opts) + Edit(apiCtx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Repository{ @@ -73,7 +76,9 @@ func TestRepoEditNameChange(t *testing.T) { Name: &name, } - Edit(&context.APIContext{Context: ctx, Org: nil}, opts) + var apiCtx = &context.APIContext{Context: ctx, Org: nil} + web.SetForm(apiCtx, &opts) + Edit(apiCtx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Repository{ diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index 9c0d902f7..7ab399b57 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -13,11 +13,12 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) // NewCommitStatus creates a new CommitStatus -func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) { +func NewCommitStatus(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/statuses/{sha} repository repoCreateStatus // --- // summary: Create a commit status @@ -49,6 +50,7 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) { // "400": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*api.CreateStatusOption) sha := ctx.Params("sha") if len(sha) == 0 { ctx.Error(http.StatusBadRequest, "sha not given", nil) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 41fa37a2c..c612c2942 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -66,7 +67,7 @@ func ListTopics(ctx *context.APIContext) { } // UpdateTopics updates repo with a new set of topics -func UpdateTopics(ctx *context.APIContext, form api.RepoTopicOptions) { +func UpdateTopics(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/topics repository repoUpdateTopics // --- // summary: Replace list of topics for a repository @@ -93,6 +94,7 @@ func UpdateTopics(ctx *context.APIContext, form api.RepoTopicOptions) { // "422": // "$ref": "#/responses/invalidTopicsError" + form := web.GetForm(ctx).(*api.RepoTopicOptions) topicNames := form.Topics validTopics, invalidTopics := models.SanitizeAndValidateTopics(topicNames) diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index a0999a6ce..656ace032 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -14,11 +14,12 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" repo_service "code.gitea.io/gitea/services/repository" ) // Transfer transfers the ownership of a repository -func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { +func Transfer(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/transfer repository repoTransfer // --- // summary: Transfer a repo ownership @@ -51,6 +52,8 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.TransferRepoOption) + newOwner, err := models.GetUserByName(opts.NewOwner) if err != nil { if models.IsErrUserNotExist(err) { diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index a3bb9cc65..8919a969e 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -5,7 +5,7 @@ package swagger import ( - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" api "code.gitea.io/gitea/modules/structs" ) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 547730ea5..33b27d60e 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -61,7 +62,7 @@ func ListAccessTokens(ctx *context.APIContext) { } // CreateAccessToken create access tokens -func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption) { +func CreateAccessToken(ctx *context.APIContext) { // swagger:operation POST /users/{username}/tokens user userCreateToken // --- // summary: Create an access token @@ -88,6 +89,8 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption // "201": // "$ref": "#/responses/AccessToken" + form := web.GetForm(ctx).(*api.CreateAccessTokenOption) + t := &models.AccessToken{ UID: ctx.User.ID, Name: form.Name, @@ -181,7 +184,7 @@ func DeleteAccessToken(ctx *context.APIContext) { } // CreateOauth2Application is the handler to create a new OAuth2 Application for the authenticated user -func CreateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2ApplicationOptions) { +func CreateOauth2Application(ctx *context.APIContext) { // swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application // --- // summary: creates a new OAuth2 application @@ -196,6 +199,9 @@ func CreateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2Appli // responses: // "201": // "$ref": "#/responses/OAuth2Application" + + data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) + app, err := models.CreateOAuth2Application(models.CreateOAuth2ApplicationOptions{ Name: data.Name, UserID: ctx.User.ID, @@ -309,7 +315,7 @@ func GetOauth2Application(ctx *context.APIContext) { } // UpdateOauth2Application update OAuth2 Application -func UpdateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2ApplicationOptions) { +func UpdateOauth2Application(ctx *context.APIContext) { // swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application // --- // summary: update an OAuth2 Application, this includes regenerating the client secret @@ -332,6 +338,8 @@ func UpdateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2Appli // "$ref": "#/responses/OAuth2Application" appID := ctx.ParamsInt64(":id") + data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) + app, err := models.UpdateOAuth2Application(models.UpdateOAuth2ApplicationOptions{ Name: data.Name, UserID: ctx.User.ID, diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index d848f5e58..bc8b2fa87 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" ) // ListEmails list all of the authenticated user's email addresses @@ -40,7 +41,7 @@ func ListEmails(ctx *context.APIContext) { } // AddEmail add an email address -func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { +func AddEmail(ctx *context.APIContext) { // swagger:operation POST /user/emails user userAddEmail // --- // summary: Add email addresses @@ -61,7 +62,7 @@ func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { // "$ref": "#/responses/EmailList" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateEmailOption) if len(form.Emails) == 0 { ctx.Error(http.StatusUnprocessableEntity, "", "Email list empty") return @@ -96,7 +97,7 @@ func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { } // DeleteEmail delete email -func DeleteEmail(ctx *context.APIContext, form api.DeleteEmailOption) { +func DeleteEmail(ctx *context.APIContext) { // swagger:operation DELETE /user/emails user userDeleteEmail // --- // summary: Delete email addresses @@ -110,7 +111,7 @@ func DeleteEmail(ctx *context.APIContext, form api.DeleteEmailOption) { // responses: // "204": // "$ref": "#/responses/empty" - + form := web.GetForm(ctx).(*api.DeleteEmailOption) if len(form.Emails) == 0 { ctx.Status(http.StatusNoContent) return diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 7290ef485..51bcaeacc 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -133,7 +134,7 @@ type swaggerUserCurrentPostGPGKey struct { } //CreateGPGKey create a GPG key belonging to the authenticated user -func CreateGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption) { +func CreateGPGKey(ctx *context.APIContext) { // swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey // --- // summary: Create a GPG key @@ -149,7 +150,8 @@ func CreateGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption) { // "422": // "$ref": "#/responses/validationError" - CreateUserGPGKey(ctx, form, ctx.User.ID) + form := web.GetForm(ctx).(*api.CreateGPGKeyOption) + CreateUserGPGKey(ctx, *form, ctx.User.ID) } //DeleteGPGKey remove a GPG key belonging to the authenticated user diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index fa16df183..df8a11c61 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -204,7 +205,7 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid } // CreatePublicKey create one public key for me -func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /user/keys user userCurrentPostKey // --- // summary: Create a public key @@ -223,7 +224,8 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { // "422": // "$ref": "#/responses/validationError" - CreateUserPublicKey(ctx, form, ctx.User.ID) + form := web.GetForm(ctx).(*api.CreateKeyOption) + CreateUserPublicKey(ctx, *form, ctx.User.ID) } // DeletePublicKey delete one public key diff --git a/routers/init.go b/routers/init.go index 79e2f9130..9d13bc9ed 100644 --- a/routers/init.go +++ b/routers/init.go @@ -37,22 +37,16 @@ import ( pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/webhook" - - "gitea.com/macaron/macaron" ) func checkRunMode() { switch setting.RunMode { - case "dev": - git.Debug = true - case "test": + case "dev", "test": git.Debug = true default: - macaron.Env = macaron.PROD - macaron.ColorLog = false git.Debug = false } - log.Info("Run Mode: %s", strings.Title(macaron.Env)) + log.Info("Run Mode: %s", strings.Title(setting.RunMode)) } // NewServices init new services diff --git a/routers/install.go b/routers/install.go index 50e929b6f..5dcd1d48a 100644 --- a/routers/install.go +++ b/routers/install.go @@ -11,18 +11,23 @@ import ( "os/exec" "path/filepath" "strings" + "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "gitea.com/go-chi/session" "gopkg.in/ini.v1" ) @@ -33,17 +38,39 @@ const ( ) // InstallInit prepare for rendering installation page -func InstallInit(ctx *context.Context) { - if setting.InstallLock { - ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") - ctx.HTML(200, tplPostInstall) - return - } - - ctx.Data["Title"] = ctx.Tr("install.install") - ctx.Data["PageIsInstall"] = true +func InstallInit(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() - ctx.Data["DbOptions"] = setting.SupportedDatabases + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + if setting.InstallLock { + resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") + _ = rnd.HTML(resp, 200, string(tplPostInstall), nil) + return + } + var locale = middlewares.Locale(resp, req) + var startTime = time.Now() + var ctx = context.Context{ + Resp: context.NewResponse(resp), + Flash: &middlewares.Flash{}, + Locale: locale, + Render: rnd, + Session: session.GetSession(req), + Data: map[string]interface{}{ + "Title": locale.Tr("install.install"), + "PageIsInstall": true, + "DbOptions": setting.SupportedDatabases, + "i18n": locale, + "Language": locale.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "PageStartTime": startTime, + "TmplLoadTimes": func() string { + return time.Since(startTime).String() + }, + }, + } + ctx.Req = context.WithContext(req, &ctx) + next.ServeHTTP(resp, ctx.Req) + }) } // Install render installation page @@ -116,12 +143,13 @@ func Install(ctx *context.Context) { form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress - auth.AssignForm(form, ctx.Data) + middlewares.AssignForm(form, ctx.Data) ctx.HTML(200, tplInstall) } // InstallPost response for submit install items -func InstallPost(ctx *context.Context, form auth.InstallForm) { +func InstallPost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.InstallForm) var err error ctx.Data["CurDbOption"] = form.DbType diff --git a/routers/org/org.go b/routers/org/org.go index 85bc25217..98a327a97 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -9,11 +9,12 @@ import ( "errors" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -33,7 +34,8 @@ func Create(ctx *context.Context) { } // CreatePost response for create organization -func CreatePost(ctx *context.Context, form auth.CreateOrgForm) { +func CreatePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.CreateOrgForm) ctx.Data["Title"] = ctx.Tr("new_org") if !ctx.User.CanCreateOrganization() { diff --git a/routers/org/org_labels.go b/routers/org/org_labels.go index e5b9d9dde..554f86c96 100644 --- a/routers/org/org_labels.go +++ b/routers/org/org_labels.go @@ -6,8 +6,9 @@ package org import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // RetrieveLabels find all the labels of an organization @@ -26,7 +27,8 @@ func RetrieveLabels(ctx *context.Context) { } // NewLabel create new label for organization -func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { +func NewLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsLabels"] = true @@ -50,7 +52,8 @@ func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { } // UpdateLabel update a label's name and color -func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) { +func UpdateLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) l, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, form.ID) if err != nil { switch { @@ -86,7 +89,8 @@ func DeleteLabel(ctx *context.Context) { } // InitializeLabels init labels for an organization -func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { +func InitializeLabels(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.InitializeLabelsForm) if ctx.HasError() { ctx.Redirect(ctx.Repo.RepoLink + "/labels") return diff --git a/routers/org/setting.go b/routers/org/setting.go index 05075ca82..ac1206625 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -9,11 +9,12 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" userSetting "code.gitea.io/gitea/routers/user/setting" ) @@ -38,7 +39,8 @@ func Settings(ctx *context.Context) { } // SettingsPost response for settings change submited -func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { +func SettingsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateOrgSettingForm) ctx.Data["Title"] = ctx.Tr("org.settings") ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility @@ -115,7 +117,8 @@ func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { } // SettingsAvatar response for change avatar on settings page -func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) { +func SettingsAvatar(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) form.Source = auth.AvatarLocal if err := userSetting.UpdateAvatarSetting(ctx, form, ctx.Org.Organization); err != nil { ctx.Flash.Error(err.Error()) diff --git a/routers/org/teams.go b/routers/org/teams.go index fa98add60..cfa49d4e9 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -11,10 +11,11 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" ) @@ -186,7 +187,8 @@ func NewTeam(ctx *context.Context) { } // NewTeamPost response for create new team -func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { +func NewTeamPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateTeamForm) ctx.Data["Title"] = ctx.Org.Organization.FullName ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeamsNew"] = true @@ -274,7 +276,8 @@ func EditTeam(ctx *context.Context) { } // EditTeamPost response for modify team information -func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { +func EditTeamPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateTeamForm) t := ctx.Org.Team ctx.Data["Title"] = ctx.Org.Organization.FullName ctx.Data["PageIsOrgTeams"] = true diff --git a/routers/private/hook.go b/routers/private/hook.go index 34e849f6f..853d3069e 100644 --- a/routers/private/hook.go +++ b/routers/private/hook.go @@ -15,16 +15,16 @@ import ( "strings" "code.gitea.io/gitea/models" + gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" - - "gitea.com/macaron/macaron" ) func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []string) error { @@ -117,7 +117,8 @@ func isErrUnverifiedCommit(err error) bool { } // HookPreReceive checks whether a individual commit is acceptable -func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) { +func HookPreReceive(ctx *gitea_context.PrivateContext) { + opts := web.GetForm(ctx).(*private.HookOptions) ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) @@ -370,7 +371,8 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) { } // HookPostReceive updates services and users -func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { +func HookPostReceive(ctx *gitea_context.PrivateContext) { + opts := web.GetForm(ctx).(*private.HookOptions) ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") @@ -540,7 +542,7 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { } // SetDefaultBranch updates the default branch -func SetDefaultBranch(ctx *macaron.Context) { +func SetDefaultBranch(ctx *gitea_context.PrivateContext) { ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") branch := ctx.Params(":branch") diff --git a/routers/private/internal.go b/routers/private/internal.go index 4fb267a49..e541591a3 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -6,47 +6,69 @@ package private import ( + "net/http" + "reflect" "strings" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" ) // CheckInternalToken check internal token is set -func CheckInternalToken(ctx *macaron.Context) { - tokens := ctx.Req.Header.Get("Authorization") - fields := strings.Fields(tokens) - if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { - log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens) - ctx.Error(403) +func CheckInternalToken(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + tokens := req.Header.Get("Authorization") + fields := strings.Fields(tokens) + if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { + log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens) + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + } else { + next.ServeHTTP(w, req) + } + }) +} + +// bind binding an obj to a handler +func bind(obj interface{}) http.HandlerFunc { + var tp = reflect.TypeOf(obj) + for tp.Kind() == reflect.Ptr { + tp = tp.Elem() } + return web.Wrap(func(ctx *context.PrivateContext) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + binding.Bind(ctx.Req, theObj) + web.SetForm(ctx, theObj) + }) } -// RegisterRoutes registers all internal APIs routes to web application. +// Routes registers all internal APIs routes to web application. // These APIs will be invoked by internal commands for example `gitea serv` and etc. -func RegisterRoutes(m *macaron.Macaron) { - bind := binding.Bind - - m.Group("/", func() { - m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent) - m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo) - m.Post("/hook/pre-receive/:owner/:repo", bind(private.HookOptions{}), HookPreReceive) - m.Post("/hook/post-receive/:owner/:repo", bind(private.HookOptions{}), HookPostReceive) - m.Post("/hook/set-default-branch/:owner/:repo/:branch", SetDefaultBranch) - m.Get("/serv/none/:keyid", ServNoCommand) - m.Get("/serv/command/:keyid/:owner/:repo", ServCommand) - m.Post("/manager/shutdown", Shutdown) - m.Post("/manager/restart", Restart) - m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) - m.Post("/manager/pause-logging", PauseLogging) - m.Post("/manager/resume-logging", ResumeLogging) - m.Post("/manager/release-and-reopen-logging", ReleaseReopenLogging) - m.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger) - m.Post("/manager/remove-logger/:group/:name", RemoveLogger) - m.Post("/mail/send", SendEmail) - }, CheckInternalToken) +func Routes() *web.Route { + var r = web.NewRoute() + r.Use(context.PrivateContexter()) + r.Use(CheckInternalToken) + + r.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent) + r.Post("/ssh/{id}/update/{repoid}", UpdatePublicKeyInRepo) + r.Post("/hook/pre-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPreReceive) + r.Post("/hook/post-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPostReceive) + r.Post("/hook/set-default-branch/{owner}/{repo}/{branch}", SetDefaultBranch) + r.Get("/serv/none/{keyid}", ServNoCommand) + r.Get("/serv/command/{keyid}/{owner}/{repo}", ServCommand) + r.Post("/manager/shutdown", Shutdown) + r.Post("/manager/restart", Restart) + r.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) + r.Post("/manager/pause-logging", PauseLogging) + r.Post("/manager/resume-logging", ResumeLogging) + r.Post("/manager/release-and-reopen-logging", ReleaseReopenLogging) + r.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger) + r.Post("/manager/remove-logger/{group}/{name}", RemoveLogger) + r.Post("/mail/send", SendEmail) + + return r } diff --git a/routers/private/key.go b/routers/private/key.go index c00330fe8..b90faa22a 100644 --- a/routers/private/key.go +++ b/routers/private/key.go @@ -9,13 +9,12 @@ import ( "net/http" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/timeutil" - - "gitea.com/macaron/macaron" ) // UpdatePublicKeyInRepo update public key and deploy key updates -func UpdatePublicKeyInRepo(ctx *macaron.Context) { +func UpdatePublicKeyInRepo(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":id") repoID := ctx.ParamsInt64(":repoid") if err := models.UpdatePublicKeyUpdated(keyID); err != nil { @@ -49,7 +48,7 @@ func UpdatePublicKeyInRepo(ctx *macaron.Context) { // AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part) // and returns public key found. -func AuthorizedPublicKeyByContent(ctx *macaron.Context) { +func AuthorizedPublicKeyByContent(ctx *context.PrivateContext) { content := ctx.Query("content") publicKey, err := models.SearchPublicKeyByContent(content) diff --git a/routers/private/mail.go b/routers/private/mail.go index b3b21d042..330de14c4 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -11,17 +11,17 @@ import ( "strconv" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/macaron" ) // SendEmail pushes messages to mail queue // // It doesn't wait before each message will be processed -func SendEmail(ctx *macaron.Context) { +func SendEmail(ctx *context.PrivateContext) { if setting.MailService == nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "err": "Mail service is not enabled.", @@ -30,7 +30,7 @@ func SendEmail(ctx *macaron.Context) { } var mail private.Email - rd := ctx.Req.Body().ReadCloser() + rd := ctx.Req.Body defer rd.Close() if err := json.NewDecoder(rd).Decode(&mail); err != nil { log.Error("%v", err) @@ -77,7 +77,7 @@ func SendEmail(ctx *macaron.Context) { sendEmail(ctx, mail.Subject, mail.Message, emails) } -func sendEmail(ctx *macaron.Context, subject, message string, to []string) { +func sendEmail(ctx *context.PrivateContext, subject, message string, to []string) { for _, email := range to { msg := mailer.NewMessage([]string{email}, subject, message) mailer.SendAsync(msg) diff --git a/routers/private/manager.go b/routers/private/manager.go index 67bd92003..e5b4583fd 100644 --- a/routers/private/manager.go +++ b/routers/private/manager.go @@ -9,17 +9,18 @@ import ( "fmt" "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" - - "gitea.com/macaron/macaron" + "code.gitea.io/gitea/modules/web" ) // FlushQueues flushes all the Queues -func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { +func FlushQueues(ctx *context.PrivateContext) { + opts := web.GetForm(ctx).(*private.FlushOptions) if opts.NonBlocking { // Save the hammer ctx here - as a new one is created each time you call this. baseCtx := graceful.GetManager().HammerContext() @@ -34,7 +35,7 @@ func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { }) return } - err := queue.GetManager().FlushAll(ctx.Req.Request.Context(), opts.Timeout) + err := queue.GetManager().FlushAll(ctx.Req.Context(), opts.Timeout) if err != nil { ctx.JSON(http.StatusRequestTimeout, map[string]interface{}{ "err": fmt.Sprintf("%v", err), @@ -44,19 +45,19 @@ func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { } // PauseLogging pauses logging -func PauseLogging(ctx *macaron.Context) { +func PauseLogging(ctx *context.PrivateContext) { log.Pause() ctx.PlainText(http.StatusOK, []byte("success")) } // ResumeLogging resumes logging -func ResumeLogging(ctx *macaron.Context) { +func ResumeLogging(ctx *context.PrivateContext) { log.Resume() ctx.PlainText(http.StatusOK, []byte("success")) } // ReleaseReopenLogging releases and reopens logging files -func ReleaseReopenLogging(ctx *macaron.Context) { +func ReleaseReopenLogging(ctx *context.PrivateContext) { if err := log.ReleaseReopen(); err != nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "err": fmt.Sprintf("Error during release and reopen: %v", err), @@ -67,7 +68,7 @@ func ReleaseReopenLogging(ctx *macaron.Context) { } // RemoveLogger removes a logger -func RemoveLogger(ctx *macaron.Context) { +func RemoveLogger(ctx *context.PrivateContext) { group := ctx.Params("group") name := ctx.Params("name") ok, err := log.GetLogger(group).DelLogger(name) @@ -84,7 +85,8 @@ func RemoveLogger(ctx *macaron.Context) { } // AddLogger adds a logger -func AddLogger(ctx *macaron.Context, opts private.LoggerOptions) { +func AddLogger(ctx *context.PrivateContext) { + opts := web.GetForm(ctx).(*private.LoggerOptions) if len(opts.Group) == 0 { opts.Group = log.DEFAULT } diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go index ec5e97605..60ae9b68e 100644 --- a/routers/private/manager_unix.go +++ b/routers/private/manager_unix.go @@ -9,20 +9,19 @@ package private import ( "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" - - "gitea.com/macaron/macaron" ) // Restart causes the server to perform a graceful restart -func Restart(ctx *macaron.Context) { +func Restart(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulRestart() ctx.PlainText(http.StatusOK, []byte("success")) } // Shutdown causes the server to perform a graceful shutdown -func Shutdown(ctx *macaron.Context) { +func Shutdown(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulShutdown() ctx.PlainText(http.StatusOK, []byte("success")) } diff --git a/routers/private/manager_windows.go b/routers/private/manager_windows.go index ac840a9d8..244dbbe4d 100644 --- a/routers/private/manager_windows.go +++ b/routers/private/manager_windows.go @@ -9,20 +9,19 @@ package private import ( "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" - - "gitea.com/macaron/macaron" ) // Restart is not implemented for Windows based servers as they can't fork -func Restart(ctx *macaron.Context) { +func Restart(ctx *context.PrivateContext) { ctx.JSON(http.StatusNotImplemented, map[string]interface{}{ "err": "windows servers cannot be gracefully restarted - shutdown and restart manually", }) } // Shutdown causes the server to perform a graceful shutdown -func Shutdown(ctx *macaron.Context) { +func Shutdown(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulShutdown() ctx.PlainText(http.StatusOK, []byte("success")) } diff --git a/routers/private/serv.go b/routers/private/serv.go index 90e1d30b0..1461194e7 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -11,17 +11,16 @@ import ( "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" - - "gitea.com/macaron/macaron" ) // ServNoCommand returns information about the provided keyid -func ServNoCommand(ctx *macaron.Context) { +func ServNoCommand(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":keyid") if keyID <= 0 { ctx.JSON(http.StatusBadRequest, map[string]interface{}{ @@ -73,7 +72,7 @@ func ServNoCommand(ctx *macaron.Context) { } // ServCommand returns information about the provided keyid -func ServCommand(ctx *macaron.Context) { +func ServCommand(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":keyid") ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") diff --git a/routers/repo/branch.go b/routers/repo/branch.go index bf9f2e6a3..7d844abe5 100644 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -10,14 +10,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repofiles" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -367,7 +368,8 @@ func getDeletedBranches(ctx *context.Context) ([]*Branch, error) { } // CreateBranch creates new branch in repository -func CreateBranch(ctx *context.Context, form auth.NewBranchForm) { +func CreateBranch(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewBranchForm) if !ctx.Repo.CanCreateBranch() { ctx.NotFound("CreateBranch", nil) return diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 0bc76504f..619912fef 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -12,10 +12,10 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repofiles" @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" ) @@ -325,17 +326,20 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo } // EditFilePost response for editing file -func EditFilePost(ctx *context.Context, form auth.EditRepoFileForm) { - editFilePost(ctx, form, false) +func EditFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditRepoFileForm) + editFilePost(ctx, *form, false) } // NewFilePost response for creating file -func NewFilePost(ctx *context.Context, form auth.EditRepoFileForm) { - editFilePost(ctx, form, true) +func NewFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditRepoFileForm) + editFilePost(ctx, *form, true) } // DiffPreviewPost render preview diff page -func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) { +func DiffPreviewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditPreviewDiffForm) treePath := cleanUploadFileName(ctx.Repo.TreePath) if len(treePath) == 0 { ctx.Error(500, "file name to diff is invalid") @@ -394,7 +398,8 @@ func DeleteFile(ctx *context.Context) { } // DeleteFilePost response for deleting file -func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { +func DeleteFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.DeleteRepoFileForm) canCommit := renderCommitRights(ctx) branchName := ctx.Repo.BranchName if form.CommitChoice == frmCommitChoiceNewBranch { @@ -556,7 +561,8 @@ func UploadFile(ctx *context.Context) { } // UploadFilePost response for uploading file -func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { +func UploadFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UploadRepoFileForm) ctx.Data["PageIsUpload"] = true ctx.Data["RequireTribute"] = true ctx.Data["RequireSimpleMDE"] = true @@ -760,7 +766,8 @@ func UploadFileToServer(ctx *context.Context) { } // RemoveUploadFileFromServer remove file from server file dir -func RemoveUploadFileFromServer(ctx *context.Context, form auth.RemoveUploadFileForm) { +func RemoveUploadFileFromServer(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RemoveUploadFileForm) if len(form.File) == 0 { ctx.Status(204) return diff --git a/routers/repo/http.go b/routers/repo/http.go index 3de45698e..0377979e8 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -35,8 +35,17 @@ import ( repo_service "code.gitea.io/gitea/services/repository" ) -// HTTP implmentation git smart HTTP protocol -func HTTP(ctx *context.Context) { +// httpBase implmentation git smart HTTP protocol +func httpBase(ctx *context.Context) (h *serviceHandler) { + if setting.Repository.DisableHTTPGit { + ctx.Resp.WriteHeader(http.StatusForbidden) + _, err := ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed")) + if err != nil { + log.Error(err.Error()) + } + return + } + if len(setting.Repository.AccessControlAllowOrigin) > 0 { allowedOrigin := setting.Repository.AccessControlAllowOrigin // Set CORS headers for browser-based git clients @@ -344,7 +353,7 @@ func HTTP(ctx *context.Context) { environ = append(environ, models.EnvRepoID+fmt.Sprintf("=%d", repo.ID)) w := ctx.Resp - r := ctx.Req.Request + r := ctx.Req cfg := &serviceConfig{ UploadPack: true, ReceivePack: true, @@ -353,47 +362,9 @@ func HTTP(ctx *context.Context) { r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name - for _, route := range routes { - if m := route.reg.FindStringSubmatch(r.URL.Path); m != nil { - if setting.Repository.DisableHTTPGit { - w.WriteHeader(http.StatusForbidden) - _, err := w.Write([]byte("Interacting with repositories by HTTP protocol is not allowed")) - if err != nil { - log.Error(err.Error()) - } - return - } - if route.method != r.Method { - if r.Proto == "HTTP/1.1" { - w.WriteHeader(http.StatusMethodNotAllowed) - _, err := w.Write([]byte("Method Not Allowed")) - if err != nil { - log.Error(err.Error()) - } - } else { - w.WriteHeader(http.StatusBadRequest) - _, err := w.Write([]byte("Bad Request")) - if err != nil { - log.Error(err.Error()) - } - } - return - } - - file := strings.Replace(r.URL.Path, m[1]+"/", "", 1) - dir, err := getGitRepoPath(m[1]) - if err != nil { - log.Error(err.Error()) - ctx.NotFound("Smart Git HTTP", err) - return - } - - route.handler(serviceHandler{cfg, w, r, dir, file, cfg.Env}) - return - } - } + dir := models.RepoPath(username, reponame) - ctx.NotFound("Smart Git HTTP", nil) + return &serviceHandler{cfg, w, r, dir, cfg.Env} } var ( @@ -449,7 +420,6 @@ type serviceHandler struct { w http.ResponseWriter r *http.Request dir string - file string environ []string } @@ -467,8 +437,8 @@ func (h *serviceHandler) setHeaderCacheForever() { h.w.Header().Set("Cache-Control", "public, max-age=31536000") } -func (h *serviceHandler) sendFile(contentType string) { - reqFile := path.Join(h.dir, h.file) +func (h *serviceHandler) sendFile(contentType, file string) { + reqFile := path.Join(h.dir, file) fi, err := os.Stat(reqFile) if os.IsNotExist(err) { @@ -482,26 +452,6 @@ func (h *serviceHandler) sendFile(contentType string) { http.ServeFile(h.w, h.r, reqFile) } -type route struct { - reg *regexp.Regexp - method string - handler func(serviceHandler) -} - -var routes = []route{ - {regexp.MustCompile(`(.*?)/git-upload-pack$`), "POST", serviceUploadPack}, - {regexp.MustCompile(`(.*?)/git-receive-pack$`), "POST", serviceReceivePack}, - {regexp.MustCompile(`(.*?)/info/refs$`), "GET", getInfoRefs}, - {regexp.MustCompile(`(.*?)/HEAD$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/alternates$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/http-alternates$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/packs$`), "GET", getInfoPacks}, - {regexp.MustCompile(`(.*?)/objects/info/[^/]*$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$`), "GET", getLooseObject}, - {regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.pack$`), "GET", getPackFile}, - {regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.idx$`), "GET", getIdxFile}, -} - // one or more key=value pairs separated by colons var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) @@ -598,12 +548,20 @@ func serviceRPC(h serviceHandler, service string) { } } -func serviceUploadPack(h serviceHandler) { - serviceRPC(h, "upload-pack") +// ServiceUploadPack implements Git Smart HTTP protocol +func ServiceUploadPack(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + serviceRPC(*h, "upload-pack") + } } -func serviceReceivePack(h serviceHandler) { - serviceRPC(h, "receive-pack") +// ServiceReceivePack implements Git Smart HTTP protocol +func ServiceReceivePack(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + serviceRPC(*h, "receive-pack") + } } func getServiceType(r *http.Request) string { @@ -630,9 +588,14 @@ func packetWrite(str string) []byte { return []byte(s + str) } -func getInfoRefs(h serviceHandler) { +// GetInfoRefs implements Git dumb HTTP +func GetInfoRefs(ctx *context.Context) { + h := httpBase(ctx) + if h == nil { + return + } h.setHeaderNoCache() - if hasAccess(getServiceType(h.r), h, false) { + if hasAccess(getServiceType(h.r), *h, false) { service := getServiceType(h.r) if protocol := h.r.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) { @@ -652,44 +615,59 @@ func getInfoRefs(h serviceHandler) { _, _ = h.w.Write(refs) } else { updateServerInfo(h.dir) - h.sendFile("text/plain; charset=utf-8") + h.sendFile("text/plain; charset=utf-8", "info/refs") } } -func getTextFile(h serviceHandler) { - h.setHeaderNoCache() - h.sendFile("text/plain") -} - -func getInfoPacks(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("text/plain; charset=utf-8") -} - -func getLooseObject(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-loose-object") +// GetTextFile implements Git dumb HTTP +func GetTextFile(p string) func(*context.Context) { + return func(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderNoCache() + file := ctx.Params("file") + if file != "" { + h.sendFile("text/plain", "objects/info/"+file) + } else { + h.sendFile("text/plain", p) + } + } + } } -func getPackFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects") +// GetInfoPacks implements Git dumb HTTP +func GetInfoPacks(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("text/plain; charset=utf-8", "objects/info/packs") + } } -func getIdxFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects-toc") +// GetLooseObject implements Git dumb HTTP +func GetLooseObject(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-loose-object", fmt.Sprintf("objects/%s/%s", + ctx.Params("head"), ctx.Params("hash"))) + } } -func getGitRepoPath(subdir string) (string, error) { - if !strings.HasSuffix(subdir, ".git") { - subdir += ".git" +// GetPackFile implements Git dumb HTTP +func GetPackFile(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-packed-objects", "objects/pack/pack-"+ctx.Params("file")+".pack") } +} - fpath := path.Join(setting.RepoRootPath, subdir) - if _, err := os.Stat(fpath); os.IsNotExist(err) { - return "", err +// GetIdxFile implements Git dumb HTTP +func GetIdxFile(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-packed-objects-toc", "objects/pack/pack-"+ctx.Params("file")+".idx") } - - return fpath, nil } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index fbeae75ab..fb7107451 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -16,10 +16,10 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/log" @@ -29,6 +29,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" comment_service "code.gitea.io/gitea/services/comments" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -924,7 +925,8 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b } // NewIssuePost response for creating new issue -func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { +func NewIssuePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateIssueForm) ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["PageIsIssueList"] = true ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0 @@ -940,7 +942,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { attachments []string ) - labelIDs, assigneeIDs, milestoneID, projectID := ValidateRepoMetas(ctx, form, false) + labelIDs, assigneeIDs, milestoneID, projectID := ValidateRepoMetas(ctx, *form, false) if ctx.Written() { return } @@ -1925,7 +1927,8 @@ func UpdateIssueStatus(ctx *context.Context) { } // NewComment create a comment for issue -func NewComment(ctx *context.Context, form auth.CreateCommentForm) { +func NewComment(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateCommentForm) issue := GetActionIssue(ctx) if ctx.Written() { return @@ -2134,7 +2137,8 @@ func DeleteComment(ctx *context.Context) { } // ChangeIssueReaction create a reaction for issue -func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { +func ChangeIssueReaction(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ReactionForm) issue := GetActionIssue(ctx) if ctx.Written() { return @@ -2229,7 +2233,8 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { } // ChangeCommentReaction create a reaction for comment -func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { +func ChangeCommentReaction(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ReactionForm) comment, err := models.GetCommentByID(ctx.ParamsInt64(":id")) if err != nil { ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) diff --git a/routers/repo/issue_label.go b/routers/repo/issue_label.go index f1e188fe3..35035103d 100644 --- a/routers/repo/issue_label.go +++ b/routers/repo/issue_label.go @@ -6,11 +6,12 @@ package repo import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" issue_service "code.gitea.io/gitea/services/issue" ) @@ -29,7 +30,8 @@ func Labels(ctx *context.Context) { } // InitializeLabels init labels for a repository -func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { +func InitializeLabels(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.InitializeLabelsForm) if ctx.HasError() { ctx.Redirect(ctx.Repo.RepoLink + "/labels") return @@ -94,7 +96,8 @@ func RetrieveLabels(ctx *context.Context) { } // NewLabel create new label for repository -func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { +func NewLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsLabels"] = true @@ -118,7 +121,8 @@ func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { } // UpdateLabel update a label's name and color -func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) { +func UpdateLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) l, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, form.ID) if err != nil { switch { diff --git a/routers/repo/issue_label_test.go b/routers/repo/issue_label_test.go index bf6251125..d67c70085 100644 --- a/routers/repo/issue_label_test.go +++ b/routers/repo/issue_label_test.go @@ -10,8 +10,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -32,7 +33,8 @@ func TestInitializeLabels(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/initialize") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 2) - InitializeLabels(ctx, auth.InitializeLabelsForm{TemplateName: "Default"}) + web.SetForm(ctx, &auth.InitializeLabelsForm{TemplateName: "Default"}) + InitializeLabels(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ RepoID: 2, @@ -74,10 +76,11 @@ func TestNewLabel(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/edit") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewLabel(ctx, auth.CreateLabelForm{ + web.SetForm(ctx, &auth.CreateLabelForm{ Title: "newlabel", Color: "#abcdef", }) + NewLabel(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ Name: "newlabel", @@ -91,11 +94,12 @@ func TestUpdateLabel(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/edit") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - UpdateLabel(ctx, auth.CreateLabelForm{ + web.SetForm(ctx, &auth.CreateLabelForm{ ID: 2, Title: "newnameforlabel", Color: "#abcdef", }) + UpdateLabel(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ ID: 2, diff --git a/routers/repo/issue_lock.go b/routers/repo/issue_lock.go index fa8758831..f8131e46a 100644 --- a/routers/repo/issue_lock.go +++ b/routers/repo/issue_lock.go @@ -8,14 +8,15 @@ import ( "net/http" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // LockIssue locks an issue. This would limit commenting abilities to // users with write access to the repo. -func LockIssue(ctx *context.Context, form auth.IssueLockForm) { - +func LockIssue(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.IssueLockForm) issue := GetActionIssue(ctx) if ctx.Written() { return diff --git a/routers/repo/issue_timetrack.go b/routers/repo/issue_timetrack.go index 0f711bc73..425f21511 100644 --- a/routers/repo/issue_timetrack.go +++ b/routers/repo/issue_timetrack.go @@ -9,12 +9,14 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // AddTimeManually tracks time manually -func AddTimeManually(c *context.Context, form auth.AddTimeManuallyForm) { +func AddTimeManually(c *context.Context) { + form := web.GetForm(c).(*auth.AddTimeManuallyForm) issue := GetActionIssue(c) if c.Written() { return diff --git a/routers/repo/migrate.go b/routers/repo/migrate.go index a628fd2e2..89452de0f 100644 --- a/routers/repo/migrate.go +++ b/routers/repo/migrate.go @@ -10,14 +10,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/migrations" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/task" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) const ( @@ -117,7 +118,8 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam } // MigratePost response for migrating from external git repository -func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { +func MigratePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MigrateRepoForm) if setting.Repository.DisableMigrations { ctx.Error(http.StatusForbidden, "MigratePost: the site administrator has disabled migrations") return @@ -192,7 +194,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, false) if err != nil { - handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, &form) + handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) return } @@ -202,5 +204,5 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { return } - handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, &form) + handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) } diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go index 96f5b4e5f..a9beed75d 100644 --- a/routers/repo/milestone.go +++ b/routers/repo/milestone.go @@ -8,14 +8,15 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "xorm.io/builder" ) @@ -106,7 +107,8 @@ func NewMilestone(ctx *context.Context) { } // NewMilestonePost response for creating milestone -func NewMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { +func NewMilestonePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateMilestoneForm) ctx.Data["Title"] = ctx.Tr("repo.milestones.new") ctx.Data["PageIsIssueList"] = true ctx.Data["PageIsMilestones"] = true @@ -165,7 +167,8 @@ func EditMilestone(ctx *context.Context) { } // EditMilestonePost response for edting milestone -func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { +func EditMilestonePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateMilestoneForm) ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") ctx.Data["PageIsMilestones"] = true ctx.Data["PageIsEditMilestone"] = true diff --git a/routers/repo/projects.go b/routers/repo/projects.go index 4cff199b3..49bcfef0c 100644 --- a/routers/repo/projects.go +++ b/routers/repo/projects.go @@ -9,12 +9,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) const ( @@ -112,7 +113,8 @@ func NewProject(ctx *context.Context) { } // NewProjectPost creates a new project -func NewProjectPost(ctx *context.Context, form auth.CreateProjectForm) { +func NewProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateProjectForm) ctx.Data["Title"] = ctx.Tr("repo.projects.new") if ctx.HasError() { @@ -217,7 +219,8 @@ func EditProject(ctx *context.Context) { } // EditProjectPost response for editing a project -func EditProjectPost(ctx *context.Context, form auth.CreateProjectForm) { +func EditProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateProjectForm) ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsProjects"] = true ctx.Data["PageIsEditProjects"] = true @@ -399,8 +402,8 @@ func DeleteProjectBoard(ctx *context.Context) { } // AddBoardToProjectPost allows a new board to be added to a project. -func AddBoardToProjectPost(ctx *context.Context, form auth.EditProjectBoardTitleForm) { - +func AddBoardToProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(models.AccessModeWrite, models.UnitTypeProjects) { ctx.JSON(403, map[string]string{ "message": "Only authorized users are allowed to perform this action.", @@ -479,8 +482,8 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project, } // EditProjectBoardTitle allows a project board's title to be updated -func EditProjectBoardTitle(ctx *context.Context, form auth.EditProjectBoardTitleForm) { - +func EditProjectBoardTitle(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) _, board := checkProjectBoardChangePermissions(ctx) if ctx.Written() { return diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 01c6efaa1..1862c15f4 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -16,17 +16,19 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/notification" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/gitdiff" pull_service "code.gitea.io/gitea/services/pull" @@ -168,7 +170,8 @@ func Fork(ctx *context.Context) { } // ForkPost response for forking a repository -func ForkPost(ctx *context.Context, form auth.CreateRepoForm) { +func ForkPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateRepoForm) ctx.Data["Title"] = ctx.Tr("new_fork") ctxUser := checkContextUser(ctx, form.UID) @@ -765,7 +768,8 @@ func UpdatePullRequest(ctx *context.Context) { } // MergePullRequest response for merging pull request -func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { +func MergePullRequest(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MergePullRequestForm) issue := checkPullInfo(ctx) if ctx.Written() { return @@ -954,7 +958,8 @@ func stopTimerIfAvailable(user *models.User, issue *models.Issue) error { } // CompareAndPullRequestPost response for creating pull request -func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) { +func CompareAndPullRequestPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateIssueForm) ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true @@ -974,7 +979,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) } defer headGitRepo.Close() - labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, form, true) + labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, *form, true) if ctx.Written() { return } @@ -984,7 +989,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) } if ctx.HasError() { - auth.AssignForm(form, ctx.Data) + middlewares.AssignForm(form, ctx.Data) // This stage is already stop creating new pull request, so it does not matter if it has // something to compare or not. diff --git a/routers/repo/pull_review.go b/routers/repo/pull_review.go index 0bacc6823..df49b6cfe 100644 --- a/routers/repo/pull_review.go +++ b/routers/repo/pull_review.go @@ -8,10 +8,11 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" ) @@ -44,7 +45,8 @@ func RenderNewCodeCommentForm(ctx *context.Context) { } // CreateCodeComment will create a code comment including an pending review if required -func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { +func CreateCodeComment(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CodeCommentForm) issue := GetActionIssue(ctx) if !issue.IsPull { return @@ -171,7 +173,8 @@ func renderConversation(ctx *context.Context, comment *models.Comment) { } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) { +func SubmitReview(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SubmitReviewForm) issue := GetActionIssue(ctx) if !issue.IsPull { return diff --git a/routers/repo/release.go b/routers/repo/release.go index 4d75c37c8..54642f9b2 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -9,14 +9,15 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/web" releaseservice "code.gitea.io/gitea/services/release" ) @@ -230,7 +231,8 @@ func NewRelease(ctx *context.Context) { } // NewReleasePost response for creating a release -func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) { +func NewReleasePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewReleaseForm) ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["PageIsReleaseList"] = true @@ -336,7 +338,8 @@ func EditRelease(ctx *context.Context) { } // EditReleasePost response for edit release -func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) { +func EditReleasePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditReleaseForm) ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsEditRelease"] = true diff --git a/routers/repo/release_test.go b/routers/repo/release_test.go index 47d1a89b5..38c0d9fec 100644 --- a/routers/repo/release_test.go +++ b/routers/repo/release_test.go @@ -8,8 +8,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" ) func TestNewReleasePost(t *testing.T) { @@ -48,7 +49,8 @@ func TestNewReleasePost(t *testing.T) { test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) test.LoadGitRepo(t, ctx) - NewReleasePost(ctx, testCase.Form) + web.SetForm(ctx, &testCase.Form) + NewReleasePost(ctx) models.AssertExistsAndLoadBean(t, &models.Release{ RepoID: 1, PublisherID: 2, diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 3832b8997..a8cfb9ad7 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -11,11 +11,12 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" archiver_service "code.gitea.io/gitea/services/archiver" repo_service "code.gitea.io/gitea/services/repository" ) @@ -181,7 +182,8 @@ func handleCreateError(ctx *context.Context, owner *models.User, err error, name } // CreatePost response for creating repository -func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { +func CreatePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateRepoForm) ctx.Data["Title"] = ctx.Tr("new_repo") ctx.Data["Gitignores"] = models.Gitignores diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 368879234..3e22e8804 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -15,9 +15,9 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repository" @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/mailer" mirror_service "code.gitea.io/gitea/services/mirror" @@ -59,7 +60,8 @@ func Settings(ctx *context.Context) { } // SettingsPost response for changes of a repository -func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { +func SettingsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RepoSettingForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsOptions"] = true @@ -839,7 +841,8 @@ func DeployKeys(ctx *context.Context) { } // DeployKeysPost response for adding a deploy key of a repository -func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) { +func DeployKeysPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddKeyForm) ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["PageIsSettingsKeys"] = true @@ -956,9 +959,10 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm) error { } // SettingsAvatar save new POSTed repository avatar -func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) { +func SettingsAvatar(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) form.Source = auth.AvatarLocal - if err := UpdateAvatarSetting(ctx, form); err != nil { + if err := UpdateAvatarSetting(ctx, *form); err != nil { ctx.Flash.Error(err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success")) diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index c2e7bc8fa..017054d4c 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -10,12 +10,13 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" ) @@ -168,7 +169,8 @@ func SettingsProtectedBranch(c *context.Context) { } // SettingsProtectedBranchPost updates the protected branch settings -func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) { +func SettingsProtectedBranchPost(ctx *context.Context) { + f := web.GetForm(ctx).(*auth.ProtectBranchForm) branch := ctx.Params("*") if !ctx.Repo.GitRepo.IsBranchExist(branch) { ctx.NotFound("IsBranchExist", nil) diff --git a/routers/repo/settings_test.go b/routers/repo/settings_test.go index 679bb0d33..85515121c 100644 --- a/routers/repo/settings_test.go +++ b/routers/repo/settings_test.go @@ -10,11 +10,12 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -52,7 +53,8 @@ func TestAddReadOnlyDeployKey(t *testing.T) { Title: "read-only", Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", } - DeployKeysPost(ctx, addKeyForm) + web.SetForm(ctx, &addKeyForm) + DeployKeysPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ @@ -81,7 +83,8 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) { Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", IsWritable: true, } - DeployKeysPost(ctx, addKeyForm) + web.SetForm(ctx, &addKeyForm) + DeployKeysPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 5d7074b33..01d142843 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -13,14 +13,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/webhook" ) @@ -181,7 +182,8 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { } // GiteaHooksNewPost response for creating Gitea webhook -func GiteaHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { +func GiteaHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWebhookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -230,8 +232,9 @@ func GiteaHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { } // GogsHooksNewPost response for creating webhook -func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) { - newGogsWebhookPost(ctx, form, models.GOGS) +func GogsHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewGogshookForm) + newGogsWebhookPost(ctx, *form, models.GOGS) } // newGogsWebhookPost response for creating gogs hook @@ -283,7 +286,8 @@ func newGogsWebhookPost(ctx *context.Context, form auth.NewGogshookForm, kind mo } // DiscordHooksNewPost response for creating discord hook -func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) { +func DiscordHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDiscordHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -334,7 +338,8 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) { } // DingtalkHooksNewPost response for creating dingtalk hook -func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) { +func DingtalkHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDingtalkHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -376,7 +381,8 @@ func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) { } // TelegramHooksNewPost response for creating telegram hook -func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { +func TelegramHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewTelegramHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -427,7 +433,8 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { } // MatrixHooksNewPost response for creating a Matrix hook -func MatrixHooksNewPost(ctx *context.Context, form auth.NewMatrixHookForm) { +func MatrixHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMatrixHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -481,7 +488,8 @@ func MatrixHooksNewPost(ctx *context.Context, form auth.NewMatrixHookForm) { } // MSTeamsHooksNewPost response for creating MS Teams hook -func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { +func MSTeamsHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -523,7 +531,8 @@ func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { } // SlackHooksNewPost response for creating slack hook -func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { +func SlackHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewSlackHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -582,7 +591,8 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { } // FeishuHooksNewPost response for creating feishu hook -func FeishuHooksNewPost(ctx *context.Context, form auth.NewFeishuHookForm) { +func FeishuHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewFeishuHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -685,7 +695,8 @@ func WebHooksEdit(ctx *context.Context) { } // WebHooksEditPost response for editing web hook -func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) { +func WebHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWebhookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -725,7 +736,8 @@ func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) { } // GogsHooksEditPost response for editing gogs hook -func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) { +func GogsHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewGogshookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -764,7 +776,8 @@ func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) { } // SlackHooksEditPost response for editing slack hook -func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) { +func SlackHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewSlackHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -814,7 +827,8 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) { } // DiscordHooksEditPost response for editing discord hook -func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) { +func DiscordHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDiscordHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -856,7 +870,8 @@ func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) { } // DingtalkHooksEditPost response for editing discord hook -func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) { +func DingtalkHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDingtalkHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -888,7 +903,8 @@ func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) } // TelegramHooksEditPost response for editing discord hook -func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) { +func TelegramHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewTelegramHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -928,7 +944,8 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) } // MatrixHooksEditPost response for editing a Matrix hook -func MatrixHooksEditPost(ctx *context.Context, form auth.NewMatrixHookForm) { +func MatrixHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMatrixHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -971,7 +988,8 @@ func MatrixHooksEditPost(ctx *context.Context, form auth.NewMatrixHookForm) { } // MSTeamsHooksEditPost response for editing MS Teams hook -func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { +func MSTeamsHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -1003,7 +1021,8 @@ func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { } // FeishuHooksEditPost response for editing feishu hook -func FeishuHooksEditPost(ctx *context.Context, form auth.NewFeishuHookForm) { +func FeishuHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewFeishuHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index ac650d3fc..c4521a307 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -13,15 +13,16 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -556,7 +557,8 @@ func NewWiki(ctx *context.Context) { } // NewWikiPost response for wiki create request -func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) { +func NewWikiPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") ctx.Data["PageIsWiki"] = true ctx.Data["RequireSimpleMDE"] = true @@ -613,7 +615,8 @@ func EditWiki(ctx *context.Context) { } // EditWikiPost response for wiki modify request -func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) { +func EditWikiPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") ctx.Data["PageIsWiki"] = true ctx.Data["RequireSimpleMDE"] = true diff --git a/routers/repo/wiki_test.go b/routers/repo/wiki_test.go index cc79c808f..badd07f08 100644 --- a/routers/repo/wiki_test.go +++ b/routers/repo/wiki_test.go @@ -10,9 +10,10 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" wiki_service "code.gitea.io/gitea/services/wiki" "github.com/stretchr/testify/assert" @@ -114,11 +115,12 @@ func TestNewWikiPost(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/wiki/_new") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: title, Content: content, Message: message, }) + NewWikiPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, title) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content) @@ -131,11 +133,12 @@ func TestNewWikiPost_ReservedName(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/wiki/_new") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: "_edit", Content: content, Message: message, }) + NewWikiPost(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg) assertWikiNotExists(t, ctx.Repo.Repository, "_edit") @@ -164,11 +167,12 @@ func TestEditWikiPost(t *testing.T) { ctx.SetParams(":page", "Home") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - EditWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: title, Content: content, Message: message, }) + EditWikiPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, title) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content) diff --git a/routers/routes/chi.go b/routers/routes/base.go similarity index 53% rename from routers/routes/chi.go rename to routers/routes/base.go index 2400140ae..a313032a8 100644 --- a/routers/routes/chi.go +++ b/routers/routes/base.go @@ -16,19 +16,16 @@ import ( "text/template" "time" + "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/metrics" - "code.gitea.io/gitea/modules/public" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/modules/templates" "gitea.com/go-chi/session" - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/prometheus/client_golang/prometheus" ) type routerLoggerOptions struct { @@ -39,20 +36,21 @@ type routerLoggerOptions struct { } // SignedUserName returns signed user's name via context -// FIXME currently no any data stored on chi.Context but macaron.Context, so this will -// return "" before we remove macaron totally func SignedUserName(req *http.Request) string { - if v, ok := req.Context().Value("SignedUserName").(string); ok { - return v + ctx := context.GetContext(req) + if ctx != nil { + v := ctx.Data["SignedUserName"] + if res, ok := v.(string); ok { + return res + } } return "" } -func setupAccessLogger(c chi.Router) { +func accessLogger() func(http.Handler) http.Handler { logger := log.GetLogger("access") - logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) - c.Use(func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { start := time.Now() next.ServeHTTP(w, req) @@ -70,15 +68,15 @@ func setupAccessLogger(c chi.Router) { ResponseWriter: rw, }) if err != nil { - log.Error("Could not set up macaron access logger: %v", err.Error()) + log.Error("Could not set up chi access logger: %v", err.Error()) } err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "") if err != nil { - log.Error("Could not set up macaron access logger: %v", err.Error()) + log.Error("Could not set up chi access logger: %v", err.Error()) } }) - }) + } } // LoggerHandler is a handler that will log the routing to the default gitea log @@ -179,131 +177,70 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor } } -var ( - sessionManager *session.Manager -) - -// NewChi creates a chi Router -func NewChi() chi.Router { - c := chi.NewRouter() - c.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - next.ServeHTTP(context.NewResponse(resp), req) - }) - }) - c.Use(middleware.RealIP) - if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { - if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { - c.Use(LoggerHandler(setting.RouterLogLevel)) - } - } - - var opt = session.Options{ - Provider: setting.SessionConfig.Provider, - ProviderConfig: setting.SessionConfig.ProviderConfig, - CookieName: setting.SessionConfig.CookieName, - CookiePath: setting.SessionConfig.CookiePath, - Gclifetime: setting.SessionConfig.Gclifetime, - Maxlifetime: setting.SessionConfig.Maxlifetime, - Secure: setting.SessionConfig.Secure, - Domain: setting.SessionConfig.Domain, - } - opt = session.PrepareOptions([]session.Options{opt}) - - var err error - sessionManager, err = session.NewManager(opt.Provider, opt) - if err != nil { - panic(err) - } - - c.Use(Recovery()) - if setting.EnableAccessLog { - setupAccessLogger(c) - } - - c.Use(public.Custom( - &public.Options{ - SkipLogging: setting.DisableRouterLog, - }, - )) - c.Use(public.Static( - &public.Options{ - Directory: path.Join(setting.StaticRootPath, "public"), - SkipLogging: setting.DisableRouterLog, - }, - )) - - c.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) - c.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) - - return c +type dataStore struct { + Data map[string]interface{} } -// RegisterInstallRoute registers the install routes -func RegisterInstallRoute(c chi.Router) { - m := NewMacaron() - RegisterMacaronInstallRoute(m) - - // We need at least one handler in chi so that it does not drop - // our middleware: https://github.com/go-gitea/gitea/issues/13725#issuecomment-735244395 - c.Get("/", func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.NotFound(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) +func (d *dataStore) GetData() map[string]interface{} { + return d.Data } -// NormalRoutes represents non install routes -func NormalRoutes() http.Handler { - r := chi.NewRouter() +// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. +// This error will be created with the gitea 500 page. +func Recovery() func(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + + sessionStore := session.GetSession(req) + if sessionStore == nil { + if setting.IsProd() { + http.Error(w, http.StatusText(500), 500) + } else { + http.Error(w, combinedErr, 500) + } + return + } - // for health check - r.Head("/", func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - }) + var lc = middlewares.Locale(w, req) + var store = dataStore{ + Data: templates.Vars{ + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + }, + } - if setting.HasRobotsTxt { - r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) { - filePath := path.Join(setting.CustomPath, "robots.txt") - fi, err := os.Stat(filePath) - if err == nil && httpcache.HandleTimeCache(req, w, fi) { - return - } - http.ServeFile(w, req, filePath) - }) - } + // Get user from session if logged in. + user, _ := sso.SignedInUser(req, w, &store, sessionStore) + if user != nil { + store.Data["IsSigned"] = true + store.Data["SignedUser"] = user + store.Data["SignedUserID"] = user.ID + store.Data["SignedUserName"] = user.Name + store.Data["IsAdmin"] = user.IsAdmin + } else { + store.Data["SignedUserID"] = int64(0) + store.Data["SignedUserName"] = "" + } - r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) { - http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301) - }) + w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) - // prometheus metrics endpoint - if setting.Metrics.Enabled { - c := metrics.NewCollector() - prometheus.MustRegister(c) + if !setting.IsProd() { + store.Data["ErrorMsg"] = combinedErr + } + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + if err != nil { + log.Error("%v", err) + } + } + }() - r.Get("/metrics", routers.Metrics) + next.ServeHTTP(w, req) + }) } - - return r -} - -// DelegateToMacaron delegates other routes to macaron -func DelegateToMacaron(r chi.Router) { - m := NewMacaron() - RegisterMacaronRoutes(m) - - r.NotFound(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - r.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) } diff --git a/routers/routes/recovery.go b/routers/routes/install.go similarity index 50% rename from routers/routes/recovery.go rename to routers/routes/install.go index cfe1a4114..0dc066d60 100644 --- a/routers/routes/recovery.go +++ b/routers/routes/install.go @@ -7,38 +7,23 @@ package routes import ( "fmt" "net/http" + "path" - "code.gitea.io/gitea/modules/auth/sso" + "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/middlewares" + "code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers" - "github.com/unrolled/render" + "gitea.com/go-chi/session" ) -type dataStore struct { - Data map[string]interface{} -} - -func (d *dataStore) GetData() map[string]interface{} { - return d.Data -} - -// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. -// Although similar to macaron.Recovery() the main difference is that this error will be created -// with the gitea 500 page. -func Recovery() func(next http.Handler) http.Handler { +func installRecovery() func(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() return func(next http.Handler) http.Handler { - rnd := render.New(render.Options{ - Extensions: []string{".tmpl"}, - Directory: "templates", - Funcs: templates.NewFuncMap(), - Asset: templates.GetAsset, - AssetNames: templates.GetAssetNames, - IsDevelopment: !setting.IsProd(), - }) - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { defer func() { // Why we need this? The first recover will try to render a beautiful @@ -62,35 +47,16 @@ func Recovery() func(next http.Handler) http.Handler { log.Error("%v", combinedErr) lc := middlewares.Locale(w, req) - - // TODO: this should be replaced by real session after macaron removed totally - sessionStore, err := sessionManager.Start(w, req) - if err != nil { - // Just invoke the above recover catch - panic("session(start): " + err.Error()) - } - var store = dataStore{ Data: templates.Vars{ - "Language": lc.Language(), - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "i18n": lc, + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + "SignedUserID": int64(0), + "SignedUserName": "", }, } - // Get user from session if logged in. - user, _ := sso.SignedInUser(req, w, &store, sessionStore) - if user != nil { - store.Data["IsSigned"] = true - store.Data["SignedUser"] = user - store.Data["SignedUserID"] = user.ID - store.Data["SignedUserName"] = user.Name - store.Data["IsAdmin"] = user.IsAdmin - } else { - store.Data["SignedUserID"] = int64(0) - store.Data["SignedUserName"] = "" - } - w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) if !setting.IsProd() { @@ -107,3 +73,44 @@ func Recovery() func(next http.Handler) http.Handler { }) } } + +// InstallRoutes registers the install routes +func InstallRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range commonMiddlewares() { + r.Use(middle) + } + + r.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + + r.Use(installRecovery()) + + r.Use(public.Custom( + &public.Options{ + SkipLogging: setting.DisableRouterLog, + }, + )) + r.Use(public.Static( + &public.Options{ + Directory: path.Join(setting.StaticRootPath, "public"), + SkipLogging: setting.DisableRouterLog, + }, + )) + + r.Use(routers.InstallInit) + r.Get("/", routers.Install) + r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) + r.NotFound(func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, setting.AppURL, 302) + }) + return r +} diff --git a/routers/routes/macaron.go b/routers/routes/web.go similarity index 70% rename from routers/routes/macaron.go rename to routers/routes/web.go index f64a0a597..243361858 100644 --- a/routers/routes/macaron.go +++ b/routers/routes/web.go @@ -6,19 +6,29 @@ package routes import ( "encoding/gob" + "fmt" + "net/http" + "os" + "path" + "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/options" + "code.gitea.io/gitea/modules/metrics" + "code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers/admin" apiv1 "code.gitea.io/gitea/routers/api/v1" + "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/dev" "code.gitea.io/gitea/routers/events" "code.gitea.io/gitea/routers/org" @@ -31,77 +41,79 @@ import ( // to registers all internal adapters _ "code.gitea.io/gitea/modules/session" - "gitea.com/macaron/binding" - "gitea.com/macaron/cache" - "gitea.com/macaron/captcha" - "gitea.com/macaron/cors" - "gitea.com/macaron/csrf" - "gitea.com/macaron/gzip" - "gitea.com/macaron/i18n" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" - "gitea.com/macaron/toolbox" + "gitea.com/go-chi/captcha" + "gitea.com/go-chi/session" + "github.com/NYTimes/gziphandler" + "github.com/go-chi/chi/middleware" + "github.com/prometheus/client_golang/prometheus" "github.com/tstranex/u2f" ) -// NewMacaron initializes Macaron instance. -func NewMacaron() *macaron.Macaron { - gob.Register(&u2f.Challenge{}) - var m *macaron.Macaron - if setting.RedirectMacaronLog { - loggerAsWriter := log.NewLoggerAsWriter("INFO", log.GetLogger("macaron")) - m = macaron.NewWithLogger(loggerAsWriter) - } else { - m = macaron.New() - } +const ( + // GzipMinSize represents min size to compress for the body size of response + GzipMinSize = 1400 +) - if setting.EnableGzip { - m.Use(gzip.Middleware()) +func commonMiddlewares() []func(http.Handler) http.Handler { + var handlers = []func(http.Handler) http.Handler{ + func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + next.ServeHTTP(context.NewResponse(resp), req) + }) + }, + middleware.RealIP, } - if setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix { - m.SetURLPrefix(setting.AppSubURL) + if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { + if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { + handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) + } } + handlers = append(handlers, func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + // Why we need this? The Recovery() will try to render a beautiful + // error page for user, but the process can still panic again, and other + // middleware like session also may panic then we have to recover twice + // and send a simple error page that should not panic any more. + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + if setting.IsProd() { + http.Error(resp, http.StatusText(500), 500) + } else { + http.Error(resp, combinedErr, 500) + } + } + }() + next.ServeHTTP(resp, req) + }) + }) - m.Use(templates.HTMLRenderer()) - - mailer.InitMailRender(templates.Mailer()) - - localeNames, err := options.Dir("locale") - - if err != nil { - log.Fatal("Failed to list locale files: %v", err) + if setting.EnableAccessLog { + handlers = append(handlers, accessLogger()) } + return handlers +} - localFiles := make(map[string][]byte) +// NormalRoutes represents non install routes +func NormalRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range commonMiddlewares() { + r.Use(middle) + } + r.Use(Recovery()) - for _, name := range localeNames { - localFiles[name], err = options.Locale(name) + r.Mount("/", WebRoutes()) + r.Mount("/api/v1", apiv1.Routes()) + r.Mount("/api/internal", private.Routes()) + return r +} - if err != nil { - log.Fatal("Failed to load %s locale file. %v", name, err) - } - } +// WebRoutes returns all web routes +func WebRoutes() *web.Route { + r := web.NewRoute() - m.Use(i18n.I18n(i18n.Options{ - SubURL: setting.AppSubURL, - Files: localFiles, - Langs: setting.Langs, - Names: setting.Names, - DefaultLang: "en-US", - Redirect: false, - CookieHttpOnly: true, - Secure: setting.SessionConfig.Secure, - CookieDomain: setting.SessionConfig.Domain, - })) - m.Use(cache.Cacher(cache.Options{ - Adapter: setting.CacheService.Adapter, - AdapterConfig: setting.CacheService.Conn, - Interval: setting.CacheService.Interval, - })) - m.Use(captcha.Captchaer(captcha.Options{ - SubURL: setting.AppSubURL, - })) - m.Use(session.Sessioner(session.Options{ + r.Use(session.Sessioner(session.Options{ Provider: setting.SessionConfig.Provider, ProviderConfig: setting.SessionConfig.ProviderConfig, CookieName: setting.SessionConfig.CookieName, @@ -111,47 +123,104 @@ func NewMacaron() *macaron.Macaron { Secure: setting.SessionConfig.Secure, Domain: setting.SessionConfig.Domain, })) - m.Use(csrf.Csrfer(csrf.Options{ - Secret: setting.SecretKey, - Cookie: setting.CSRFCookieName, - SetCookie: true, - Secure: setting.SessionConfig.Secure, - CookieHttpOnly: setting.CSRFCookieHTTPOnly, - Header: "X-Csrf-Token", - CookieDomain: setting.SessionConfig.Domain, - CookiePath: setting.AppSubURL, - })) - m.Use(toolbox.Toolboxer(m, toolbox.Options{ - HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{ - { - Desc: "Database connection", - Func: models.Ping, - }, + + r.Use(public.Custom( + &public.Options{ + SkipLogging: setting.DisableRouterLog, }, - DisableDebug: !setting.EnablePprof, - })) - m.Use(context.Contexter()) - m.SetAutoHead(true) - return m -} + )) + r.Use(public.Static( + &public.Options{ + Directory: path.Join(setting.StaticRootPath, "public"), + SkipLogging: setting.DisableRouterLog, + }, + )) + + r.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) + r.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) -// RegisterMacaronInstallRoute registers the install routes -func RegisterMacaronInstallRoute(m *macaron.Macaron) { - m.Combo("/", routers.InstallInit).Get(routers.Install). - Post(binding.BindIgnErr(auth.InstallForm{}), routers.InstallPost) - m.NotFound(func(ctx *context.Context) { - ctx.Redirect(setting.AppURL, 302) + gob.Register(&u2f.Challenge{}) + + if setting.EnableGzip { + h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize)) + if err != nil { + log.Fatal("GzipHandlerWithOpts failed: %v", err) + } + r.Use(h) + } + + if (setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix) && setting.AppSubURL != "" { + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL) + next.ServeHTTP(resp, req) + }) + }) + } + + mailer.InitMailRender(templates.Mailer()) + + r.Use(captcha.Captchaer(context.GetImageCaptcha())) + // Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary + r.Use(context.Contexter()) + // Removed: SetAutoHead allow a get request redirect to head if get method is not exist + + r.Use(user.GetNotificationCount) + r.Use(repo.GetActiveStopwatch) + r.Use(func(ctx *context.Context) { + ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled() + ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled() + ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled() + ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled() + }) + + // for health check + r.Head("/", func(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + if setting.HasRobotsTxt { + r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) { + filePath := path.Join(setting.CustomPath, "robots.txt") + fi, err := os.Stat(filePath) + if err == nil && httpcache.HandleTimeCache(req, w, fi) { + return + } + http.ServeFile(w, req, filePath) + }) + } + + r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301) }) + + // prometheus metrics endpoint + if setting.Metrics.Enabled { + c := metrics.NewCollector() + prometheus.MustRegister(c) + + r.Get("/metrics", routers.Metrics) + } + + if setting.API.EnableSwagger { + // Note: The route moved from apiroutes because it's in fact want to render a web page + r.Get("/api/swagger", misc.Swagger) // Render V1 by default + } + + RegisterRoutes(r) + + return r } -// RegisterMacaronRoutes routes routes to Macaron -func RegisterMacaronRoutes(m *macaron.Macaron) { +// RegisterRoutes routes routes to Macaron +func RegisterRoutes(m *web.Route) { reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) - bindIgnErr := binding.BindIgnErr + //bindIgnErr := binding.BindIgnErr + bindIgnErr := web.Bind validation.AddBindingRules() openIDSignInEnabled := func(ctx *context.Context) { @@ -175,15 +244,6 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { } } - m.Use(user.GetNotificationCount) - m.Use(repo.GetActiveStopwatch) - m.Use(func(ctx *context.Context) { - ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled() - ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled() - ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled() - ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled() - }) - // FIXME: not all routes need go through same middlewares. // Especially some AJAX requests, we can reduce middleware number to improve performance. // Routers. @@ -198,8 +258,6 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/organizations", routers.ExploreOrganizations) m.Get("/code", routers.ExploreCode) }, ignSignIn) - m.Combo("/install", routers.InstallInit).Get(routers.Install). - Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) m.Get("/issues", reqSignIn, user.Issues) m.Get("/pulls", reqSignIn, user.Pulls) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) @@ -226,8 +284,8 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/sign_up", user.SignUp) m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) m.Group("/oauth2", func() { - m.Get("/:provider", user.SignInOAuth) - m.Get("/:provider/callback", user.SignInOAuthCallback) + m.Get("/{provider}", user.SignInOAuth) + m.Get("/{provider}/callback", user.SignInOAuthCallback) }) m.Get("/link_account", user.LinkAccount) m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn) @@ -261,7 +319,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) m.Get("/change_password", user.MustChangePassword) m.Post("/change_password", bindIgnErr(auth.MustChangePasswordForm{}), user.MustChangePasswordPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), userSetting.AvatarPost) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), userSetting.AvatarPost) m.Post("/avatar/delete", userSetting.DeleteAvatar) m.Group("/account", func() { m.Combo("").Get(userSetting.Account).Post(bindIgnErr(auth.ChangePasswordForm{}), userSetting.AccountPost) @@ -291,9 +349,9 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/account_link", userSetting.DeleteAccountLink) }) m.Group("/applications/oauth2", func() { - m.Get("/:id", userSetting.OAuth2ApplicationShow) - m.Post("/:id", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsEdit) - m.Post("/:id/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret) + m.Get("/{id}", userSetting.OAuth2ApplicationShow) + m.Post("/{id}", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsEdit) + m.Post("/{id}/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret) m.Post("", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost) m.Post("/delete", userSetting.DeleteOAuth2Application) m.Post("/revoke", userSetting.RevokeOAuth2Grant) @@ -316,18 +374,18 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) m.Any("/activate", user.Activate, reqSignIn) m.Any("/activate_email", user.ActivateEmail) - m.Get("/avatar/:username/:size", user.Avatar) + m.Get("/avatar/{username}/{size}", user.Avatar) m.Get("/email2user", user.Email2User) m.Get("/recover_account", user.ResetPasswd) m.Post("/recover_account", user.ResetPasswdPost) m.Get("/forgot_password", user.ForgotPasswd) m.Post("/forgot_password", user.ForgotPasswdPost) m.Post("/logout", user.SignOut) - m.Get("/task/:task", user.TaskStatus) + m.Get("/task/{task}", user.TaskStatus) }) // ***** END: User ***** - m.Get("/avatar/:hash", user.AvatarByEmailHash) + m.Get("/avatar/{hash}", user.AvatarByEmailHash) adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) @@ -339,12 +397,12 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/config/test_mail", admin.SendTestMail) m.Group("/monitor", func() { m.Get("", admin.Monitor) - m.Post("/cancel/:pid", admin.MonitorCancel) - m.Group("/queue/:qid", func() { + m.Post("/cancel/{pid}", admin.MonitorCancel) + m.Group("/queue/{qid}", func() { m.Get("", admin.Queue) m.Post("/set", admin.SetQueueSettings) m.Post("/add", admin.AddWorkers) - m.Post("/cancel/:pid", admin.WorkerCancel) + m.Post("/cancel/{pid}", admin.WorkerCancel) m.Post("/flush", admin.Flush) }) }) @@ -352,8 +410,8 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/users", func() { m.Get("", admin.Users) m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost) - m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost) - m.Post("/:userid/delete", admin.DeleteUser) + m.Combo("/{userid}").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost) + m.Post("/{userid}/delete", admin.DeleteUser) }) m.Group("/emails", func() { @@ -374,20 +432,20 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/hooks", func() { m.Get("", admin.DefaultOrSystemWebhooks) m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) }) - m.Group("/^:configType(default-hooks|system-hooks)$", func() { - m.Get("/:type/new", repo.WebhooksNew) + m.Group("/{configType:default-hooks|system-hooks}", func() { + m.Get("/{type}/new", repo.WebhooksNew) m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) @@ -402,9 +460,9 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/auths", func() { m.Get("", admin.Authentications) m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) - m.Combo("/:authid").Get(admin.EditAuthSource). + m.Combo("/{authid}").Get(admin.EditAuthSource). Post(bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost) - m.Post("/:authid/delete", admin.DeleteAuthSource) + m.Post("/{authid}/delete", admin.DeleteAuthSource) }) m.Group("/notices", func() { @@ -416,15 +474,15 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { // ***** END: Admin ***** m.Group("", func() { - m.Get("/:username", user.Profile) - m.Get("/attachments/:uuid", repo.GetAttachment) + m.Get("/{username}", user.Profile) + m.Get("/attachments/{uuid}", repo.GetAttachment) }, ignSignIn) - m.Group("/:username", func() { - m.Post("/action/:action", user.Action) + m.Group("/{username}", func() { + m.Post("/action/{action}", user.Action) }, reqSignIn) - if macaron.Env == macaron.DEV { + if !setting.IsProd() { m.Get("/template/*", dev.TemplatePreview) } @@ -449,44 +507,44 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost) }) - m.Group("/:org", func() { + m.Group("/{org}", func() { m.Get("/dashboard", user.Dashboard) - m.Get("/dashboard/:team", user.Dashboard) + m.Get("/dashboard/{team}", user.Dashboard) m.Get("/issues", user.Issues) - m.Get("/issues/:team", user.Issues) + m.Get("/issues/{team}", user.Issues) m.Get("/pulls", user.Pulls) - m.Get("/pulls/:team", user.Pulls) + m.Get("/pulls/{team}", user.Pulls) m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) - m.Get("/milestones/:team", reqMilestonesDashboardPageEnabled, user.Milestones) + m.Get("/milestones/{team}", reqMilestonesDashboardPageEnabled, user.Milestones) m.Get("/members", org.Members) - m.Post("/members/action/:action", org.MembersAction) + m.Post("/members/action/{action}", org.MembersAction) m.Get("/teams", org.Teams) }, context.OrgAssignment(true, false, true)) - m.Group("/:org", func() { - m.Get("/teams/:team", org.TeamMembers) - m.Get("/teams/:team/repositories", org.TeamRepositories) - m.Post("/teams/:team/action/:action", org.TeamsAction) - m.Post("/teams/:team/action/repo/:action", org.TeamsRepoAction) + m.Group("/{org}", func() { + m.Get("/teams/{team}", org.TeamMembers) + m.Get("/teams/{team}/repositories", org.TeamRepositories) + m.Post("/teams/{team}/action/{action}", org.TeamsAction) + m.Post("/teams/{team}/action/repo/{action}", org.TeamsRepoAction) }, context.OrgAssignment(true, false, true)) - m.Group("/:org", func() { + m.Group("/{org}", func() { m.Get("/teams/new", org.NewTeam) m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) - m.Get("/teams/:team/edit", org.EditTeam) - m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost) - m.Post("/teams/:team/delete", org.DeleteTeam) + m.Get("/teams/{team}/edit", org.EditTeam) + m.Post("/teams/{team}/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost) + m.Post("/teams/{team}/delete", org.DeleteTeam) m.Group("/settings", func() { m.Combo("").Get(org.Settings). Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), org.SettingsAvatar) m.Post("/avatar/delete", org.SettingsDeleteAvatar) m.Group("/hooks", func() { m.Get("", org.Webhooks) m.Post("/delete", org.DeleteWebhook) - m.Get("/:type/new", repo.WebhooksNew) + m.Get("/{type}/new", repo.WebhooksNew) m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) @@ -496,16 +554,16 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) }) m.Group("/labels", func() { @@ -529,19 +587,19 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/migrate", repo.Migrate) m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) m.Group("/fork", func() { - m.Combo("/:repoid").Get(repo.Fork). + m.Combo("/{repoid}").Get(repo.Fork). Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) }, reqSignIn) // ***** Release Attachment Download without Signin - m.Get("/:username/:reponame/releases/download/:vTag/:fileName", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload) + m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Group("/settings", func() { m.Combo("").Get(repo.Settings). Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), repo.SettingsAvatar) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), repo.SettingsAvatar) m.Post("/avatar/delete", repo.SettingsDeleteAvatar) m.Group("/collaboration", func() { @@ -562,7 +620,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/hooks", func() { m.Get("", repo.Webhooks) m.Post("/delete", repo.DeleteWebhook) - m.Get("/:type/new", repo.WebhooksNew) + m.Get("/{type}/new", repo.WebhooksNew) m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) @@ -572,21 +630,21 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/:id/test", repo.TestWebhook) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/{id}/test", repo.TestWebhook) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) m.Group("/git", func() { m.Get("", repo.GitHooks) - m.Combo("/:name").Get(repo.GitHooksEdit). + m.Combo("/{name}").Get(repo.GitHooksEdit). Post(repo.GitHooksEditPost) }, context.GitHookService()) }) @@ -598,16 +656,16 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }) m.Group("/lfs", func() { - m.Get("", repo.LFSFiles) - m.Get("/show/:oid", repo.LFSFileGet) - m.Post("/delete/:oid", repo.LFSDelete) + m.Get("/", repo.LFSFiles) + m.Get("/show/{oid}", repo.LFSFileGet) + m.Post("/delete/{oid}", repo.LFSDelete) m.Get("/pointers", repo.LFSPointerFiles) m.Post("/pointers/associate", repo.LFSAutoAssociate) m.Get("/find", repo.LFSFileFind) m.Group("/locks", func() { m.Get("/", repo.LFSLocks) m.Post("/", repo.LFSLockFile) - m.Post("/:lid/unlock", repo.LFSUnlock) + m.Post("/{lid}/unlock", repo.LFSUnlock) }) }) @@ -617,12 +675,12 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }) }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) - m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) + m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) // Grouping for those endpoints not requiring authentication - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Group("/milestone", func() { - m.Get("/:id", repo.MilestoneIssuesAndPulls) + m.Get("/{id}", repo.MilestoneIssuesAndPulls) }, reqRepoIssuesOrPullsReader, context.RepoRef()) m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). Get(ignSignIn, repo.SetDiffViewStyle, repo.CompareDiff). @@ -630,7 +688,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, context.RepoAssignment(), context.UnitTypes()) // Grouping for those endpoints that do require authentication - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Group("/issues", func() { m.Group("/new", func() { m.Combo("").Get(context.RepoRef(), repo.NewIssue). @@ -641,7 +699,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { // FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest. // So they can apply their own enable/disable logic on routers. m.Group("/issues", func() { - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Post("/title", repo.UpdateIssueTitle) m.Post("/content", repo.UpdateIssueContent) m.Post("/watch", repo.IssueWatch) @@ -658,13 +716,13 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/cancel", repo.CancelStopwatch) }) }) - m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction) + m.Post("/reactions/{action}", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction) m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue) m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue) }, context.RepoMustNotBeArchived()) - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Get("/attachments", repo.GetIssueAttachments) - m.Get("/attachments/:uuid", repo.GetAttachment) + m.Get("/attachments/{uuid}", repo.GetAttachment) }) m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) @@ -677,12 +735,12 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Post("/attachments", repo.UploadIssueAttachment) m.Post("/attachments/remove", repo.DeleteAttachment) }, context.RepoMustNotBeArchived()) - m.Group("/comments/:id", func() { + m.Group("/comments/{id}", func() { m.Post("", repo.UpdateCommentContent) m.Post("/delete", repo.DeleteComment) - m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction) + m.Post("/reactions/{action}", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction) }, context.RepoMustNotBeArchived()) - m.Group("/comments/:id", func() { + m.Group("/comments/{id}", func() { m.Get("/attachments", repo.GetCommentAttachments) }) m.Group("/labels", func() { @@ -694,13 +752,13 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/milestones", func() { m.Combo("/new").Get(repo.NewMilestone). Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) - m.Get("/:id/edit", repo.EditMilestone) - m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) - m.Post("/:id/:action", repo.ChangeMilestoneStatus) + m.Get("/{id}/edit", repo.EditMilestone) + m.Post("/{id}/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) + m.Post("/{id}/{action}", repo.ChangeMilestoneStatus) m.Post("/delete", repo.DeleteMilestone) }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) m.Group("/pull", func() { - m.Post("/:index/target_branch", repo.UpdatePullRequestTarget) + m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget) }, context.RepoMustNotBeArchived()) m.Group("", func() { @@ -723,7 +781,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) m.Group("/branches", func() { - m.Group("/_new/", func() { + m.Group("/_new", func() { m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch) @@ -735,14 +793,14 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, reqSignIn, context.RepoAssignment(), context.UnitTypes()) // Releases - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Get("/tags", repo.TagsList, repo.MustBeNotEmpty, reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag)) m.Group("/releases", func() { m.Get("/", repo.Releases) m.Get("/tag/*", repo.SingleRelease) m.Get("/latest", repo.LatestRelease) - m.Get("/attachments/:uuid", repo.GetAttachment) + m.Get("/attachments/{uuid}", repo.GetAttachment) }, repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag)) m.Group("/releases", func() { m.Get("/new", repo.NewRelease) @@ -772,56 +830,57 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }) }, ignSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Post("/topics", repo.TopicsPost) }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Group("", func() { - m.Get("/^:type(issues|pulls)$", repo.Issues) - m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) - m.Get("/labels/", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) + m.Get("/{type:issues|pulls}", repo.Issues) + m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue) + m.Get("/labels", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones) }, context.RepoRef()) m.Group("/projects", func() { m.Get("", repo.Projects) - m.Get("/:id", repo.ViewProject) + m.Get("/{id}", repo.ViewProject) m.Group("", func() { m.Get("/new", repo.NewProject) m.Post("/new", bindIgnErr(auth.CreateProjectForm{}), repo.NewProjectPost) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Post("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.AddBoardToProjectPost) m.Post("/delete", repo.DeleteProject) m.Get("/edit", repo.EditProject) m.Post("/edit", bindIgnErr(auth.CreateProjectForm{}), repo.EditProjectPost) - m.Post("/^:action(open|close)$", repo.ChangeProjectStatus) + m.Post("/{action:open|close}", repo.ChangeProjectStatus) - m.Group("/:boardID", func() { + m.Group("/{boardID}", func() { m.Put("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.EditProjectBoardTitle) m.Delete("", repo.DeleteProjectBoard) m.Post("/default", repo.SetDefaultProjectBoard) - m.Post("/:index", repo.MoveIssueAcrossBoards) + m.Post("/{index}", repo.MoveIssueAcrossBoards) }) }) }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) }, reqRepoProjectsReader, repo.MustEnableProjects) m.Group("/wiki", func() { - m.Get("/?:page", repo.Wiki) + m.Get("/", repo.Wiki) + m.Get("/{page}", repo.Wiki) m.Get("/_pages", repo.WikiPages) - m.Get("/:page/_revision", repo.WikiRevision) - m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) - m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff) + m.Get("/{page}/_revision", repo.WikiRevision) + m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) + m.Get("/commit/{sha:[a-f0-9]{7,40}}.{:patch|diff}", repo.RawDiff) m.Group("", func() { m.Combo("/_new").Get(repo.NewWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) - m.Combo("/:page/_edit").Get(repo.EditWiki). + m.Combo("/{page}/_edit").Get(repo.EditWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) - m.Post("/:page/delete", repo.DeleteWikiPagePost) + m.Post("/{page}/delete", repo.DeleteWikiPagePost) }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter) }, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) { ctx.Data["PageIsWiki"] = true @@ -833,12 +892,12 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("/activity", func() { m.Get("", repo.Activity) - m.Get("/:period", repo.Activity) + m.Get("/{period}", repo.Activity) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases)) m.Group("/activity_author_data", func() { m.Get("", repo.ActivityAuthors) - m.Get("/:period", repo.ActivityAuthors) + m.Get("/{period}", repo.ActivityAuthors) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypeCode)) m.Group("/archive", func() { @@ -851,10 +910,10 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) m.Group("/blob_excerpt", func() { - m.Get("/:sha", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) + m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) - m.Group("/pulls/:index", func() { + m.Group("/pulls/{index}", func() { m.Get(".diff", repo.DownloadPullDiff) m.Get(".patch", repo.DownloadPullPatch) m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) @@ -875,7 +934,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownloadOrLFS) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownloadOrLFS) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS) - m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS) + m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS) }, repo.MustBeNotEmpty, reqRepoCodeReader) @@ -884,7 +943,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload) - m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) + m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) }, repo.MustBeNotEmpty, reqRepoCodeReader) @@ -905,7 +964,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("", func() { m.Get("/graph", repo.Graph) - m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) + m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) m.Group("/src", func() { @@ -919,39 +978,52 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Group("", func() { m.Get("/forks", repo.Forks) }, context.RepoRef(), reqRepoCodeReader) - m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", + m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) }, ignSignIn, context.RepoAssignment(), context.UnitTypes()) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Get("/stars", repo.Stars) m.Get("/watchers", repo.Watchers) m.Get("/search", reqRepoCodeReader, repo.Search) }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) - m.Group("/:username", func() { - m.Group("/:reponame", func() { + m.Group("/{username}", func() { + m.Group("/{reponame}", func() { m.Get("", repo.SetEditorconfigIfExists, repo.Home) - m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) - m.Group("/:reponame", func() { - m.Group("\\.git/info/lfs", func() { + m.Group("/{reponame}", func() { + m.Group("/info/lfs", func() { m.Post("/objects/batch", lfs.BatchHandler) - m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler) - m.Any("/objects/:oid", lfs.ObjectOidHandler) + m.Get("/objects/{oid}/{filename}", lfs.ObjectOidHandler) + m.Any("/objects/{oid}", lfs.ObjectOidHandler) m.Post("/objects", lfs.PostHandler) m.Post("/verify", lfs.VerifyHandler) m.Group("/locks", func() { m.Get("/", lfs.GetListLockHandler) m.Post("/", lfs.PostLockHandler) m.Post("/verify", lfs.VerifyLockHandler) - m.Post("/:lid/unlock", lfs.UnLockHandler) + m.Post("/{lid}/unlock", lfs.UnLockHandler) }) m.Any("/*", func(ctx *context.Context) { ctx.NotFound("", nil) }) }, ignSignInAndCsrf) - m.Any("/*", ignSignInAndCsrf, repo.HTTP) + + m.Group("", func() { + m.Post("/git-upload-pack", repo.ServiceUploadPack) + m.Post("/git-receive-pack", repo.ServiceReceivePack) + m.Get("/info/refs", repo.GetInfoRefs) + m.Get("/HEAD", repo.GetTextFile("HEAD")) + m.Get("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) + m.Get("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) + m.Get("/objects/info/packs", repo.GetInfoPacks) + m.Get("/objects/info/{file:[^/]*}", repo.GetTextFile("")) + m.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) + m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) + m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + }, ignSignInAndCsrf) + m.Head("/tasks/trigger", repo.TriggerTask) }) }) @@ -964,30 +1036,9 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { }, reqSignIn) if setting.API.EnableSwagger { - m.Get("/swagger.v1.json", templates.JSONRenderer(), routers.SwaggerV1Json) - } - - var handlers []macaron.Handler - if setting.CORSConfig.Enabled { - handlers = append(handlers, cors.CORS(cors.Options{ - Scheme: setting.CORSConfig.Scheme, - AllowDomain: setting.CORSConfig.AllowDomain, - AllowSubdomain: setting.CORSConfig.AllowSubdomain, - Methods: setting.CORSConfig.Methods, - MaxAgeSeconds: int(setting.CORSConfig.MaxAge.Seconds()), - AllowCredentials: setting.CORSConfig.AllowCredentials, - })) + m.Get("/swagger.v1.json", routers.SwaggerV1Json) } - handlers = append(handlers, ignSignIn) - m.Group("/api", func() { - apiv1.RegisterRoutes(m) - }, handlers...) - - m.Group("/api/internal", func() { - // package name internal is ideal but Golang is not allowed, so we use private as package name. - private.RegisterRoutes(m) - }) // Not found handler. - m.NotFound(routers.NotFound) + m.NotFound(web.Wrap(routers.NotFound)) } diff --git a/routers/swagger_json.go b/routers/swagger_json.go index 58c4ec50d..3ff1674f0 100644 --- a/routers/swagger_json.go +++ b/routers/swagger_json.go @@ -7,6 +7,7 @@ package routers import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" ) // tplSwaggerV1Json swagger v1 json template @@ -14,5 +15,10 @@ const tplSwaggerV1Json base.TplName = "swagger/v1_json" // SwaggerV1Json render swagger v1 json func SwaggerV1Json(ctx *context.Context) { - ctx.HTML(200, tplSwaggerV1Json) + t := ctx.Render.TemplateLookup(string(tplSwaggerV1Json)) + ctx.Resp.Header().Set("Content-Type", "application/json") + if err := t.Execute(ctx.Resp, ctx.Data); err != nil { + log.Error("%v", err) + ctx.Error(500) + } } diff --git a/routers/user/auth.go b/routers/user/auth.go index bce801847..909d0a2ee 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -12,22 +12,22 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/captcha" "github.com/markbates/goth" "github.com/tstranex/u2f" ) @@ -149,7 +149,7 @@ func SignIn(ctx *context.Context) { } // SignInPost response for sign in request -func SignInPost(ctx *context.Context, form auth.SignInForm) { +func SignInPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("sign_in") orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers() @@ -170,6 +170,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { return } + form := web.GetForm(ctx).(*auth.SignInForm) u, err := models.UserSignIn(form.UserName, form.Password) if err != nil { if models.IsErrUserNotExist(err) { @@ -250,7 +251,8 @@ func TwoFactor(ctx *context.Context) { } // TwoFactorPost validates a user's two-factor authentication token. -func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { +func TwoFactorPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorAuthForm) ctx.Data["Title"] = ctx.Tr("twofa") // Ensure user is in a 2FA session. @@ -328,7 +330,8 @@ func TwoFactorScratch(ctx *context.Context) { } // TwoFactorScratchPost validates and invalidates a user's two-factor scratch token. -func TwoFactorScratchPost(ctx *context.Context, form auth.TwoFactorScratchAuthForm) { +func TwoFactorScratchPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorScratchAuthForm) ctx.Data["Title"] = ctx.Tr("twofa_scratch") // Ensure user is in a 2FA session. @@ -427,7 +430,8 @@ func U2FChallenge(ctx *context.Context) { } // U2FSign authenticates the user by signResp -func U2FSign(ctx *context.Context, signResp u2f.SignResponse) { +func U2FSign(ctx *context.Context) { + signResp := web.GetForm(ctx).(*u2f.SignResponse) challSess := ctx.Session.Get("u2fChallenge") idSess := ctx.Session.Get("twofaUid") if challSess == nil || idSess == nil { @@ -447,7 +451,7 @@ func U2FSign(ctx *context.Context, signResp u2f.SignResponse) { log.Fatal("parsing u2f registration: %v", err) continue } - newCounter, authErr := r.Authenticate(signResp, *challenge, reg.Counter) + newCounter, authErr := r.Authenticate(*signResp, *challenge, reg.Counter) if authErr == nil { reg.Counter = newCounter user, err := models.GetUserByID(id) @@ -563,20 +567,20 @@ func SignInOAuth(ctx *context.Context) { } // try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user - user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp) + user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) if err == nil && user != nil { // we got the user without going through the whole OAuth2 authentication flow again handleOAuth2SignIn(user, gothUser, ctx, err) return } - if err = oauth2.Auth(loginSource.Name, ctx.Req.Request, ctx.Resp); err != nil { + if err = oauth2.Auth(loginSource.Name, ctx.Req, ctx.Resp); err != nil { if strings.Contains(err.Error(), "no provider for ") { if err = models.ResetOAuth2(); err != nil { ctx.ServerError("SignIn", err) return } - if err = oauth2.Auth(loginSource.Name, ctx.Req.Request, ctx.Resp); err != nil { + if err = oauth2.Auth(loginSource.Name, ctx.Req, ctx.Resp); err != nil { ctx.ServerError("SignIn", err) } return @@ -602,7 +606,7 @@ func SignInOAuthCallback(ctx *context.Context) { return } - u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp) + u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) handleOAuth2SignIn(u, gothUser, ctx, err) } @@ -788,7 +792,8 @@ func LinkAccount(ctx *context.Context) { } // LinkAccountPostSignIn handle the coupling of external account with another account using signIn -func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) { +func LinkAccountPostSignIn(ctx *context.Context) { + signInForm := web.GetForm(ctx).(*auth.SignInForm) ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["LinkAccountMode"] = true @@ -870,7 +875,8 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) { } // LinkAccountPostRegister handle the creation of a new account for an external account using signUp -func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) { +func LinkAccountPostRegister(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RegisterForm) // TODO Make insecure passwords optional for local accounts also, // once email-based Second-Factor Auth is available ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration @@ -909,7 +915,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: valid, err = recaptcha.Verify(ctx.Req.Context(), form.GRecaptchaResponse) case setting.HCaptcha: @@ -1029,7 +1035,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au // HandleSignOut resets the session and sets the cookies func HandleSignOut(ctx *context.Context) { _ = ctx.Session.Flush() - _ = ctx.Session.Destroy(ctx.Context) + _ = ctx.Session.Destroy(ctx.Resp, ctx.Req) ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) @@ -1069,7 +1075,8 @@ func SignUp(ctx *context.Context) { } // SignUpPost response for sign up information submission -func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) { +func SignUpPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RegisterForm) ctx.Data["Title"] = ctx.Tr("sign_up") ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" @@ -1097,7 +1104,7 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: valid, err = recaptcha.Verify(ctx.Req.Context(), form.GRecaptchaResponse) case setting.HCaptcha: @@ -1562,7 +1569,8 @@ func MustChangePassword(ctx *context.Context) { // MustChangePasswordPost response for updating a user's password after his/her // account was created by an admin -func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) { +func MustChangePasswordPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MustChangePasswordForm) ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" if ctx.HasError() { diff --git a/routers/user/auth_openid.go b/routers/user/auth_openid.go index 39e75f202..1efcc7eda 100644 --- a/routers/user/auth_openid.go +++ b/routers/user/auth_openid.go @@ -9,19 +9,18 @@ import ( "net/url" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" - - "gitea.com/macaron/captcha" ) const ( @@ -90,7 +89,8 @@ func allowedOpenIDURI(uri string) (err error) { } // SignInOpenIDPost response for openid sign in request -func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) { +func SignInOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SignInOpenIDForm) ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLoginOpenID"] = true @@ -143,9 +143,9 @@ func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) { // signInOpenIDVerify handles response from OpenID provider func signInOpenIDVerify(ctx *context.Context) { - log.Trace("Incoming call to: " + ctx.Req.Request.URL.String()) + log.Trace("Incoming call to: " + ctx.Req.URL.String()) - fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] + fullURL := setting.AppURL + ctx.Req.URL.String()[1:] log.Trace("Full URL: " + fullURL) var id, err = openid.Verify(fullURL) @@ -276,8 +276,8 @@ func ConnectOpenID(ctx *context.Context) { } // ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account -func ConnectOpenIDPost(ctx *context.Context, form auth.ConnectOpenIDForm) { - +func ConnectOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ConnectOpenIDForm) oid, _ := ctx.Session.Get("openid_verified_uri").(string) if oid == "" { ctx.Redirect(setting.AppSubURL + "/user/login/openid") @@ -346,7 +346,8 @@ func RegisterOpenID(ctx *context.Context) { } // RegisterOpenIDPost handles submission of a form to create a new user authenticated via an OpenID URI -func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.SignUpOpenIDForm) { +func RegisterOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SignUpOpenIDForm) oid, _ := ctx.Session.Get("openid_verified_uri").(string) if oid == "" { ctx.Redirect(setting.AppSubURL + "/user/login/openid") @@ -369,7 +370,7 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: if err := ctx.Req.ParseForm(); err != nil { ctx.ServerError("", err) diff --git a/routers/user/oauth.go b/routers/user/oauth.go index dda1268f8..d943ec420 100644 --- a/routers/user/oauth.go +++ b/routers/user/oauth.go @@ -12,14 +12,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" "github.com/dgrijalva/jwt-go" ) @@ -192,9 +193,10 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac } // AuthorizeOAuth manages authorize requests -func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) { +func AuthorizeOAuth(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AuthorizationForm) errs := binding.Errors{} - errs = form.Validate(ctx.Context, errs) + errs = form.Validate(ctx.Req, errs) if len(errs) > 0 { errstring := "" for _, e := range errs { @@ -341,7 +343,8 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) { } // GrantApplicationOAuth manages the post request submitted when a user grants access to an application -func GrantApplicationOAuth(ctx *context.Context, form auth.GrantApplicationForm) { +func GrantApplicationOAuth(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.GrantApplicationForm) if ctx.Session.Get("client_id") != form.ClientID || ctx.Session.Get("state") != form.State || ctx.Session.Get("redirect_uri") != form.RedirectURI { ctx.Error(400) @@ -386,7 +389,8 @@ func GrantApplicationOAuth(ctx *context.Context, form auth.GrantApplicationForm) } // AccessTokenOAuth manages all access token requests by the client -func AccessTokenOAuth(ctx *context.Context, form auth.AccessTokenForm) { +func AccessTokenOAuth(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AccessTokenForm) if form.ClientID == "" { authHeader := ctx.Req.Header.Get("Authorization") authContent := strings.SplitN(authHeader, " ", 2) diff --git a/routers/user/setting/account.go b/routers/user/setting/account.go index 42c2c59b7..4900bba14 100644 --- a/routers/user/setting/account.go +++ b/routers/user/setting/account.go @@ -10,13 +10,14 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" ) @@ -36,7 +37,8 @@ func Account(ctx *context.Context) { } // AccountPost response for change user's password -func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) { +func AccountPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ChangePasswordForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -80,7 +82,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) { } // EmailPost response for change user's email -func EmailPost(ctx *context.Context, form auth.AddEmailForm) { +func EmailPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddEmailForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -252,8 +255,8 @@ func DeleteAccount(ctx *context.Context) { } // UpdateUIThemePost is used to update users' specific theme -func UpdateUIThemePost(ctx *context.Context, form auth.UpdateThemeForm) { - +func UpdateUIThemePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateThemeForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true diff --git a/routers/user/setting/account_test.go b/routers/user/setting/account_test.go index 841ecb8ac..0e7e147b8 100644 --- a/routers/user/setting/account_test.go +++ b/routers/user/setting/account_test.go @@ -9,9 +9,10 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -85,11 +86,12 @@ func TestChangePassword(t *testing.T) { test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - AccountPost(ctx, auth.ChangePasswordForm{ + web.SetForm(ctx, &auth.ChangePasswordForm{ OldPassword: req.OldPassword, Password: req.NewPassword, Retype: req.Retype, }) + AccountPost(ctx) assert.Contains(t, ctx.Flash.ErrorMsg, req.Message) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) diff --git a/routers/user/setting/applications.go b/routers/user/setting/applications.go index 04f9d9f7f..8da36dc6c 100644 --- a/routers/user/setting/applications.go +++ b/routers/user/setting/applications.go @@ -7,10 +7,11 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -28,7 +29,8 @@ func Applications(ctx *context.Context) { } // ApplicationsPost response for add user's access token -func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { +func ApplicationsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewAccessTokenForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true diff --git a/routers/user/setting/keys.go b/routers/user/setting/keys.go index 76c7ef9da..a52ffd667 100644 --- a/routers/user/setting/keys.go +++ b/routers/user/setting/keys.go @@ -7,10 +7,11 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -31,7 +32,8 @@ func Keys(ctx *context.Context) { } // KeysPost response for change user's SSH/GPG keys -func KeysPost(ctx *context.Context, form auth.AddKeyForm) { +func KeysPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddKeyForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled diff --git a/routers/user/setting/oauth2.go b/routers/user/setting/oauth2.go index f42c1123e..7f0f8db1c 100644 --- a/routers/user/setting/oauth2.go +++ b/routers/user/setting/oauth2.go @@ -8,11 +8,12 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -20,7 +21,8 @@ const ( ) // OAuthApplicationsPost response for adding a oauth2 application -func OAuthApplicationsPost(ctx *context.Context, form auth.EditOAuth2ApplicationForm) { +func OAuthApplicationsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditOAuth2ApplicationForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true @@ -51,7 +53,8 @@ func OAuthApplicationsPost(ctx *context.Context, form auth.EditOAuth2Application } // OAuthApplicationsEdit response for editing oauth2 application -func OAuthApplicationsEdit(ctx *context.Context, form auth.EditOAuth2ApplicationForm) { +func OAuthApplicationsEdit(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditOAuth2ApplicationForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index c935b5623..7e90a7cce 100644 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -14,12 +14,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "github.com/unknwon/i18n" ) @@ -71,7 +72,8 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin } // ProfilePost response for change user's profile -func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { +func ProfilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateProfileForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsProfile"] = true @@ -123,7 +125,7 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { // UpdateAvatarSetting update user's avatar // FIXME: limit size. -func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error { +func UpdateAvatarSetting(ctx *context.Context, form *auth.AvatarForm, ctxUser *models.User) error { ctxUser.UseCustomAvatar = form.Source == auth.AvatarLocal if len(form.Gravatar) > 0 { if form.Avatar != nil { @@ -171,7 +173,8 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *mo } // AvatarPost response for change user's avatar request -func AvatarPost(ctx *context.Context, form auth.AvatarForm) { +func AvatarPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil { ctx.Flash.Error(err.Error()) } else { diff --git a/routers/user/setting/security_openid.go b/routers/user/setting/security_openid.go index 6813765f6..401705608 100644 --- a/routers/user/setting/security_openid.go +++ b/routers/user/setting/security_openid.go @@ -6,15 +6,17 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) // OpenIDPost response for change user's openid -func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { +func OpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddOpenIDForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true @@ -70,9 +72,9 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { } func settingsOpenIDVerify(ctx *context.Context) { - log.Trace("Incoming call to: " + ctx.Req.Request.URL.String()) + log.Trace("Incoming call to: " + ctx.Req.URL.String()) - fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] + fullURL := setting.AppURL + ctx.Req.URL.String()[1:] log.Trace("Full URL: " + fullURL) id, err := openid.Verify(fullURL) diff --git a/routers/user/setting/security_twofa.go b/routers/user/setting/security_twofa.go index 3f4c8f6c3..0dee827ca 100644 --- a/routers/user/setting/security_twofa.go +++ b/routers/user/setting/security_twofa.go @@ -13,10 +13,11 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" @@ -165,7 +166,8 @@ func EnrollTwoFactor(ctx *context.Context) { } // EnrollTwoFactorPost handles enrolling the user into 2FA. -func EnrollTwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { +func EnrollTwoFactorPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorAuthForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true diff --git a/routers/user/setting/security_u2f.go b/routers/user/setting/security_u2f.go index 7e32b4aae..8140c3c04 100644 --- a/routers/user/setting/security_u2f.go +++ b/routers/user/setting/security_u2f.go @@ -8,16 +8,18 @@ import ( "errors" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "github.com/tstranex/u2f" ) // U2FRegister initializes the u2f registration procedure -func U2FRegister(ctx *context.Context, form auth.U2FRegistrationForm) { +func U2FRegister(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.U2FRegistrationForm) if form.Name == "" { ctx.Error(409) return @@ -55,7 +57,8 @@ func U2FRegister(ctx *context.Context, form auth.U2FRegistrationForm) { } // U2FRegisterPost receives the response of the security key -func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { +func U2FRegisterPost(ctx *context.Context) { + response := web.GetForm(ctx).(*u2f.RegisterResponse) challSess := ctx.Session.Get("u2fChallenge") u2fName := ctx.Session.Get("u2fName") if challSess == nil || u2fName == nil { @@ -69,7 +72,7 @@ func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { // certificate by default. SkipAttestationVerify: true, } - reg, err := u2f.Register(response, *challenge, config) + reg, err := u2f.Register(*response, *challenge, config) if err != nil { ctx.ServerError("u2f.Register", err) return @@ -82,7 +85,8 @@ func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { } // U2FDelete deletes an security key by id -func U2FDelete(ctx *context.Context, form auth.U2FDeleteForm) { +func U2FDelete(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.U2FDeleteForm) reg, err := models.GetU2FRegistrationByID(form.ID) if err != nil { if models.IsErrU2FRegistrationNotExist(err) { diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 3fbb80caf..aa6a9b793 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -340,23 +340,6 @@
{{.Config | JsonPrettyPrint}}
{{end}}
-
{{$.i18n.Tr "admin.config.macaron_log_mode"}}
- {{if .RedirectMacaronLog}} - {{if .Loggers.macaron.SubLogDescriptions}} -
{{$.i18n.Tr "admin.config.own_named_logger"}}
- {{range .Loggers.macaron.SubLogDescriptions}} -
{{$.i18n.Tr "admin.config.log_mode"}}
-
{{.Name}} ({{.Provider}})
-
{{$.i18n.Tr "admin.config.log_config"}}
-
{{.Config | JsonPrettyPrint}}
- {{end}} - {{else}} -
{{$.i18n.Tr "admin.config.routes_to_default_logger"}}
- {{end}} - {{else}} -
{{$.i18n.Tr "admin.config.go_log"}}
- {{end}} -
{{$.i18n.Tr "admin.config.router_log_mode"}}
{{if .DisableRouterLog}}
{{$.i18n.Tr "admin.config.disabled_logger"}}
diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl index 5bb71ba49..f00237047 100644 --- a/templates/repo/sub_menu.tmpl +++ b/templates/repo/sub_menu.tmpl @@ -6,7 +6,7 @@ {{svg "octicon-history"}} {{.CommitsCount}} {{.i18n.Tr (TrN .i18n.Lang .CommitsCount "repo.commit" "repo.commits") }}
- {{svg "octicon-git-branch"}} {{.BranchesCount}} {{.i18n.Tr (TrN .i18n.Lang .BranchesCount "repo.branch" "repo.branches") }} + {{svg "octicon-git-branch"}} {{.BranchesCount}} {{.i18n.Tr (TrN .i18n.Lang .BranchesCount "repo.branch" "repo.branches") }}
{{if $.Permission.CanRead $.UnitTypeCode}}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0f26943da..86f09e2bb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14212,7 +14212,7 @@ } }, "x-go-name": "MergePullRequestForm", - "x-go-package": "code.gitea.io/gitea/modules/auth" + "x-go-package": "code.gitea.io/gitea/modules/forms" }, "MigrateRepoForm": { "description": "MigrateRepoForm form for migrating repository\nthis is used to interact with web ui", @@ -14292,7 +14292,7 @@ "x-go-name": "Wiki" } }, - "x-go-package": "code.gitea.io/gitea/modules/auth" + "x-go-package": "code.gitea.io/gitea/modules/forms" }, "MigrateRepoOptions": { "description": "MigrateRepoOptions options for migrating repository's\nthis is used to interact with api v1", diff --git a/vendor/gitea.com/macaron/binding/.drone.yml b/vendor/gitea.com/go-chi/binding/.drone.yml similarity index 100% rename from vendor/gitea.com/macaron/binding/.drone.yml rename to vendor/gitea.com/go-chi/binding/.drone.yml diff --git a/vendor/gitea.com/macaron/binding/.gitignore b/vendor/gitea.com/go-chi/binding/.gitignore similarity index 100% rename from vendor/gitea.com/macaron/binding/.gitignore rename to vendor/gitea.com/go-chi/binding/.gitignore diff --git a/vendor/gitea.com/macaron/binding/LICENSE b/vendor/gitea.com/go-chi/binding/LICENSE similarity index 100% rename from vendor/gitea.com/macaron/binding/LICENSE rename to vendor/gitea.com/go-chi/binding/LICENSE diff --git a/vendor/gitea.com/go-chi/binding/README.md b/vendor/gitea.com/go-chi/binding/README.md new file mode 100644 index 000000000..80ea76373 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/README.md @@ -0,0 +1,5 @@ +Middleware binding provides request data binding and validation for net/http, It's a fork of [Macaron](https://github.com/go-macaron/macaron). + +## 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/go-chi/binding/binding.go similarity index 80% rename from vendor/gitea.com/macaron/binding/binding.go rename to vendor/gitea.com/go-chi/binding/binding.go index 633d836b6..acd2a7e17 100644 --- a/vendor/gitea.com/macaron/binding/binding.go +++ b/vendor/gitea.com/go-chi/binding/binding.go @@ -1,5 +1,6 @@ // Copyright 2014 Martini Authors // Copyright 2014 The Macaron Authors +// Copyright 2020 The Gitea 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 @@ -13,7 +14,7 @@ // 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 is a middleware that provides request data binding and validation for Chi. package binding import ( @@ -29,26 +30,27 @@ import ( "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 { +// 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(req *http.Request, obj interface{}) Errors { + contentType := req.Header.Get("Content-Type") + if req.Method == "POST" || req.Method == "PUT" || len(contentType) > 0 { switch { case strings.Contains(contentType, "form-urlencoded"): - ctx.Invoke(Form(obj, ifacePtr...)) + return Form(req, obj) case strings.Contains(contentType, "multipart/form-data"): - ctx.Invoke(MultipartForm(obj, ifacePtr...)) + return MultipartForm(req, obj) case strings.Contains(contentType, "json"): - ctx.Invoke(Json(obj, ifacePtr...)) + return JSON(req, obj) default: var errors Errors if contentType == "" { @@ -56,11 +58,10 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) { } 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. + return errors } } else { - ctx.Invoke(Form(obj, ifacePtr...)) + return Form(req, obj) } } @@ -94,34 +95,6 @@ func errorHandler(errs Errors, rw http.ResponseWriter) { } } -// 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 @@ -131,27 +104,25 @@ func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler { // 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...) +func Form(req *http.Request, formStruct interface{}) Errors { + var errors Errors + + ensurePointer(formStruct) + formStructV := reflect.ValueOf(formStruct) + parseErr := 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(formStructV, req.Form, nil, errors) + return append(errors, Validate(req, formStruct)...) } -// Maximum amount of memory to use when parsing a multipart form. +// MaxMemory represents 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) @@ -159,57 +130,53 @@ var MaxMemory = int64(1024 * 1024 * 10) // 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...) - } +func MultipartForm(req *http.Request, formStruct interface{}) Errors { + var errors Errors + ensurePointer(formStruct) + formStructV := reflect.ValueOf(formStruct) + // This if check is necessary due to https://github.com/martini-contrib/csrf/issues/6 + if 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 := 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()) + } - ctx.Req.MultipartForm = form + if req.Form == nil { + req.ParseForm() } + for k, v := range form.Value { + req.Form[k] = append(req.Form[k], v...) + } + + req.MultipartForm = form } - errors = mapForm(formStruct, ctx.Req.MultipartForm.Value, ctx.Req.MultipartForm.File, errors) - validateAndMap(formStruct, ctx, errors, ifacePtr...) } + errors = mapForm(formStructV, req.MultipartForm.Value, req.MultipartForm.File, errors) + return append(errors, Validate(req, formStruct)...) } -// Json is middleware to deserialize a JSON payload from the request +// 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()) - } +func JSON(req *http.Request, jsonStruct interface{}) Errors { + var errors Errors + ensurePointer(jsonStruct) + + if req.Body != nil { + defer req.Body.Close() + err := json.NewDecoder(req.Body).Decode(jsonStruct) + if err != nil && err != io.EOF { + errors.Add([]string{}, ERR_DESERIALIZATION, err.Error()) } - validateAndMap(jsonStruct, ctx, errors, ifacePtr...) } + return append(errors, Validate(req, jsonStruct)...) } // RawValidate is same as Validate but does not require a HTTP context, @@ -238,31 +205,29 @@ func RawValidate(obj interface{}) Errors { // 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) +func Validate(req *http.Request, 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) + if validator, ok := e.(Validator); ok { + errs = validator.Validate(req, errs) } } - ctx.Map(errs) + } else { + errs = validateStruct(errs, obj) + if validator, ok := obj.(Validator); ok { + errs = validator.Validate(req, errs) + } } + return errs } var ( @@ -394,6 +359,13 @@ func validateStruct(errors Errors, obj interface{}) Errors { 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") + } +} + 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++ { @@ -715,36 +687,18 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V 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") +// Pointers must be bind to. +func ensurePointer(obj interface{}) { + if reflect.TypeOf(obj).Kind() != reflect.Ptr { + panic("Pointers are only 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) + Error(*http.Request, Errors) } // Validator is the interface that handles some rudimentary @@ -756,6 +710,6 @@ type ( // 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 + Validate(*http.Request, Errors) Errors } ) diff --git a/vendor/gitea.com/macaron/binding/errors.go b/vendor/gitea.com/go-chi/binding/errors.go similarity index 100% rename from vendor/gitea.com/macaron/binding/errors.go rename to vendor/gitea.com/go-chi/binding/errors.go diff --git a/vendor/gitea.com/macaron/binding/go.mod b/vendor/gitea.com/go-chi/binding/go.mod similarity index 57% rename from vendor/gitea.com/macaron/binding/go.mod rename to vendor/gitea.com/go-chi/binding/go.mod index 8609495d4..68e7cc5c7 100644 --- a/vendor/gitea.com/macaron/binding/go.mod +++ b/vendor/gitea.com/go-chi/binding/go.mod @@ -1,9 +1,9 @@ -module gitea.com/macaron/binding +module gitea.com/go-chi/binding -go 1.11 +go 1.13 require ( - gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/go-chi/chi v1.5.1 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/go-chi/binding/go.sum similarity index 68% rename from vendor/gitea.com/macaron/toolbox/go.sum rename to vendor/gitea.com/go-chi/binding/go.sum index 56302b6a5..158860f7c 100644 --- a/vendor/gitea.com/macaron/toolbox/go.sum +++ b/vendor/gitea.com/go-chi/binding/go.sum @@ -1,7 +1,5 @@ -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/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w= +github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= 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= @@ -18,13 +16,7 @@ github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6x 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/go-chi/cache/.drone.yml similarity index 100% rename from vendor/gitea.com/macaron/cache/.drone.yml rename to vendor/gitea.com/go-chi/cache/.drone.yml diff --git a/vendor/gitea.com/macaron/cache/.gitignore b/vendor/gitea.com/go-chi/cache/.gitignore similarity index 100% rename from vendor/gitea.com/macaron/cache/.gitignore rename to vendor/gitea.com/go-chi/cache/.gitignore diff --git a/vendor/gitea.com/macaron/cache/LICENSE b/vendor/gitea.com/go-chi/cache/LICENSE similarity index 100% rename from vendor/gitea.com/macaron/cache/LICENSE rename to vendor/gitea.com/go-chi/cache/LICENSE diff --git a/vendor/gitea.com/macaron/cache/README.md b/vendor/gitea.com/go-chi/cache/README.md similarity index 100% rename from vendor/gitea.com/macaron/cache/README.md rename to vendor/gitea.com/go-chi/cache/README.md diff --git a/vendor/gitea.com/macaron/cache/cache.go b/vendor/gitea.com/go-chi/cache/cache.go similarity index 74% rename from vendor/gitea.com/macaron/cache/cache.go rename to vendor/gitea.com/go-chi/cache/cache.go index 0893bca6c..79d92cc6d 100644 --- a/vendor/gitea.com/macaron/cache/cache.go +++ b/vendor/gitea.com/go-chi/cache/cache.go @@ -18,16 +18,8 @@ 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. @@ -62,24 +54,18 @@ type Options struct { Section string } -func prepareOptions(options []Options) Options { - var opt Options - if len(options) > 0 { - opt = options[0] - } +func prepareOptions(opt Options) Options { 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") + opt.Adapter = "memory" } if opt.Interval == 0 { - opt.Interval = sec.Key("INTERVAL").MustInt(60) + opt.Interval = 60 } if len(opt.AdapterConfig) == 0 { - opt.AdapterConfig = sec.Key("ADAPTER_CONFIG").MustString("data/caches") + opt.AdapterConfig = "data/caches" } return opt @@ -87,27 +73,15 @@ func prepareOptions(options []Options) Options { // 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] +func NewCacher(opt Options) (Cache, error) { + opt = prepareOptions(opt) + adapter, ok := adapters[opt.Adapter] if !ok { - return nil, fmt.Errorf("cache: unknown adapter '%s'(forgot to import?)", name) + return nil, fmt.Errorf("cache: unknown adapter '%s'(forgot to import?)", opt.Adapter) } 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. diff --git a/vendor/gitea.com/macaron/cache/file.go b/vendor/gitea.com/go-chi/cache/file.go similarity index 98% rename from vendor/gitea.com/macaron/cache/file.go rename to vendor/gitea.com/go-chi/cache/file.go index 3a51892b4..d07ab0b11 100644 --- a/vendor/gitea.com/macaron/cache/file.go +++ b/vendor/gitea.com/go-chi/cache/file.go @@ -26,7 +26,6 @@ import ( "sync" "time" - "gitea.com/macaron/macaron" "github.com/unknwon/com" ) @@ -191,7 +190,7 @@ func (c *FileCacher) StartAndGC(opt Options) error { c.interval = opt.Interval if !filepath.IsAbs(c.rootPath) { - c.rootPath = filepath.Join(macaron.Root, c.rootPath) + panic("rootPath must be an absolute path") } c.lock.Unlock() diff --git a/vendor/gitea.com/macaron/cache/go.mod b/vendor/gitea.com/go-chi/cache/go.mod similarity index 93% rename from vendor/gitea.com/macaron/cache/go.mod rename to vendor/gitea.com/go-chi/cache/go.mod index d214f0c34..3609e210b 100644 --- a/vendor/gitea.com/macaron/cache/go.mod +++ b/vendor/gitea.com/go-chi/cache/go.mod @@ -1,9 +1,8 @@ -module gitea.com/macaron/cache +module gitea.com/go-chi/cache go 1.11 require ( - gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb github.com/BurntSushi/toml v0.3.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 // indirect diff --git a/vendor/gitea.com/macaron/cache/go.sum b/vendor/gitea.com/go-chi/cache/go.sum similarity index 93% rename from vendor/gitea.com/macaron/cache/go.sum rename to vendor/gitea.com/go-chi/cache/go.sum index ceec0162a..8ed30d7a7 100644 --- a/vendor/gitea.com/macaron/cache/go.sum +++ b/vendor/gitea.com/go-chi/cache/go.sum @@ -1,7 +1,3 @@ -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= @@ -70,8 +66,6 @@ github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6x 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= @@ -98,7 +92,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ 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= diff --git a/vendor/gitea.com/macaron/cache/memcache/memcache.go b/vendor/gitea.com/go-chi/cache/memcache/memcache.go similarity index 98% rename from vendor/gitea.com/macaron/cache/memcache/memcache.go rename to vendor/gitea.com/go-chi/cache/memcache/memcache.go index 79f44bfcc..400a77456 100644 --- a/vendor/gitea.com/macaron/cache/memcache/memcache.go +++ b/vendor/gitea.com/go-chi/cache/memcache/memcache.go @@ -21,7 +21,7 @@ import ( "github.com/bradfitz/gomemcache/memcache" "github.com/unknwon/com" - "gitea.com/macaron/cache" + "gitea.com/go-chi/cache" ) // MemcacheCacher represents a memcache cache adapter implementation. diff --git a/vendor/gitea.com/macaron/cache/memcache/memcache.goconvey b/vendor/gitea.com/go-chi/cache/memcache/memcache.goconvey similarity index 100% rename from vendor/gitea.com/macaron/cache/memcache/memcache.goconvey rename to vendor/gitea.com/go-chi/cache/memcache/memcache.goconvey diff --git a/vendor/gitea.com/macaron/cache/memory.go b/vendor/gitea.com/go-chi/cache/memory.go similarity index 100% rename from vendor/gitea.com/macaron/cache/memory.go rename to vendor/gitea.com/go-chi/cache/memory.go diff --git a/vendor/gitea.com/macaron/cache/utils.go b/vendor/gitea.com/go-chi/cache/utils.go similarity index 100% rename from vendor/gitea.com/macaron/cache/utils.go rename to vendor/gitea.com/go-chi/cache/utils.go diff --git a/vendor/gitea.com/macaron/captcha/.drone.yml b/vendor/gitea.com/go-chi/captcha/.drone.yml similarity index 100% rename from vendor/gitea.com/macaron/captcha/.drone.yml rename to vendor/gitea.com/go-chi/captcha/.drone.yml diff --git a/vendor/gitea.com/macaron/captcha/LICENSE b/vendor/gitea.com/go-chi/captcha/LICENSE similarity index 100% rename from vendor/gitea.com/macaron/captcha/LICENSE rename to vendor/gitea.com/go-chi/captcha/LICENSE diff --git a/vendor/gitea.com/macaron/captcha/README.md b/vendor/gitea.com/go-chi/captcha/README.md similarity index 100% rename from vendor/gitea.com/macaron/captcha/README.md rename to vendor/gitea.com/go-chi/captcha/README.md diff --git a/vendor/gitea.com/macaron/captcha/captcha.go b/vendor/gitea.com/go-chi/captcha/captcha.go similarity index 77% rename from vendor/gitea.com/macaron/captcha/captcha.go rename to vendor/gitea.com/go-chi/captcha/captcha.go index 283db3b43..9d7e1f2cc 100644 --- a/vendor/gitea.com/macaron/captcha/captcha.go +++ b/vendor/gitea.com/go-chi/captcha/captcha.go @@ -20,11 +20,11 @@ import ( "fmt" "html/template" "image/color" + "net/http" "path" "strings" - "gitea.com/macaron/cache" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/cache" "github.com/unknwon/com" ) @@ -40,7 +40,7 @@ var ( // Captcha represents a captcha service. type Captcha struct { - store cache.Cache + Store cache.Cache SubURL string URLPrefix string FieldIdName string @@ -75,22 +75,17 @@ func (c *Captcha) CreateHTML() template.HTML { `, 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 { + 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 { +func (c *Captcha) VerifyReq(req *http.Request) bool { req.ParseForm() return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName)) } @@ -105,13 +100,13 @@ func (c *Captcha) Verify(id string, challenge string) bool { key := c.key(id) - if v, ok := c.store.Get(key).(string); ok { + if v, ok := c.Store.Get(key).(string); ok { chars = v } else { return false } - defer c.store.Delete(key) + defer c.Store.Delete(key) if len(chars) != len(challenge) { return false @@ -191,7 +186,8 @@ func prepareOptions(options []Options) Options { } // NewCaptcha initializes and returns a captcha with given options. -func NewCaptcha(opt Options) *Captcha { +func NewCaptcha(opts ...Options) *Captcha { + opt := prepareOptions(opts) return &Captcha{ SubURL: opt.SubURL, URLPrefix: opt.URLPrefix, @@ -209,45 +205,44 @@ func NewCaptcha(opt Options) *Captcha { // 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)) +func Captchaer(cpt *Captcha) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if strings.HasPrefix(req.URL.Path, cpt.URLPrefix) { + var chars string + id := path.Base(req.URL.Path) + if i := strings.Index(id, "."); i > -1 { + id = id[:i] } - } else { - if v, ok := cpt.store.Get(key).(string); ok { - chars = v + key := cpt.key(id) + + reloads := req.URL.Query()["reload"] + // Reload captcha. + if len(reloads) > 0 && len(reloads[0]) > 0 { + chars = cpt.genRandChars() + if err := cpt.Store.Put(key, chars, cpt.Expiration); err != nil { + w.WriteHeader(500) + w.Write([]byte("captcha reload error")) + panic(fmt.Errorf("reload captcha: %v", err)) + } } else { - ctx.Status(404) - ctx.Write([]byte("captcha not found")) - return + if v, ok := cpt.Store.Get(key).(string); ok { + chars = v + } else { + w.WriteHeader(404) + w.Write([]byte("captcha not found")) + return + } } - } - ctx.Status(200) - if _, err := NewImage([]byte(chars), cpt.StdWidth, cpt.StdHeight, cpt.ColorPalette).WriteTo(ctx.Resp); err != nil { - panic(fmt.Errorf("write captcha: %v", err)) + w.WriteHeader(200) + if _, err := NewImage([]byte(chars), cpt.StdWidth, cpt.StdHeight, cpt.ColorPalette).WriteTo(w); err != nil { + panic(fmt.Errorf("write captcha: %v", err)) + } + return } - return - } - ctx.Data["Captcha"] = cpt - ctx.Map(cpt) + next.ServeHTTP(w, req) + }) } } diff --git a/vendor/gitea.com/macaron/toolbox/go.mod b/vendor/gitea.com/go-chi/captcha/go.mod similarity index 55% rename from vendor/gitea.com/macaron/toolbox/go.mod rename to vendor/gitea.com/go-chi/captcha/go.mod index f54b8074c..adbf9ffb4 100644 --- a/vendor/gitea.com/macaron/toolbox/go.mod +++ b/vendor/gitea.com/go-chi/captcha/go.mod @@ -1,9 +1,10 @@ -module gitea.com/macaron/toolbox +module gitea.com/go-chi/captcha go 1.11 require ( - gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e + github.com/go-chi/chi v1.5.1 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/go-chi/captcha/go.sum similarity index 85% rename from vendor/gitea.com/macaron/captcha/go.sum rename to vendor/gitea.com/go-chi/captcha/go.sum index 6c6f0e4b6..ab69564a0 100644 --- a/vendor/gitea.com/macaron/captcha/go.sum +++ b/vendor/gitea.com/go-chi/captcha/go.sum @@ -1,20 +1,19 @@ -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= +gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e h1:zgPGaf3kXP0cVm9J0l8ZA2+XDzILYATg0CXbihR6N+o= +gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= 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-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w= +github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= 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/golang/snappy v0.0.2/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= @@ -31,7 +30,7 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W 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/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= 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= @@ -47,8 +46,6 @@ github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6x 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= @@ -69,8 +66,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw 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= diff --git a/vendor/gitea.com/macaron/captcha/image.go b/vendor/gitea.com/go-chi/captcha/image.go similarity index 100% rename from vendor/gitea.com/macaron/captcha/image.go rename to vendor/gitea.com/go-chi/captcha/image.go diff --git a/vendor/gitea.com/macaron/captcha/siprng.go b/vendor/gitea.com/go-chi/captcha/siprng.go similarity index 100% rename from vendor/gitea.com/macaron/captcha/siprng.go rename to vendor/gitea.com/go-chi/captcha/siprng.go diff --git a/vendor/gitea.com/lunny/log/.gitignore b/vendor/gitea.com/lunny/log/.gitignore deleted file mode 100644 index 3a11644b4..000000000 --- a/vendor/gitea.com/lunny/log/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -log.db -*.log -logs -.vscode \ No newline at end of file diff --git a/vendor/gitea.com/lunny/log/LICENSE b/vendor/gitea.com/lunny/log/LICENSE deleted file mode 100644 index c9338f829..000000000 --- a/vendor/gitea.com/lunny/log/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 - 2016 lunny -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the {organization} nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gitea.com/lunny/log/README.md b/vendor/gitea.com/lunny/log/README.md deleted file mode 100644 index be63a4d37..000000000 --- a/vendor/gitea.com/lunny/log/README.md +++ /dev/null @@ -1,51 +0,0 @@ -## log - -[![](https://goreportcard.com/badge/gitea.com/lunny/log)](https://goreportcard.com/report/gitea.com/lunny/log) -[![GoDoc](https://godoc.org/gitea.com/lunny/log?status.png)](https://godoc.org/gitea.com/lunny/log) - -[简体中文](https://gitea.com/lunny/log/blob/master/README_CN.md) - -# Installation - -``` -go get gitea.com/lunny/log -``` - -# Features - -* Add color support for unix console -* Implemented dbwriter to save log to database -* Implemented FileWriter to save log to file by date or time. -* Location configuration - -# Example - -For Single File: -```Go -f, _ := os.Create("my.log") -log.Std.SetOutput(f) -``` - -For Multiple Writer: -```Go -f, _ := os.Create("my.log") -log.Std.SetOutput(io.MultiWriter(f, os.Stdout)) -``` - -For log files by date or time: -```Go -w := log.NewFileWriter(log.FileOptions{ - ByType:log.ByDay, - Dir:"./logs", -}) -log.Std.SetOutput(w) -``` - -# About - -This repo is an extension of Golang log. - -# LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/vendor/gitea.com/lunny/log/README_CN.md b/vendor/gitea.com/lunny/log/README_CN.md deleted file mode 100644 index 29cd6c1ca..000000000 --- a/vendor/gitea.com/lunny/log/README_CN.md +++ /dev/null @@ -1,54 +0,0 @@ -## log - -[![](https://goreportcard.com/badge/gitea.com/lunny/log)](https://goreportcard.com/report/gitea.com/lunny/log) -[![GoDoc](https://godoc.org/gitea.com/lunny/log?status.png)](https://godoc.org/gitea.com/lunny/log) - -[English](https://gitea.com/lunny/log/blob/master/README.md) - -# 安装 - -``` -go get gitea.com/lunny/log -``` - -# 特性 - -* 对unix增加控制台颜色支持 -* 实现了保存log到数据库支持 -* 实现了保存log到按日期的文件支持 -* 实现了设置日期的地区 - -# 例子 - -保存到单个文件: - -```Go -f, _ := os.Create("my.log") -log.Std.SetOutput(f) -``` - -保存到数据库: - -```Go -f, _ := os.Create("my.log") -log.Std.SetOutput(io.MultiWriter(f, os.Stdout)) -``` - -保存到按时间分隔的文件: - -```Go -w := log.NewFileWriter(log.FileOptions{ - ByType:log.ByDay, - Dir:"./logs", -}) -log.Std.SetOutput(w) -``` - -# 关于 - -本 Log 是在 golang 的 log 之上的扩展 - -# LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/vendor/gitea.com/lunny/log/dbwriter.go b/vendor/gitea.com/lunny/log/dbwriter.go deleted file mode 100644 index e8ff00bd8..000000000 --- a/vendor/gitea.com/lunny/log/dbwriter.go +++ /dev/null @@ -1,36 +0,0 @@ -package log - -import ( - "database/sql" - "time" -) - -type DBWriter struct { - db *sql.DB - stmt *sql.Stmt - content chan []byte -} - -func NewDBWriter(db *sql.DB) (*DBWriter, error) { - _, err := db.Exec("CREATE TABLE IF NOT EXISTS log (id int, content text, created datetime)") - if err != nil { - return nil, err - } - stmt, err := db.Prepare("INSERT INTO log (content, created) values (?, ?)") - if err != nil { - return nil, err - } - return &DBWriter{db, stmt, make(chan []byte, 1000)}, nil -} - -func (w *DBWriter) Write(p []byte) (n int, err error) { - _, err = w.stmt.Exec(string(p), time.Now()) - if err == nil { - n = len(p) - } - return -} - -func (w *DBWriter) Close() { - w.stmt.Close() -} diff --git a/vendor/gitea.com/lunny/log/filewriter.go b/vendor/gitea.com/lunny/log/filewriter.go deleted file mode 100644 index f0bb4d1df..000000000 --- a/vendor/gitea.com/lunny/log/filewriter.go +++ /dev/null @@ -1,112 +0,0 @@ -package log - -import ( - "io" - "os" - "path/filepath" - "sync" - "time" -) - -var _ io.Writer = &Files{} - -type ByType int - -const ( - ByDay ByType = iota - ByHour - ByMonth -) - -var ( - formats = map[ByType]string{ - ByDay: "2006-01-02", - ByHour: "2006-01-02-15", - ByMonth: "2006-01", - } -) - -func SetFileFormat(t ByType, format string) { - formats[t] = format -} - -func (b ByType) Format() string { - return formats[b] -} - -type Files struct { - FileOptions - f *os.File - lastFormat string - lock sync.Mutex -} - -type FileOptions struct { - Dir string - ByType ByType - Loc *time.Location -} - -func prepareFileOption(opts []FileOptions) FileOptions { - var opt FileOptions - if len(opts) > 0 { - opt = opts[0] - } - if opt.Dir == "" { - opt.Dir = "./" - } - err := os.MkdirAll(opt.Dir, os.ModePerm) - if err != nil { - panic(err.Error()) - } - - if opt.Loc == nil { - opt.Loc = time.Local - } - return opt -} - -func NewFileWriter(opts ...FileOptions) *Files { - opt := prepareFileOption(opts) - return &Files{ - FileOptions: opt, - } -} - -func (f *Files) getFile() (*os.File, error) { - var err error - t := time.Now().In(f.Loc) - if f.f == nil { - f.lastFormat = t.Format(f.ByType.Format()) - f.f, err = os.OpenFile(filepath.Join(f.Dir, f.lastFormat+".log"), - os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) - return f.f, err - } - if f.lastFormat != t.Format(f.ByType.Format()) { - f.f.Close() - f.lastFormat = t.Format(f.ByType.Format()) - f.f, err = os.OpenFile(filepath.Join(f.Dir, f.lastFormat+".log"), - os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) - return f.f, err - } - return f.f, nil -} - -func (f *Files) Write(bs []byte) (int, error) { - f.lock.Lock() - defer f.lock.Unlock() - - w, err := f.getFile() - if err != nil { - return 0, err - } - return w.Write(bs) -} - -func (f *Files) Close() { - if f.f != nil { - f.f.Close() - f.f = nil - } - f.lastFormat = "" -} diff --git a/vendor/gitea.com/lunny/log/go.mod b/vendor/gitea.com/lunny/log/go.mod deleted file mode 100644 index f75670643..000000000 --- a/vendor/gitea.com/lunny/log/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module gitea.com/lunny/log - -go 1.12 - -require github.com/mattn/go-sqlite3 v1.10.0 diff --git a/vendor/gitea.com/lunny/log/go.sum b/vendor/gitea.com/lunny/log/go.sum deleted file mode 100644 index 263c18737..000000000 --- a/vendor/gitea.com/lunny/log/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= diff --git a/vendor/gitea.com/lunny/log/logext.go b/vendor/gitea.com/lunny/log/logext.go deleted file mode 100644 index 215c45f30..000000000 --- a/vendor/gitea.com/lunny/log/logext.go +++ /dev/null @@ -1,595 +0,0 @@ -package log - -import ( - "bytes" - "fmt" - "io" - "os" - "runtime" - "strings" - "sync" - "time" -) - -// These flags define which text to prefix to each log entry generated by the Logger. -const ( - // Bits or'ed together to control what's printed. There is no control over the - // order they appear (the order listed here) or the format they present (as - // described in the comments). A colon appears after these items: - // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message - Ldate = 1 << iota // the date: 2009/0123 - Ltime // the time: 01:23:23 - Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. - Llongfile // full file name and line number: /a/b/c/d.go:23 - Lshortfile // final file name element and line number: d.go:23. overrides Llongfile - Lmodule // module name - Llevel // level: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) - Llongcolor // color will start [info] end of line - Lshortcolor // color only include [info] - LstdFlags = Ldate | Ltime // initial values for the standard logger - //Ldefault = Llevel | LstdFlags | Lshortfile | Llongcolor -) // [prefix][time][level][module][shortfile|longfile] - -func Ldefault() int { - if runtime.GOOS == "windows" { - return Llevel | LstdFlags | Lshortfile - } - return Llevel | LstdFlags | Lshortfile | Llongcolor -} - -func Version() string { - return "0.2.0.1121" -} - -const ( - Lall = iota -) -const ( - Ldebug = iota - Linfo - Lwarn - Lerror - Lpanic - Lfatal - Lnone -) - -const ( - ForeBlack = iota + 30 //30 - ForeRed //31 - ForeGreen //32 - ForeYellow //33 - ForeBlue //34 - ForePurple //35 - ForeCyan //36 - ForeWhite //37 -) - -const ( - BackBlack = iota + 40 //40 - BackRed //41 - BackGreen //42 - BackYellow //43 - BackBlue //44 - BackPurple //45 - BackCyan //46 - BackWhite //47 -) - -var levels = []string{ - "[Debug]", - "[Info]", - "[Warn]", - "[Error]", - "[Panic]", - "[Fatal]", -} - -// MUST called before all logs -func SetLevels(lvs []string) { - levels = lvs -} - -var colors = []int{ - ForeCyan, - ForeGreen, - ForeYellow, - ForeRed, - ForePurple, - ForeBlue, -} - -// MUST called before all logs -func SetColors(cls []int) { - colors = cls -} - -// A Logger represents an active logging object that generates lines of -// output to an io.Writer. Each logging operation makes a single call to -// the Writer's Write method. A Logger can be used simultaneously from -// multiple goroutines; it guarantees to serialize access to the Writer. -type Logger struct { - mu sync.Mutex // ensures atomic writes; protects the following fields - prefix string // prefix to write at beginning of each line - flag int // properties - Level int - out io.Writer // destination for output - buf bytes.Buffer // for accumulating text to write - levelStats [6]int64 - loc *time.Location -} - -// New creates a new Logger. The out variable sets the -// destination to which log data will be written. -// The prefix appears at the beginning of each generated log line. -// The flag argument defines the logging properties. -func New(out io.Writer, prefix string, flag int) *Logger { - l := &Logger{out: out, prefix: prefix, Level: 1, flag: flag, loc: time.Local} - if out != os.Stdout { - l.flag = RmColorFlags(l.flag) - } - return l -} - -var Std = New(os.Stderr, "", Ldefault()) - -// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. -// Knows the buffer has capacity. -func itoa(buf *bytes.Buffer, i int, wid int) { - var u uint = uint(i) - if u == 0 && wid <= 1 { - buf.WriteByte('0') - return - } - - // Assemble decimal in reverse order. - var b [32]byte - bp := len(b) - for ; u > 0 || wid > 0; u /= 10 { - bp-- - wid-- - b[bp] = byte(u%10) + '0' - } - - // avoid slicing b to avoid an allocation. - for bp < len(b) { - buf.WriteByte(b[bp]) - bp++ - } -} - -func moduleOf(file string) string { - pos := strings.LastIndex(file, "/") - if pos != -1 { - pos1 := strings.LastIndex(file[:pos], "/src/") - if pos1 != -1 { - return file[pos1+5 : pos] - } - } - return "UNKNOWN" -} - -func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, - file string, line int, lvl int, reqId string) { - if l.prefix != "" { - buf.WriteString(l.prefix) - } - if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { - if l.flag&Ldate != 0 { - year, month, day := t.Date() - itoa(buf, year, 4) - buf.WriteByte('/') - itoa(buf, int(month), 2) - buf.WriteByte('/') - itoa(buf, day, 2) - buf.WriteByte(' ') - } - if l.flag&(Ltime|Lmicroseconds) != 0 { - hour, min, sec := t.Clock() - itoa(buf, hour, 2) - buf.WriteByte(':') - itoa(buf, min, 2) - buf.WriteByte(':') - itoa(buf, sec, 2) - if l.flag&Lmicroseconds != 0 { - buf.WriteByte('.') - itoa(buf, t.Nanosecond()/1e3, 6) - } - buf.WriteByte(' ') - } - } - if reqId != "" { - buf.WriteByte('[') - buf.WriteString(reqId) - buf.WriteByte(']') - buf.WriteByte(' ') - } - - if l.flag&(Lshortcolor|Llongcolor) != 0 { - buf.WriteString(fmt.Sprintf("\033[1;%dm", colors[lvl])) - } - if l.flag&Llevel != 0 { - buf.WriteString(levels[lvl]) - buf.WriteByte(' ') - } - if l.flag&Lshortcolor != 0 { - buf.WriteString("\033[0m") - } - - if l.flag&Lmodule != 0 { - buf.WriteByte('[') - buf.WriteString(moduleOf(file)) - buf.WriteByte(']') - buf.WriteByte(' ') - } - if l.flag&(Lshortfile|Llongfile) != 0 { - if l.flag&Lshortfile != 0 { - short := file - for i := len(file) - 1; i > 0; i-- { - if file[i] == '/' { - short = file[i+1:] - break - } - } - file = short - } - buf.WriteString(file) - buf.WriteByte(':') - itoa(buf, line, -1) - buf.WriteByte(' ') - } -} - -// Output writes the output for a logging event. The string s contains -// the text to print after the prefix specified by the flags of the -// Logger. A newline is appended if the last character of s is not -// already a newline. Calldepth is used to recover the PC and is -// provided for generality, although at the moment on all pre-defined -// paths it will be 2. -func (l *Logger) Output(reqId string, lvl int, calldepth int, s string) error { - if lvl < l.Level { - return nil - } - now := time.Now().In(l.loc) // get this early. - var file string - var line int - l.mu.Lock() - defer l.mu.Unlock() - if l.flag&(Lshortfile|Llongfile|Lmodule) != 0 { - // release lock while getting caller info - it's expensive. - l.mu.Unlock() - var ok bool - _, file, line, ok = runtime.Caller(calldepth) - if !ok { - file = "???" - line = 0 - } - l.mu.Lock() - } - l.levelStats[lvl]++ - l.buf.Reset() - l.formatHeader(&l.buf, now, file, line, lvl, reqId) - l.buf.WriteString(s) - if l.flag&Llongcolor != 0 { - l.buf.WriteString("\033[0m") - } - if len(s) > 0 && s[len(s)-1] != '\n' { - l.buf.WriteByte('\n') - } - _, err := l.out.Write(l.buf.Bytes()) - return err -} - -// ----------------------------------------- - -// Printf calls l.Output to print to the logger. -// Arguments are handled in the manner of fmt.Printf. -func (l *Logger) Printf(format string, v ...interface{}) { - l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) -} - -// Print calls l.Output to print to the logger. -// Arguments are handled in the manner of fmt.Print. -func (l *Logger) Print(v ...interface{}) { - l.Output("", Linfo, 2, fmt.Sprint(v...)) -} - -// Println calls l.Output to print to the logger. -// Arguments are handled in the manner of fmt.Println. -func (l *Logger) Println(v ...interface{}) { - l.Output("", Linfo, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func (l *Logger) Debugf(format string, v ...interface{}) { - l.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) -} - -func (l *Logger) Debug(v ...interface{}) { - l.Output("", Ldebug, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- -func (l *Logger) Infof(format string, v ...interface{}) { - l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) -} - -func (l *Logger) Info(v ...interface{}) { - l.Output("", Linfo, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- -func (l *Logger) Warnf(format string, v ...interface{}) { - l.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) -} - -func (l *Logger) Warn(v ...interface{}) { - l.Output("", Lwarn, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func (l *Logger) Errorf(format string, v ...interface{}) { - l.Output("", Lerror, 2, fmt.Sprintf(format, v...)) -} - -func (l *Logger) Error(v ...interface{}) { - l.Output("", Lerror, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func (l *Logger) Fatal(v ...interface{}) { - l.Output("", Lfatal, 2, fmt.Sprintln(v...)) - os.Exit(1) -} - -// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). -func (l *Logger) Fatalf(format string, v ...interface{}) { - l.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) - os.Exit(1) -} - -// ----------------------------------------- -// Panic is equivalent to l.Print() followed by a call to panic(). -func (l *Logger) Panic(v ...interface{}) { - s := fmt.Sprintln(v...) - l.Output("", Lpanic, 2, s) - panic(s) -} - -// Panicf is equivalent to l.Printf() followed by a call to panic(). -func (l *Logger) Panicf(format string, v ...interface{}) { - s := fmt.Sprintf(format, v...) - l.Output("", Lpanic, 2, s) - panic(s) -} - -// ----------------------------------------- -func (l *Logger) Stack(v ...interface{}) { - s := fmt.Sprint(v...) - s += "\n" - buf := make([]byte, 1024*1024) - n := runtime.Stack(buf, true) - s += string(buf[:n]) - s += "\n" - l.Output("", Lerror, 2, s) -} - -// ----------------------------------------- -func (l *Logger) Stat() (stats []int64) { - l.mu.Lock() - v := l.levelStats - l.mu.Unlock() - return v[:] -} - -// Flags returns the output flags for the logger. -func (l *Logger) Flags() int { - l.mu.Lock() - defer l.mu.Unlock() - return l.flag -} - -func RmColorFlags(flag int) int { - // for un std out, it should not show color since almost them don't support - if flag&Llongcolor != 0 { - flag = flag ^ Llongcolor - } - if flag&Lshortcolor != 0 { - flag = flag ^ Lshortcolor - } - return flag -} - -func (l *Logger) Location() *time.Location { - return l.loc -} - -func (l *Logger) SetLocation(loc *time.Location) { - l.loc = loc -} - -// SetFlags sets the output flags for the logger. -func (l *Logger) SetFlags(flag int) { - l.mu.Lock() - defer l.mu.Unlock() - if l.out != os.Stdout { - flag = RmColorFlags(flag) - } - l.flag = flag -} - -// Prefix returns the output prefix for the logger. -func (l *Logger) Prefix() string { - l.mu.Lock() - defer l.mu.Unlock() - return l.prefix -} - -// SetPrefix sets the output prefix for the logger. -func (l *Logger) SetPrefix(prefix string) { - l.mu.Lock() - defer l.mu.Unlock() - l.prefix = prefix -} - -// SetOutputLevel sets the output level for the logger. -func (l *Logger) SetOutputLevel(lvl int) { - l.mu.Lock() - defer l.mu.Unlock() - l.Level = lvl -} - -func (l *Logger) OutputLevel() int { - return l.Level -} - -func (l *Logger) SetOutput(w io.Writer) { - l.mu.Lock() - defer l.mu.Unlock() - l.out = w - if w != os.Stdout { - l.flag = RmColorFlags(l.flag) - } -} - -// SetOutput sets the output destination for the standard logger. -func SetOutput(w io.Writer) { - Std.SetOutput(w) -} - -func SetLocation(loc *time.Location) { - Std.SetLocation(loc) -} - -func Location() *time.Location { - return Std.Location() -} - -// Flags returns the output flags for the standard logger. -func Flags() int { - return Std.Flags() -} - -// SetFlags sets the output flags for the standard logger. -func SetFlags(flag int) { - Std.SetFlags(flag) -} - -// Prefix returns the output prefix for the standard logger. -func Prefix() string { - return Std.Prefix() -} - -// SetPrefix sets the output prefix for the standard logger. -func SetPrefix(prefix string) { - Std.SetPrefix(prefix) -} - -func SetOutputLevel(lvl int) { - Std.SetOutputLevel(lvl) -} - -func OutputLevel() int { - return Std.OutputLevel() -} - -// ----------------------------------------- - -// Print calls Output to print to the standard logger. -// Arguments are handled in the manner of fmt.Print. -func Print(v ...interface{}) { - Std.Output("", Linfo, 2, fmt.Sprintln(v...)) -} - -// Printf calls Output to print to the standard logger. -// Arguments are handled in the manner of fmt.Printf. -func Printf(format string, v ...interface{}) { - Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) -} - -// Println calls Output to print to the standard logger. -// Arguments are handled in the manner of fmt.Println. -func Println(v ...interface{}) { - Std.Output("", Linfo, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func Debugf(format string, v ...interface{}) { - Std.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) -} - -func Debug(v ...interface{}) { - Std.Output("", Ldebug, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func Infof(format string, v ...interface{}) { - Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) -} - -func Info(v ...interface{}) { - Std.Output("", Linfo, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func Warnf(format string, v ...interface{}) { - Std.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) -} - -func Warn(v ...interface{}) { - Std.Output("", Lwarn, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -func Errorf(format string, v ...interface{}) { - Std.Output("", Lerror, 2, fmt.Sprintf(format, v...)) -} - -func Error(v ...interface{}) { - Std.Output("", Lerror, 2, fmt.Sprintln(v...)) -} - -// ----------------------------------------- - -// Fatal is equivalent to Print() followed by a call to os.Exit(1). -func Fatal(v ...interface{}) { - Std.Output("", Lfatal, 2, fmt.Sprintln(v...)) -} - -// Fatalf is equivalent to Printf() followed by a call to os.Exit(1). -func Fatalf(format string, v ...interface{}) { - Std.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) -} - -// ----------------------------------------- - -// Panic is equivalent to Print() followed by a call to panic(). -func Panic(v ...interface{}) { - Std.Output("", Lpanic, 2, fmt.Sprintln(v...)) -} - -// Panicf is equivalent to Printf() followed by a call to panic(). -func Panicf(format string, v ...interface{}) { - Std.Output("", Lpanic, 2, fmt.Sprintf(format, v...)) -} - -// ----------------------------------------- - -func Stack(v ...interface{}) { - s := fmt.Sprint(v...) - s += "\n" - buf := make([]byte, 1024*1024) - n := runtime.Stack(buf, true) - s += string(buf[:n]) - s += "\n" - Std.Output("", Lerror, 2, s) -} - -// ----------------------------------------- diff --git a/vendor/gitea.com/lunny/nodb/.gitignore b/vendor/gitea.com/lunny/nodb/.gitignore deleted file mode 100644 index 8f4051772..000000000 --- a/vendor/gitea.com/lunny/nodb/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -build -*.pyc -.DS_Store -nohup.out -build_config.mk -var -.vscode diff --git a/vendor/gitea.com/lunny/nodb/LICENSE b/vendor/gitea.com/lunny/nodb/LICENSE deleted file mode 100644 index 7ece9fdf5..000000000 --- a/vendor/gitea.com/lunny/nodb/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 siddontang - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/gitea.com/lunny/nodb/README.md b/vendor/gitea.com/lunny/nodb/README.md deleted file mode 100644 index 673424338..000000000 --- a/vendor/gitea.com/lunny/nodb/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# NoDB - -[中文](https://gitea.com/lunny/nodb/blob/master/README_CN.md) - -Nodb is a fork of [ledisdb](https://github.com/siddontang/ledisdb) and shrink version. It's get rid of all C or other language codes and only keep Go's. It aims to provide a nosql database library rather than a redis like server. So if you want a redis like server, ledisdb is the best choose. - -Nodb is a pure Go and high performance NoSQL database library. It supports some data structure like kv, list, hash, zset, bitmap, set. - -Nodb now use [goleveldb](https://github.com/syndtr/goleveldb) as backend to store data. - -## Features - -+ Rich data structure: KV, List, Hash, ZSet, Bitmap, Set. -+ Stores lots of data, over the memory limit. -+ Supports expiration and ttl. -+ Easy to embed in your own Go application. - -## Install - - go get gitea.com/lunny/nodb - -## Package Example - -### Open And Select database -```go -import( - "gitea.com/lunny/nodb" - "gitea.com/lunny/nodb/config" -) - -cfg := new(config.Config) -cfg.DataDir = "./" -dbs, err := nodb.Open(cfg) -if err != nil { - fmt.Printf("nodb: error opening db: %v", err) -} - -db, _ := dbs.Select(0) -``` -### KV - -KV is the most basic nodb type like any other key-value database. -```go -err := db.Set(key, value) -value, err := db.Get(key) -``` -### List - -List is simply lists of values, sorted by insertion order. -You can push or pop value on the list head (left) or tail (right). -```go -err := db.LPush(key, value1) -err := db.RPush(key, value2) -value1, err := db.LPop(key) -value2, err := db.RPop(key) -``` -### Hash - -Hash is a map between fields and values. -```go -n, err := db.HSet(key, field1, value1) -n, err := db.HSet(key, field2, value2) -value1, err := db.HGet(key, field1) -value2, err := db.HGet(key, field2) -``` -### ZSet - -ZSet is a sorted collections of values. -Every member of zset is associated with score, a int64 value which used to sort, from smallest to greatest score. -Members are unique, but score may be same. -```go -n, err := db.ZAdd(key, ScorePair{score1, member1}, ScorePair{score2, member2}) -ay, err := db.ZRangeByScore(key, minScore, maxScore, 0, -1) -``` -## Links - -+ [Ledisdb Official Website](http://ledisdb.com) -+ [GoDoc](https://godoc.org/gitea.com/lunny/nodb) - - -## Thanks - -Gmail: siddontang@gmail.com diff --git a/vendor/gitea.com/lunny/nodb/README_CN.md b/vendor/gitea.com/lunny/nodb/README_CN.md deleted file mode 100644 index adbf51dce..000000000 --- a/vendor/gitea.com/lunny/nodb/README_CN.md +++ /dev/null @@ -1,80 +0,0 @@ -# NoDB - -[English](https://gitea.com/lunny/nodb/blob/master/README.md) - -Nodb 是 [ledisdb](https://github.com/siddontang/ledisdb) 的克隆和缩减版本。该版本去掉了所有C和其它语言的依赖,只保留Go语言的。目标是提供一个Nosql数据库的开发库而不是提供一个像Redis那样的服务器。因此如果你想要的是一个独立服务器,你可以直接选择ledisdb。 - -Nodb 是一个纯Go的高性能 NoSQL 数据库。他支持 kv, list, hash, zset, bitmap, set 等数据结构。 - -Nodb 当前底层使用 (goleveldb)[https://github.com/syndtr/goleveldb] 来存储数据。 - -## 特性 - -+ 丰富的数据结构支持: KV, List, Hash, ZSet, Bitmap, Set。 -+ 永久存储并且不受内存的限制。 -+ 高性能那个。 -+ 可以方便的嵌入到你的应用程序中。 - -## 安装 - - go get gitea.com/lunny/nodb - -## 例子 - -### 打开和选择数据库 -```go -import( - "gitea.com/lunny/nodb" - "gitea.com/lunny/nodb/config" -) - -cfg := new(config.Config) -cfg.DataDir = "./" -dbs, err := nodb.Open(cfg) -if err != nil { - fmt.Printf("nodb: error opening db: %v", err) -} -db, _ := dbs.Select(0) -``` -### KV - -KV 是最基础的功能,和其它Nosql一样。 -```go -err := db.Set(key, value) -value, err := db.Get(key) -``` -### List - -List 是一些值的简单列表,按照插入的顺序排列。你可以从左或右push和pop值。 -```go -err := db.LPush(key, value1) -err := db.RPush(key, value2) -value1, err := db.LPop(key) -value2, err := db.RPop(key) -``` -### Hash - -Hash 是一个field和value对应的map。 -```go -n, err := db.HSet(key, field1, value1) -n, err := db.HSet(key, field2, value2) -value1, err := db.HGet(key, field1) -value2, err := db.HGet(key, field2) -``` -### ZSet - -ZSet 是一个排序的值集合。zset的每个成员对应一个score,这是一个int64的值用于从小到大排序。成员不可重复,但是score可以相同。 -```go -n, err := db.ZAdd(key, ScorePair{score1, member1}, ScorePair{score2, member2}) -ay, err := db.ZRangeByScore(key, minScore, maxScore, 0, -1) -``` - -## 链接 - -+ [Ledisdb Official Website](http://ledisdb.com) -+ [GoDoc](https://godoc.org/gitea.com/lunny/nodb) - - -## 感谢 - -Gmail: siddontang@gmail.com diff --git a/vendor/gitea.com/lunny/nodb/batch.go b/vendor/gitea.com/lunny/nodb/batch.go deleted file mode 100644 index e2bc28995..000000000 --- a/vendor/gitea.com/lunny/nodb/batch.go +++ /dev/null @@ -1,106 +0,0 @@ -package nodb - -import ( - "sync" - - "gitea.com/lunny/nodb/store" -) - -type batch struct { - l *Nodb - - store.WriteBatch - - sync.Locker - - logs [][]byte - - tx *Tx -} - -func (b *batch) Commit() error { - b.l.commitLock.Lock() - defer b.l.commitLock.Unlock() - - err := b.WriteBatch.Commit() - - if b.l.binlog != nil { - if err == nil { - if b.tx == nil { - b.l.binlog.Log(b.logs...) - } else { - b.tx.logs = append(b.tx.logs, b.logs...) - } - } - b.logs = [][]byte{} - } - - return err -} - -func (b *batch) Lock() { - b.Locker.Lock() -} - -func (b *batch) Unlock() { - if b.l.binlog != nil { - b.logs = [][]byte{} - } - b.WriteBatch.Rollback() - b.Locker.Unlock() -} - -func (b *batch) Put(key []byte, value []byte) { - if b.l.binlog != nil { - buf := encodeBinLogPut(key, value) - b.logs = append(b.logs, buf) - } - b.WriteBatch.Put(key, value) -} - -func (b *batch) Delete(key []byte) { - if b.l.binlog != nil { - buf := encodeBinLogDelete(key) - b.logs = append(b.logs, buf) - } - b.WriteBatch.Delete(key) -} - -type dbBatchLocker struct { - l *sync.Mutex - wrLock *sync.RWMutex -} - -func (l *dbBatchLocker) Lock() { - l.wrLock.RLock() - l.l.Lock() -} - -func (l *dbBatchLocker) Unlock() { - l.l.Unlock() - l.wrLock.RUnlock() -} - -type txBatchLocker struct { -} - -func (l *txBatchLocker) Lock() {} -func (l *txBatchLocker) Unlock() {} - -type multiBatchLocker struct { -} - -func (l *multiBatchLocker) Lock() {} -func (l *multiBatchLocker) Unlock() {} - -func (l *Nodb) newBatch(wb store.WriteBatch, locker sync.Locker, tx *Tx) *batch { - b := new(batch) - b.l = l - b.WriteBatch = wb - - b.tx = tx - b.Locker = locker - - b.logs = [][]byte{} - return b -} diff --git a/vendor/gitea.com/lunny/nodb/binlog.go b/vendor/gitea.com/lunny/nodb/binlog.go deleted file mode 100644 index 50a9e9d29..000000000 --- a/vendor/gitea.com/lunny/nodb/binlog.go +++ /dev/null @@ -1,391 +0,0 @@ -package nodb - -import ( - "bufio" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "os" - "path" - "strconv" - "strings" - "sync" - "time" - - "gitea.com/lunny/log" - "gitea.com/lunny/nodb/config" -) - -type BinLogHead struct { - CreateTime uint32 - BatchId uint32 - PayloadLen uint32 -} - -func (h *BinLogHead) Len() int { - return 12 -} - -func (h *BinLogHead) Write(w io.Writer) error { - if err := binary.Write(w, binary.BigEndian, h.CreateTime); err != nil { - return err - } - - if err := binary.Write(w, binary.BigEndian, h.BatchId); err != nil { - return err - } - - if err := binary.Write(w, binary.BigEndian, h.PayloadLen); err != nil { - return err - } - - return nil -} - -func (h *BinLogHead) handleReadError(err error) error { - if err == io.EOF { - return io.ErrUnexpectedEOF - } else { - return err - } -} - -func (h *BinLogHead) Read(r io.Reader) error { - var err error - if err = binary.Read(r, binary.BigEndian, &h.CreateTime); err != nil { - return err - } - - if err = binary.Read(r, binary.BigEndian, &h.BatchId); err != nil { - return h.handleReadError(err) - } - - if err = binary.Read(r, binary.BigEndian, &h.PayloadLen); err != nil { - return h.handleReadError(err) - } - - return nil -} - -func (h *BinLogHead) InSameBatch(ho *BinLogHead) bool { - if h.CreateTime == ho.CreateTime && h.BatchId == ho.BatchId { - return true - } else { - return false - } -} - -/* -index file format: -ledis-bin.00001 -ledis-bin.00002 -ledis-bin.00003 - -log file format - -Log: Head|PayloadData - -Head: createTime|batchId|payloadData - -*/ - -type BinLog struct { - sync.Mutex - - path string - - cfg *config.BinLogConfig - - logFile *os.File - - logWb *bufio.Writer - - indexName string - logNames []string - lastLogIndex int64 - - batchId uint32 - - ch chan struct{} -} - -func NewBinLog(cfg *config.Config) (*BinLog, error) { - l := new(BinLog) - - l.cfg = &cfg.BinLog - l.cfg.Adjust() - - l.path = path.Join(cfg.DataDir, "binlog") - - if err := os.MkdirAll(l.path, os.ModePerm); err != nil { - return nil, err - } - - l.logNames = make([]string, 0, 16) - - l.ch = make(chan struct{}) - - if err := l.loadIndex(); err != nil { - return nil, err - } - - return l, nil -} - -func (l *BinLog) flushIndex() error { - data := strings.Join(l.logNames, "\n") - - bakName := fmt.Sprintf("%s.bak", l.indexName) - f, err := os.OpenFile(bakName, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - log.Errorf("create binlog bak index error %s", err.Error()) - return err - } - - if _, err := f.WriteString(data); err != nil { - log.Errorf("write binlog index error %s", err.Error()) - f.Close() - return err - } - - f.Close() - - if err := os.Rename(bakName, l.indexName); err != nil { - log.Errorf("rename binlog bak index error %s", err.Error()) - return err - } - - return nil -} - -func (l *BinLog) loadIndex() error { - l.indexName = path.Join(l.path, fmt.Sprintf("ledis-bin.index")) - if _, err := os.Stat(l.indexName); os.IsNotExist(err) { - //no index file, nothing to do - } else { - indexData, err := ioutil.ReadFile(l.indexName) - if err != nil { - return err - } - - lines := strings.Split(string(indexData), "\n") - for _, line := range lines { - line = strings.Trim(line, "\r\n ") - if len(line) == 0 { - continue - } - - if _, err := os.Stat(path.Join(l.path, line)); err != nil { - log.Errorf("load index line %s error %s", line, err.Error()) - return err - } else { - l.logNames = append(l.logNames, line) - } - } - } - if l.cfg.MaxFileNum > 0 && len(l.logNames) > l.cfg.MaxFileNum { - //remove oldest logfile - if err := l.Purge(len(l.logNames) - l.cfg.MaxFileNum); err != nil { - return err - } - } - - var err error - if len(l.logNames) == 0 { - l.lastLogIndex = 1 - } else { - lastName := l.logNames[len(l.logNames)-1] - - if l.lastLogIndex, err = strconv.ParseInt(path.Ext(lastName)[1:], 10, 64); err != nil { - log.Errorf("invalid logfile name %s", err.Error()) - return err - } - - //like mysql, if server restart, a new binlog will create - l.lastLogIndex++ - } - - return nil -} - -func (l *BinLog) getLogFile() string { - return l.FormatLogFileName(l.lastLogIndex) -} - -func (l *BinLog) openNewLogFile() error { - var err error - lastName := l.getLogFile() - - logPath := path.Join(l.path, lastName) - if l.logFile, err = os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY, 0666); err != nil { - log.Errorf("open new logfile error %s", err.Error()) - return err - } - - if l.cfg.MaxFileNum > 0 && len(l.logNames) == l.cfg.MaxFileNum { - l.purge(1) - } - - l.logNames = append(l.logNames, lastName) - - if l.logWb == nil { - l.logWb = bufio.NewWriterSize(l.logFile, 1024) - } else { - l.logWb.Reset(l.logFile) - } - - if err = l.flushIndex(); err != nil { - return err - } - - return nil -} - -func (l *BinLog) checkLogFileSize() bool { - if l.logFile == nil { - return false - } - - st, _ := l.logFile.Stat() - if st.Size() >= int64(l.cfg.MaxFileSize) { - l.closeLog() - return true - } - - return false -} - -func (l *BinLog) closeLog() { - l.lastLogIndex++ - - l.logFile.Close() - l.logFile = nil -} - -func (l *BinLog) purge(n int) { - for i := 0; i < n; i++ { - logPath := path.Join(l.path, l.logNames[i]) - os.Remove(logPath) - } - - copy(l.logNames[0:], l.logNames[n:]) - l.logNames = l.logNames[0 : len(l.logNames)-n] -} - -func (l *BinLog) Close() { - if l.logFile != nil { - l.logFile.Close() - l.logFile = nil - } -} - -func (l *BinLog) LogNames() []string { - return l.logNames -} - -func (l *BinLog) LogFileName() string { - return l.getLogFile() -} - -func (l *BinLog) LogFilePos() int64 { - if l.logFile == nil { - return 0 - } else { - st, _ := l.logFile.Stat() - return st.Size() - } -} - -func (l *BinLog) LogFileIndex() int64 { - return l.lastLogIndex -} - -func (l *BinLog) FormatLogFileName(index int64) string { - return fmt.Sprintf("ledis-bin.%07d", index) -} - -func (l *BinLog) FormatLogFilePath(index int64) string { - return path.Join(l.path, l.FormatLogFileName(index)) -} - -func (l *BinLog) LogPath() string { - return l.path -} - -func (l *BinLog) Purge(n int) error { - l.Lock() - defer l.Unlock() - - if len(l.logNames) == 0 { - return nil - } - - if n >= len(l.logNames) { - n = len(l.logNames) - //can not purge current log file - if l.logNames[n-1] == l.getLogFile() { - n = n - 1 - } - } - - l.purge(n) - - return l.flushIndex() -} - -func (l *BinLog) PurgeAll() error { - l.Lock() - defer l.Unlock() - - l.closeLog() - return l.openNewLogFile() -} - -func (l *BinLog) Log(args ...[]byte) error { - l.Lock() - defer l.Unlock() - - var err error - - if l.logFile == nil { - if err = l.openNewLogFile(); err != nil { - return err - } - } - - head := &BinLogHead{} - - head.CreateTime = uint32(time.Now().Unix()) - head.BatchId = l.batchId - - l.batchId++ - - for _, data := range args { - head.PayloadLen = uint32(len(data)) - - if err := head.Write(l.logWb); err != nil { - return err - } - - if _, err := l.logWb.Write(data); err != nil { - return err - } - } - - if err = l.logWb.Flush(); err != nil { - log.Errorf("write log error %s", err.Error()) - return err - } - - l.checkLogFileSize() - - close(l.ch) - l.ch = make(chan struct{}) - - return nil -} - -func (l *BinLog) Wait() <-chan struct{} { - return l.ch -} diff --git a/vendor/gitea.com/lunny/nodb/binlog_util.go b/vendor/gitea.com/lunny/nodb/binlog_util.go deleted file mode 100644 index 22124dda0..000000000 --- a/vendor/gitea.com/lunny/nodb/binlog_util.go +++ /dev/null @@ -1,215 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "fmt" - "strconv" -) - -var ( - errBinLogDeleteType = errors.New("invalid bin log delete type") - errBinLogPutType = errors.New("invalid bin log put type") - errBinLogCommandType = errors.New("invalid bin log command type") -) - -func encodeBinLogDelete(key []byte) []byte { - buf := make([]byte, 1+len(key)) - buf[0] = BinLogTypeDeletion - copy(buf[1:], key) - return buf -} - -func decodeBinLogDelete(sz []byte) ([]byte, error) { - if len(sz) < 1 || sz[0] != BinLogTypeDeletion { - return nil, errBinLogDeleteType - } - - return sz[1:], nil -} - -func encodeBinLogPut(key []byte, value []byte) []byte { - buf := make([]byte, 3+len(key)+len(value)) - buf[0] = BinLogTypePut - pos := 1 - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - copy(buf[pos:], key) - pos += len(key) - copy(buf[pos:], value) - - return buf -} - -func decodeBinLogPut(sz []byte) ([]byte, []byte, error) { - if len(sz) < 3 || sz[0] != BinLogTypePut { - return nil, nil, errBinLogPutType - } - - keyLen := int(binary.BigEndian.Uint16(sz[1:])) - if 3+keyLen > len(sz) { - return nil, nil, errBinLogPutType - } - - return sz[3 : 3+keyLen], sz[3+keyLen:], nil -} - -func FormatBinLogEvent(event []byte) (string, error) { - logType := uint8(event[0]) - - var err error - var k []byte - var v []byte - - var buf []byte = make([]byte, 0, 1024) - - switch logType { - case BinLogTypePut: - k, v, err = decodeBinLogPut(event) - buf = append(buf, "PUT "...) - case BinLogTypeDeletion: - k, err = decodeBinLogDelete(event) - buf = append(buf, "DELETE "...) - default: - err = errInvalidBinLogEvent - } - - if err != nil { - return "", err - } - - if buf, err = formatDataKey(buf, k); err != nil { - return "", err - } - - if v != nil && len(v) != 0 { - buf = append(buf, fmt.Sprintf(" %q", v)...) - } - - return String(buf), nil -} - -func formatDataKey(buf []byte, k []byte) ([]byte, error) { - if len(k) < 2 { - return nil, errInvalidBinLogEvent - } - - buf = append(buf, fmt.Sprintf("DB:%2d ", k[0])...) - buf = append(buf, fmt.Sprintf("%s ", TypeName[k[1]])...) - - db := new(DB) - db.index = k[0] - - //to do format at respective place - - switch k[1] { - case KVType: - if key, err := db.decodeKVKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case HashType: - if key, field, err := db.hDecodeHashKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(field)) - } - case HSizeType: - if key, err := db.hDecodeSizeKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case ListType: - if key, seq, err := db.lDecodeListKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendInt(buf, int64(seq), 10) - } - case LMetaType: - if key, err := db.lDecodeMetaKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case ZSetType: - if key, m, err := db.zDecodeSetKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(m)) - } - case ZSizeType: - if key, err := db.zDecodeSizeKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case ZScoreType: - if key, m, score, err := db.zDecodeScoreKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(m)) - buf = append(buf, ' ') - buf = strconv.AppendInt(buf, score, 10) - } - case BitType: - if key, seq, err := db.bDecodeBinKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendUint(buf, uint64(seq), 10) - } - case BitMetaType: - if key, err := db.bDecodeMetaKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case SetType: - if key, member, err := db.sDecodeSetKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(member)) - } - case SSizeType: - if key, err := db.sDecodeSizeKey(k); err != nil { - return nil, err - } else { - buf = strconv.AppendQuote(buf, String(key)) - } - case ExpTimeType: - if tp, key, t, err := db.expDecodeTimeKey(k); err != nil { - return nil, err - } else { - buf = append(buf, TypeName[tp]...) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(key)) - buf = append(buf, ' ') - buf = strconv.AppendInt(buf, t, 10) - } - case ExpMetaType: - if tp, key, err := db.expDecodeMetaKey(k); err != nil { - return nil, err - } else { - buf = append(buf, TypeName[tp]...) - buf = append(buf, ' ') - buf = strconv.AppendQuote(buf, String(key)) - } - default: - return nil, errInvalidBinLogEvent - } - - return buf, nil -} diff --git a/vendor/gitea.com/lunny/nodb/config/config.go b/vendor/gitea.com/lunny/nodb/config/config.go deleted file mode 100644 index 01ca070cc..000000000 --- a/vendor/gitea.com/lunny/nodb/config/config.go +++ /dev/null @@ -1,135 +0,0 @@ -package config - -import ( - "io/ioutil" - - "github.com/pelletier/go-toml" -) - -type Size int - -const ( - DefaultAddr string = "127.0.0.1:6380" - DefaultHttpAddr string = "127.0.0.1:11181" - - DefaultDBName string = "goleveldb" - - DefaultDataDir string = "./data" -) - -const ( - MaxBinLogFileSize int = 1024 * 1024 * 1024 - MaxBinLogFileNum int = 10000 - - DefaultBinLogFileSize int = MaxBinLogFileSize - DefaultBinLogFileNum int = 10 -) - -type LevelDBConfig struct { - Compression bool `toml:"compression"` - BlockSize int `toml:"block_size"` - WriteBufferSize int `toml:"write_buffer_size"` - CacheSize int `toml:"cache_size"` - MaxOpenFiles int `toml:"max_open_files"` -} - -type LMDBConfig struct { - MapSize int `toml:"map_size"` - NoSync bool `toml:"nosync"` -} - -type BinLogConfig struct { - MaxFileSize int `toml:"max_file_size"` - MaxFileNum int `toml:"max_file_num"` -} - -type Config struct { - DataDir string `toml:"data_dir"` - - DBName string `toml:"db_name"` - - LevelDB LevelDBConfig `toml:"leveldb"` - - LMDB LMDBConfig `toml:"lmdb"` - - BinLog BinLogConfig `toml:"binlog"` - - SlaveOf string `toml:"slaveof"` - - AccessLog string `toml:"access_log"` -} - -func NewConfigWithFile(fileName string) (*Config, error) { - data, err := ioutil.ReadFile(fileName) - if err != nil { - return nil, err - } - - return NewConfigWithData(data) -} - -func NewConfigWithData(data []byte) (*Config, error) { - cfg := NewConfigDefault() - - err := toml.Unmarshal(data, cfg) - if err != nil { - return nil, err - } - - return cfg, nil -} - -func NewConfigDefault() *Config { - cfg := new(Config) - - cfg.DataDir = DefaultDataDir - - cfg.DBName = DefaultDBName - - // disable binlog - cfg.BinLog.MaxFileNum = 0 - cfg.BinLog.MaxFileSize = 0 - - // disable replication - cfg.SlaveOf = "" - - // disable access log - cfg.AccessLog = "" - - cfg.LMDB.MapSize = 20 * 1024 * 1024 - cfg.LMDB.NoSync = true - - return cfg -} - -func (cfg *LevelDBConfig) Adjust() { - if cfg.CacheSize <= 0 { - cfg.CacheSize = 4 * 1024 * 1024 - } - - if cfg.BlockSize <= 0 { - cfg.BlockSize = 4 * 1024 - } - - if cfg.WriteBufferSize <= 0 { - cfg.WriteBufferSize = 4 * 1024 * 1024 - } - - if cfg.MaxOpenFiles < 1024 { - cfg.MaxOpenFiles = 1024 - } -} - -func (cfg *BinLogConfig) Adjust() { - if cfg.MaxFileSize <= 0 { - cfg.MaxFileSize = DefaultBinLogFileSize - } else if cfg.MaxFileSize > MaxBinLogFileSize { - cfg.MaxFileSize = MaxBinLogFileSize - } - - if cfg.MaxFileNum <= 0 { - cfg.MaxFileNum = DefaultBinLogFileNum - } else if cfg.MaxFileNum > MaxBinLogFileNum { - cfg.MaxFileNum = MaxBinLogFileNum - } -} diff --git a/vendor/gitea.com/lunny/nodb/config/config.toml b/vendor/gitea.com/lunny/nodb/config/config.toml deleted file mode 100644 index 2a3a2466e..000000000 --- a/vendor/gitea.com/lunny/nodb/config/config.toml +++ /dev/null @@ -1,45 +0,0 @@ -# LedisDB configuration - -# Server listen address -addr = "127.0.0.1:6380" - -# Server http listen address, set empty to disable -http_addr = "127.0.0.1:11181" - -# Data store path, all ledisdb's data will be saved here -data_dir = "/tmp/ledis_server" - -# Log server command, set empty to disable -access_log = "" - -# Set slaveof to enable replication from master, empty, no replication -slaveof = "" - -# Choose which backend storage to use, now support: -# -# leveldb -# rocksdb -# goleveldb -# lmdb -# boltdb -# hyperleveldb -# memory -# -db_name = "leveldb" - -[leveldb] -compression = false -block_size = 32768 -write_buffer_size = 67108864 -cache_size = 524288000 -max_open_files = 1024 - -[lmdb] -map_size = 524288000 -nosync = true - -[binlog] -max_file_size = 0 -max_file_num = 0 - - diff --git a/vendor/gitea.com/lunny/nodb/const.go b/vendor/gitea.com/lunny/nodb/const.go deleted file mode 100644 index 446dae634..000000000 --- a/vendor/gitea.com/lunny/nodb/const.go +++ /dev/null @@ -1,98 +0,0 @@ -package nodb - -import ( - "errors" -) - -const ( - NoneType byte = 0 - KVType byte = 1 - HashType byte = 2 - HSizeType byte = 3 - ListType byte = 4 - LMetaType byte = 5 - ZSetType byte = 6 - ZSizeType byte = 7 - ZScoreType byte = 8 - BitType byte = 9 - BitMetaType byte = 10 - SetType byte = 11 - SSizeType byte = 12 - - maxDataType byte = 100 - - ExpTimeType byte = 101 - ExpMetaType byte = 102 -) - -var ( - TypeName = map[byte]string{ - KVType: "kv", - HashType: "hash", - HSizeType: "hsize", - ListType: "list", - LMetaType: "lmeta", - ZSetType: "zset", - ZSizeType: "zsize", - ZScoreType: "zscore", - BitType: "bit", - BitMetaType: "bitmeta", - SetType: "set", - SSizeType: "ssize", - ExpTimeType: "exptime", - ExpMetaType: "expmeta", - } -) - -const ( - defaultScanCount int = 10 -) - -var ( - errKeySize = errors.New("invalid key size") - errValueSize = errors.New("invalid value size") - errHashFieldSize = errors.New("invalid hash field size") - errSetMemberSize = errors.New("invalid set member size") - errZSetMemberSize = errors.New("invalid zset member size") - errExpireValue = errors.New("invalid expire value") -) - -const ( - //we don't support too many databases - MaxDBNumber uint8 = 16 - - //max key size - MaxKeySize int = 1024 - - //max hash field size - MaxHashFieldSize int = 1024 - - //max zset member size - MaxZSetMemberSize int = 1024 - - //max set member size - MaxSetMemberSize int = 1024 - - //max value size - MaxValueSize int = 10 * 1024 * 1024 -) - -var ( - ErrScoreMiss = errors.New("zset score miss") -) - -const ( - BinLogTypeDeletion uint8 = 0x0 - BinLogTypePut uint8 = 0x1 - BinLogTypeCommand uint8 = 0x2 -) - -const ( - DBAutoCommit uint8 = 0x0 - DBInTransaction uint8 = 0x1 - DBInMulti uint8 = 0x2 -) - -var ( - Version = "0.1" -) diff --git a/vendor/gitea.com/lunny/nodb/doc.go b/vendor/gitea.com/lunny/nodb/doc.go deleted file mode 100644 index 2f7df33ff..000000000 --- a/vendor/gitea.com/lunny/nodb/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// package nodb is a high performance embedded NoSQL. -// -// nodb supports various data structure like kv, list, hash and zset like redis. -// -// Other features include binlog replication, data with a limited time-to-live. -// -// Usage -// -// First create a nodb instance before use: -// -// l := nodb.Open(cfg) -// -// cfg is a Config instance which contains configuration for nodb use, -// like DataDir (root directory for nodb working to store data). -// -// After you create a nodb instance, you can select a DB to store you data: -// -// db, _ := l.Select(0) -// -// DB must be selected by a index, nodb supports only 16 databases, so the index range is [0-15]. -// -// KV -// -// KV is the most basic nodb type like any other key-value database. -// -// err := db.Set(key, value) -// value, err := db.Get(key) -// -// List -// -// List is simply lists of values, sorted by insertion order. -// You can push or pop value on the list head (left) or tail (right). -// -// err := db.LPush(key, value1) -// err := db.RPush(key, value2) -// value1, err := db.LPop(key) -// value2, err := db.RPop(key) -// -// Hash -// -// Hash is a map between fields and values. -// -// n, err := db.HSet(key, field1, value1) -// n, err := db.HSet(key, field2, value2) -// value1, err := db.HGet(key, field1) -// value2, err := db.HGet(key, field2) -// -// ZSet -// -// ZSet is a sorted collections of values. -// Every member of zset is associated with score, a int64 value which used to sort, from smallest to greatest score. -// Members are unique, but score may be same. -// -// n, err := db.ZAdd(key, ScorePair{score1, member1}, ScorePair{score2, member2}) -// ay, err := db.ZRangeByScore(key, minScore, maxScore, 0, -1) -// -// Binlog -// -// nodb supports binlog, so you can sync binlog to another server for replication. If you want to open binlog support, set UseBinLog to true in config. -// -package nodb diff --git a/vendor/gitea.com/lunny/nodb/dump.go b/vendor/gitea.com/lunny/nodb/dump.go deleted file mode 100644 index 3c9722e00..000000000 --- a/vendor/gitea.com/lunny/nodb/dump.go +++ /dev/null @@ -1,200 +0,0 @@ -package nodb - -import ( - "bufio" - "bytes" - "encoding/binary" - "io" - "os" - - "github.com/siddontang/go-snappy/snappy" -) - -//dump format -// fileIndex(bigendian int64)|filePos(bigendian int64) -// |keylen(bigendian int32)|key|valuelen(bigendian int32)|value...... -// -//key and value are both compressed for fast transfer dump on network using snappy - -type BinLogAnchor struct { - LogFileIndex int64 - LogPos int64 -} - -func (m *BinLogAnchor) WriteTo(w io.Writer) error { - if err := binary.Write(w, binary.BigEndian, m.LogFileIndex); err != nil { - return err - } - - if err := binary.Write(w, binary.BigEndian, m.LogPos); err != nil { - return err - } - return nil -} - -func (m *BinLogAnchor) ReadFrom(r io.Reader) error { - err := binary.Read(r, binary.BigEndian, &m.LogFileIndex) - if err != nil { - return err - } - - err = binary.Read(r, binary.BigEndian, &m.LogPos) - if err != nil { - return err - } - - return nil -} - -func (l *Nodb) DumpFile(path string) error { - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - - return l.Dump(f) -} - -func (l *Nodb) Dump(w io.Writer) error { - m := new(BinLogAnchor) - - var err error - - l.wLock.Lock() - defer l.wLock.Unlock() - - if l.binlog != nil { - m.LogFileIndex = l.binlog.LogFileIndex() - m.LogPos = l.binlog.LogFilePos() - } - - wb := bufio.NewWriterSize(w, 4096) - if err = m.WriteTo(wb); err != nil { - return err - } - - it := l.ldb.NewIterator() - it.SeekToFirst() - - compressBuf := make([]byte, 4096) - - var key []byte - var value []byte - for ; it.Valid(); it.Next() { - key = it.RawKey() - value = it.RawValue() - - if key, err = snappy.Encode(compressBuf, key); err != nil { - return err - } - - if err = binary.Write(wb, binary.BigEndian, uint16(len(key))); err != nil { - return err - } - - if _, err = wb.Write(key); err != nil { - return err - } - - if value, err = snappy.Encode(compressBuf, value); err != nil { - return err - } - - if err = binary.Write(wb, binary.BigEndian, uint32(len(value))); err != nil { - return err - } - - if _, err = wb.Write(value); err != nil { - return err - } - } - - if err = wb.Flush(); err != nil { - return err - } - - compressBuf = nil - - return nil -} - -func (l *Nodb) LoadDumpFile(path string) (*BinLogAnchor, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - return l.LoadDump(f) -} - -func (l *Nodb) LoadDump(r io.Reader) (*BinLogAnchor, error) { - l.wLock.Lock() - defer l.wLock.Unlock() - - info := new(BinLogAnchor) - - rb := bufio.NewReaderSize(r, 4096) - - err := info.ReadFrom(rb) - if err != nil { - return nil, err - } - - var keyLen uint16 - var valueLen uint32 - - var keyBuf bytes.Buffer - var valueBuf bytes.Buffer - - deKeyBuf := make([]byte, 4096) - deValueBuf := make([]byte, 4096) - - var key, value []byte - - for { - if err = binary.Read(rb, binary.BigEndian, &keyLen); err != nil && err != io.EOF { - return nil, err - } else if err == io.EOF { - break - } - - if _, err = io.CopyN(&keyBuf, rb, int64(keyLen)); err != nil { - return nil, err - } - - if key, err = snappy.Decode(deKeyBuf, keyBuf.Bytes()); err != nil { - return nil, err - } - - if err = binary.Read(rb, binary.BigEndian, &valueLen); err != nil { - return nil, err - } - - if _, err = io.CopyN(&valueBuf, rb, int64(valueLen)); err != nil { - return nil, err - } - - if value, err = snappy.Decode(deValueBuf, valueBuf.Bytes()); err != nil { - return nil, err - } - - if err = l.ldb.Put(key, value); err != nil { - return nil, err - } - - keyBuf.Reset() - valueBuf.Reset() - } - - deKeyBuf = nil - deValueBuf = nil - - //if binlog enable, we will delete all binlogs and open a new one for handling simply - if l.binlog != nil { - l.binlog.PurgeAll() - } - - return info, nil -} diff --git a/vendor/gitea.com/lunny/nodb/go.mod b/vendor/gitea.com/lunny/nodb/go.mod deleted file mode 100644 index ae0ed81f3..000000000 --- a/vendor/gitea.com/lunny/nodb/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module gitea.com/lunny/nodb - -go 1.12 - -require ( - gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e - github.com/mattn/go-sqlite3 v1.11.0 // indirect - github.com/pelletier/go-toml v1.8.1 - github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d - github.com/syndtr/goleveldb v1.0.0 -) diff --git a/vendor/gitea.com/lunny/nodb/go.sum b/vendor/gitea.com/lunny/nodb/go.sum deleted file mode 100644 index 91ae7d165..000000000 --- a/vendor/gitea.com/lunny/nodb/go.sum +++ /dev/null @@ -1,42 +0,0 @@ -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= -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/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/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -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.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -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/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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/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 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gitea.com/lunny/nodb/info.go b/vendor/gitea.com/lunny/nodb/info.go deleted file mode 100644 index 3fd37e3d4..000000000 --- a/vendor/gitea.com/lunny/nodb/info.go +++ /dev/null @@ -1,24 +0,0 @@ -package nodb - -// todo, add info - -// type Keyspace struct { -// Kvs int `json:"kvs"` -// KvExpires int `json:"kv_expires"` - -// Lists int `json:"lists"` -// ListExpires int `json:"list_expires"` - -// Bitmaps int `json:"bitmaps"` -// BitmapExpires int `json:"bitmap_expires"` - -// ZSets int `json:"zsets"` -// ZSetExpires int `json:"zset_expires"` - -// Hashes int `json:"hashes"` -// HashExpires int `json:"hahsh_expires"` -// } - -// type Info struct { -// KeySpaces [MaxDBNumber]Keyspace -// } diff --git a/vendor/gitea.com/lunny/nodb/multi.go b/vendor/gitea.com/lunny/nodb/multi.go deleted file mode 100644 index ca581ce9a..000000000 --- a/vendor/gitea.com/lunny/nodb/multi.go +++ /dev/null @@ -1,73 +0,0 @@ -package nodb - -import ( - "errors" - "fmt" -) - -var ( - ErrNestMulti = errors.New("nest multi not supported") - ErrMultiDone = errors.New("multi has been closed") -) - -type Multi struct { - *DB -} - -func (db *DB) IsInMulti() bool { - return db.status == DBInMulti -} - -// begin a mutli to execute commands, -// it will block any other write operations before you close the multi, unlike transaction, mutli can not rollback -func (db *DB) Multi() (*Multi, error) { - if db.IsInMulti() { - return nil, ErrNestMulti - } - - m := new(Multi) - - m.DB = new(DB) - m.DB.status = DBInMulti - - m.DB.l = db.l - - m.l.wLock.Lock() - - m.DB.sdb = db.sdb - - m.DB.bucket = db.sdb - - m.DB.index = db.index - - m.DB.kvBatch = m.newBatch() - m.DB.listBatch = m.newBatch() - m.DB.hashBatch = m.newBatch() - m.DB.zsetBatch = m.newBatch() - m.DB.binBatch = m.newBatch() - m.DB.setBatch = m.newBatch() - - return m, nil -} - -func (m *Multi) newBatch() *batch { - return m.l.newBatch(m.bucket.NewWriteBatch(), &multiBatchLocker{}, nil) -} - -func (m *Multi) Close() error { - if m.bucket == nil { - return ErrMultiDone - } - m.l.wLock.Unlock() - m.bucket = nil - return nil -} - -func (m *Multi) Select(index int) error { - if index < 0 || index >= int(MaxDBNumber) { - return fmt.Errorf("invalid db index %d", index) - } - - m.DB.index = uint8(index) - return nil -} diff --git a/vendor/gitea.com/lunny/nodb/nodb.go b/vendor/gitea.com/lunny/nodb/nodb.go deleted file mode 100644 index b48757941..000000000 --- a/vendor/gitea.com/lunny/nodb/nodb.go +++ /dev/null @@ -1,128 +0,0 @@ -package nodb - -import ( - "fmt" - "sync" - "time" - - "gitea.com/lunny/nodb/config" - "gitea.com/lunny/nodb/store" - "gitea.com/lunny/log" -) - -type Nodb struct { - cfg *config.Config - - ldb *store.DB - dbs [MaxDBNumber]*DB - - quit chan struct{} - jobs *sync.WaitGroup - - binlog *BinLog - - wLock sync.RWMutex //allow one write at same time - commitLock sync.Mutex //allow one write commit at same time -} - -func Open(cfg *config.Config) (*Nodb, error) { - if len(cfg.DataDir) == 0 { - cfg.DataDir = config.DefaultDataDir - } - - ldb, err := store.Open(cfg) - if err != nil { - return nil, err - } - - l := new(Nodb) - - l.quit = make(chan struct{}) - l.jobs = new(sync.WaitGroup) - - l.ldb = ldb - - if cfg.BinLog.MaxFileNum > 0 && cfg.BinLog.MaxFileSize > 0 { - l.binlog, err = NewBinLog(cfg) - if err != nil { - return nil, err - } - } else { - l.binlog = nil - } - - for i := uint8(0); i < MaxDBNumber; i++ { - l.dbs[i] = l.newDB(i) - } - - l.activeExpireCycle() - - return l, nil -} - -func (l *Nodb) Close() { - close(l.quit) - l.jobs.Wait() - - l.ldb.Close() - - if l.binlog != nil { - l.binlog.Close() - l.binlog = nil - } -} - -func (l *Nodb) Select(index int) (*DB, error) { - if index < 0 || index >= int(MaxDBNumber) { - return nil, fmt.Errorf("invalid db index %d", index) - } - - return l.dbs[index], nil -} - -func (l *Nodb) FlushAll() error { - for index, db := range l.dbs { - if _, err := db.FlushAll(); err != nil { - log.Errorf("flush db %d error %s", index, err.Error()) - } - } - - return nil -} - -// very dangerous to use -func (l *Nodb) DataDB() *store.DB { - return l.ldb -} - -func (l *Nodb) activeExpireCycle() { - var executors []*elimination = make([]*elimination, len(l.dbs)) - for i, db := range l.dbs { - executors[i] = db.newEliminator() - } - - l.jobs.Add(1) - go func() { - tick := time.NewTicker(1 * time.Second) - end := false - done := make(chan struct{}) - for !end { - select { - case <-tick.C: - go func() { - for _, eli := range executors { - eli.active() - } - done <- struct{}{} - }() - <-done - case <-l.quit: - end = true - break - } - } - - tick.Stop() - l.jobs.Done() - }() -} diff --git a/vendor/gitea.com/lunny/nodb/nodb_db.go b/vendor/gitea.com/lunny/nodb/nodb_db.go deleted file mode 100644 index 76922be15..000000000 --- a/vendor/gitea.com/lunny/nodb/nodb_db.go +++ /dev/null @@ -1,171 +0,0 @@ -package nodb - -import ( - "fmt" - "sync" - - "gitea.com/lunny/nodb/store" -) - -type ibucket interface { - Get(key []byte) ([]byte, error) - - Put(key []byte, value []byte) error - Delete(key []byte) error - - NewIterator() *store.Iterator - - NewWriteBatch() store.WriteBatch - - RangeIterator(min []byte, max []byte, rangeType uint8) *store.RangeLimitIterator - RevRangeIterator(min []byte, max []byte, rangeType uint8) *store.RangeLimitIterator - RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *store.RangeLimitIterator - RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *store.RangeLimitIterator -} - -type DB struct { - l *Nodb - - sdb *store.DB - - bucket ibucket - - index uint8 - - kvBatch *batch - listBatch *batch - hashBatch *batch - zsetBatch *batch - binBatch *batch - setBatch *batch - - status uint8 -} - -func (l *Nodb) newDB(index uint8) *DB { - d := new(DB) - - d.l = l - - d.sdb = l.ldb - - d.bucket = d.sdb - - d.status = DBAutoCommit - d.index = index - - d.kvBatch = d.newBatch() - d.listBatch = d.newBatch() - d.hashBatch = d.newBatch() - d.zsetBatch = d.newBatch() - d.binBatch = d.newBatch() - d.setBatch = d.newBatch() - - return d -} - -func (db *DB) newBatch() *batch { - return db.l.newBatch(db.bucket.NewWriteBatch(), &dbBatchLocker{l: &sync.Mutex{}, wrLock: &db.l.wLock}, nil) -} - -func (db *DB) Index() int { - return int(db.index) -} - -func (db *DB) IsAutoCommit() bool { - return db.status == DBAutoCommit -} - -func (db *DB) FlushAll() (drop int64, err error) { - all := [...](func() (int64, error)){ - db.flush, - db.lFlush, - db.hFlush, - db.zFlush, - db.bFlush, - db.sFlush} - - for _, flush := range all { - if n, e := flush(); e != nil { - err = e - return - } else { - drop += n - } - } - - return -} - -func (db *DB) newEliminator() *elimination { - eliminator := newEliminator(db) - - eliminator.regRetireContext(KVType, db.kvBatch, db.delete) - eliminator.regRetireContext(ListType, db.listBatch, db.lDelete) - eliminator.regRetireContext(HashType, db.hashBatch, db.hDelete) - eliminator.regRetireContext(ZSetType, db.zsetBatch, db.zDelete) - eliminator.regRetireContext(BitType, db.binBatch, db.bDelete) - eliminator.regRetireContext(SetType, db.setBatch, db.sDelete) - - return eliminator -} - -func (db *DB) flushRegion(t *batch, minKey []byte, maxKey []byte) (drop int64, err error) { - it := db.bucket.RangeIterator(minKey, maxKey, store.RangeROpen) - for ; it.Valid(); it.Next() { - t.Delete(it.RawKey()) - drop++ - if drop&1023 == 0 { - if err = t.Commit(); err != nil { - return - } - } - } - it.Close() - return -} - -func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) { - var deleteFunc func(t *batch, key []byte) int64 - var metaDataType byte - switch dataType { - case KVType: - deleteFunc = db.delete - metaDataType = KVType - case ListType: - deleteFunc = db.lDelete - metaDataType = LMetaType - case HashType: - deleteFunc = db.hDelete - metaDataType = HSizeType - case ZSetType: - deleteFunc = db.zDelete - metaDataType = ZSizeType - case BitType: - deleteFunc = db.bDelete - metaDataType = BitMetaType - case SetType: - deleteFunc = db.sDelete - metaDataType = SSizeType - default: - return 0, fmt.Errorf("invalid data type: %s", TypeName[dataType]) - } - - var keys [][]byte - keys, err = db.scan(metaDataType, nil, 1024, false, "") - for len(keys) != 0 || err != nil { - for _, key := range keys { - deleteFunc(t, key) - db.rmExpire(t, dataType, key) - - } - - if err = t.Commit(); err != nil { - return - } else { - drop += int64(len(keys)) - } - keys, err = db.scan(metaDataType, nil, 1024, false, "") - } - return -} diff --git a/vendor/gitea.com/lunny/nodb/replication.go b/vendor/gitea.com/lunny/nodb/replication.go deleted file mode 100644 index c2153f428..000000000 --- a/vendor/gitea.com/lunny/nodb/replication.go +++ /dev/null @@ -1,312 +0,0 @@ -package nodb - -import ( - "bufio" - "bytes" - "errors" - "io" - "os" - "time" - - "gitea.com/lunny/log" - "gitea.com/lunny/nodb/store/driver" -) - -const ( - maxReplBatchNum = 100 - maxReplLogSize = 1 * 1024 * 1024 -) - -var ( - ErrSkipEvent = errors.New("skip to next event") -) - -var ( - errInvalidBinLogEvent = errors.New("invalid binglog event") - errInvalidBinLogFile = errors.New("invalid binlog file") -) - -type replBatch struct { - wb driver.IWriteBatch - events [][]byte - l *Nodb - - lastHead *BinLogHead -} - -func (b *replBatch) Commit() error { - b.l.commitLock.Lock() - defer b.l.commitLock.Unlock() - - err := b.wb.Commit() - if err != nil { - b.Rollback() - return err - } - - if b.l.binlog != nil { - if err = b.l.binlog.Log(b.events...); err != nil { - b.Rollback() - return err - } - } - - b.events = [][]byte{} - b.lastHead = nil - - return nil -} - -func (b *replBatch) Rollback() error { - b.wb.Rollback() - b.events = [][]byte{} - b.lastHead = nil - return nil -} - -func (l *Nodb) replicateEvent(b *replBatch, event []byte) error { - if len(event) == 0 { - return errInvalidBinLogEvent - } - - b.events = append(b.events, event) - - logType := uint8(event[0]) - switch logType { - case BinLogTypePut: - return l.replicatePutEvent(b, event) - case BinLogTypeDeletion: - return l.replicateDeleteEvent(b, event) - default: - return errInvalidBinLogEvent - } -} - -func (l *Nodb) replicatePutEvent(b *replBatch, event []byte) error { - key, value, err := decodeBinLogPut(event) - if err != nil { - return err - } - - b.wb.Put(key, value) - - return nil -} - -func (l *Nodb) replicateDeleteEvent(b *replBatch, event []byte) error { - key, err := decodeBinLogDelete(event) - if err != nil { - return err - } - - b.wb.Delete(key) - - return nil -} - -func ReadEventFromReader(rb io.Reader, f func(head *BinLogHead, event []byte) error) error { - head := &BinLogHead{} - var err error - - for { - if err = head.Read(rb); err != nil { - if err == io.EOF { - break - } else { - return err - } - } - - var dataBuf bytes.Buffer - - if _, err = io.CopyN(&dataBuf, rb, int64(head.PayloadLen)); err != nil { - return err - } - - err = f(head, dataBuf.Bytes()) - if err != nil && err != ErrSkipEvent { - return err - } - } - - return nil -} - -func (l *Nodb) ReplicateFromReader(rb io.Reader) error { - b := new(replBatch) - - b.wb = l.ldb.NewWriteBatch() - b.l = l - - f := func(head *BinLogHead, event []byte) error { - if b.lastHead == nil { - b.lastHead = head - } else if !b.lastHead.InSameBatch(head) { - if err := b.Commit(); err != nil { - log.Fatalf("replication error %s, skip to next", err.Error()) - return ErrSkipEvent - } - b.lastHead = head - } - - err := l.replicateEvent(b, event) - if err != nil { - log.Fatalf("replication error %s, skip to next", err.Error()) - return ErrSkipEvent - } - return nil - } - - err := ReadEventFromReader(rb, f) - if err != nil { - b.Rollback() - return err - } - return b.Commit() -} - -func (l *Nodb) ReplicateFromData(data []byte) error { - rb := bytes.NewReader(data) - - err := l.ReplicateFromReader(rb) - - return err -} - -func (l *Nodb) ReplicateFromBinLog(filePath string) error { - f, err := os.Open(filePath) - if err != nil { - return err - } - - rb := bufio.NewReaderSize(f, 4096) - - err = l.ReplicateFromReader(rb) - - f.Close() - - return err -} - -// try to read events, if no events read, try to wait the new event singal until timeout seconds -func (l *Nodb) ReadEventsToTimeout(info *BinLogAnchor, w io.Writer, timeout int) (n int, err error) { - lastIndex := info.LogFileIndex - lastPos := info.LogPos - - n = 0 - if l.binlog == nil { - //binlog not supported - info.LogFileIndex = 0 - info.LogPos = 0 - return - } - - n, err = l.ReadEventsTo(info, w) - if err == nil && info.LogFileIndex == lastIndex && info.LogPos == lastPos { - //no events read - select { - case <-l.binlog.Wait(): - case <-time.After(time.Duration(timeout) * time.Second): - } - return l.ReadEventsTo(info, w) - } - return -} - -func (l *Nodb) ReadEventsTo(info *BinLogAnchor, w io.Writer) (n int, err error) { - n = 0 - if l.binlog == nil { - //binlog not supported - info.LogFileIndex = 0 - info.LogPos = 0 - return - } - - index := info.LogFileIndex - offset := info.LogPos - - filePath := l.binlog.FormatLogFilePath(index) - - var f *os.File - f, err = os.Open(filePath) - if os.IsNotExist(err) { - lastIndex := l.binlog.LogFileIndex() - - if index == lastIndex { - //no binlog at all - info.LogPos = 0 - } else { - //slave binlog info had lost - info.LogFileIndex = -1 - } - } - - if err != nil { - if os.IsNotExist(err) { - err = nil - } - return - } - - defer f.Close() - - var fileSize int64 - st, _ := f.Stat() - fileSize = st.Size() - - if fileSize == info.LogPos { - return - } - - if _, err = f.Seek(offset, os.SEEK_SET); err != nil { - //may be invliad seek offset - return - } - - var lastHead *BinLogHead = nil - - head := &BinLogHead{} - - batchNum := 0 - - for { - if err = head.Read(f); err != nil { - if err == io.EOF { - //we will try to use next binlog - if index < l.binlog.LogFileIndex() { - info.LogFileIndex += 1 - info.LogPos = 0 - } - err = nil - return - } else { - return - } - - } - - if lastHead == nil { - lastHead = head - batchNum++ - } else if !lastHead.InSameBatch(head) { - lastHead = head - batchNum++ - if batchNum > maxReplBatchNum || n > maxReplLogSize { - return - } - } - - if err = head.Write(w); err != nil { - return - } - - if _, err = io.CopyN(w, f, int64(head.PayloadLen)); err != nil { - return - } - - n += (head.Len() + int(head.PayloadLen)) - info.LogPos = info.LogPos + int64(head.Len()) + int64(head.PayloadLen) - } - - return -} diff --git a/vendor/gitea.com/lunny/nodb/scan.go b/vendor/gitea.com/lunny/nodb/scan.go deleted file mode 100644 index fc0bdfb11..000000000 --- a/vendor/gitea.com/lunny/nodb/scan.go +++ /dev/null @@ -1,144 +0,0 @@ -package nodb - -import ( - "bytes" - "errors" - "regexp" - - "gitea.com/lunny/nodb/store" -) - -var errDataType = errors.New("error data type") -var errMetaKey = errors.New("error meta key") - -// Seek search the prefix key -func (db *DB) Seek(key []byte) (*store.Iterator, error) { - return db.seek(KVType, key) -} - -func (db *DB) seek(dataType byte, key []byte) (*store.Iterator, error) { - var minKey []byte - var err error - - if len(key) > 0 { - if err = checkKeySize(key); err != nil { - return nil, err - } - if minKey, err = db.encodeMetaKey(dataType, key); err != nil { - return nil, err - } - - } else { - if minKey, err = db.encodeMinKey(dataType); err != nil { - return nil, err - } - } - - it := db.bucket.NewIterator() - it.Seek(minKey) - return it, nil -} - -func (db *DB) MaxKey() ([]byte, error) { - return db.encodeMaxKey(KVType) -} - -func (db *DB) Key(it *store.Iterator) ([]byte, error) { - return db.decodeMetaKey(KVType, it.Key()) -} - -func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) { - var minKey, maxKey []byte - var err error - var r *regexp.Regexp - - if len(match) > 0 { - if r, err = regexp.Compile(match); err != nil { - return nil, err - } - } - - if len(key) > 0 { - if err = checkKeySize(key); err != nil { - return nil, err - } - if minKey, err = db.encodeMetaKey(dataType, key); err != nil { - return nil, err - } - - } else { - if minKey, err = db.encodeMinKey(dataType); err != nil { - return nil, err - } - } - - if maxKey, err = db.encodeMaxKey(dataType); err != nil { - return nil, err - } - - if count <= 0 { - count = defaultScanCount - } - - v := make([][]byte, 0, count) - - it := db.bucket.NewIterator() - it.Seek(minKey) - - if !inclusive { - if it.Valid() && bytes.Equal(it.RawKey(), minKey) { - it.Next() - } - } - - for i := 0; it.Valid() && i < count && bytes.Compare(it.RawKey(), maxKey) < 0; it.Next() { - if k, err := db.decodeMetaKey(dataType, it.Key()); err != nil { - continue - } else if r != nil && !r.Match(k) { - continue - } else { - v = append(v, k) - i++ - } - } - it.Close() - return v, nil -} - -func (db *DB) encodeMinKey(dataType byte) ([]byte, error) { - return db.encodeMetaKey(dataType, nil) -} - -func (db *DB) encodeMaxKey(dataType byte) ([]byte, error) { - k, err := db.encodeMetaKey(dataType, nil) - if err != nil { - return nil, err - } - k[len(k)-1] = dataType + 1 - return k, nil -} - -func (db *DB) encodeMetaKey(dataType byte, key []byte) ([]byte, error) { - switch dataType { - case KVType: - return db.encodeKVKey(key), nil - case LMetaType: - return db.lEncodeMetaKey(key), nil - case HSizeType: - return db.hEncodeSizeKey(key), nil - case ZSizeType: - return db.zEncodeSizeKey(key), nil - case BitMetaType: - return db.bEncodeMetaKey(key), nil - case SSizeType: - return db.sEncodeSizeKey(key), nil - default: - return nil, errDataType - } -} -func (db *DB) decodeMetaKey(dataType byte, ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != dataType { - return nil, errMetaKey - } - return ek[2:], nil -} diff --git a/vendor/gitea.com/lunny/nodb/store/db.go b/vendor/gitea.com/lunny/nodb/store/db.go deleted file mode 100644 index fb0cf2dec..000000000 --- a/vendor/gitea.com/lunny/nodb/store/db.go +++ /dev/null @@ -1,61 +0,0 @@ -package store - -import ( - "gitea.com/lunny/nodb/store/driver" -) - -type DB struct { - driver.IDB -} - -func (db *DB) NewIterator() *Iterator { - it := new(Iterator) - it.it = db.IDB.NewIterator() - - return it -} - -func (db *DB) NewWriteBatch() WriteBatch { - return db.IDB.NewWriteBatch() -} - -func (db *DB) NewSnapshot() (*Snapshot, error) { - var err error - s := &Snapshot{} - if s.ISnapshot, err = db.IDB.NewSnapshot(); err != nil { - return nil, err - } - - return s, nil -} - -func (db *DB) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator { - return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1}) -} - -func (db *DB) RevRangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator { - return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1}) -} - -//count < 0, unlimit. -// -//offset must >= 0, if < 0, will get nothing. -func (db *DB) RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator { - return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count}) -} - -//count < 0, unlimit. -// -//offset must >= 0, if < 0, will get nothing. -func (db *DB) RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator { - return NewRevRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count}) -} - -func (db *DB) Begin() (*Tx, error) { - tx, err := db.IDB.Begin() - if err != nil { - return nil, err - } - - return &Tx{tx}, nil -} diff --git a/vendor/gitea.com/lunny/nodb/store/driver/batch.go b/vendor/gitea.com/lunny/nodb/store/driver/batch.go deleted file mode 100644 index 6b79c21c4..000000000 --- a/vendor/gitea.com/lunny/nodb/store/driver/batch.go +++ /dev/null @@ -1,39 +0,0 @@ -package driver - -type BatchPuter interface { - BatchPut([]Write) error -} - -type Write struct { - Key []byte - Value []byte -} - -type WriteBatch struct { - batch BatchPuter - wb []Write -} - -func (w *WriteBatch) Put(key, value []byte) { - if value == nil { - value = []byte{} - } - w.wb = append(w.wb, Write{key, value}) -} - -func (w *WriteBatch) Delete(key []byte) { - w.wb = append(w.wb, Write{key, nil}) -} - -func (w *WriteBatch) Commit() error { - return w.batch.BatchPut(w.wb) -} - -func (w *WriteBatch) Rollback() error { - w.wb = w.wb[0:0] - return nil -} - -func NewWriteBatch(puter BatchPuter) IWriteBatch { - return &WriteBatch{puter, []Write{}} -} diff --git a/vendor/gitea.com/lunny/nodb/store/driver/driver.go b/vendor/gitea.com/lunny/nodb/store/driver/driver.go deleted file mode 100644 index 6da67df08..000000000 --- a/vendor/gitea.com/lunny/nodb/store/driver/driver.go +++ /dev/null @@ -1,67 +0,0 @@ -package driver - -import ( - "errors" -) - -var ( - ErrTxSupport = errors.New("transaction is not supported") -) - -type IDB interface { - Close() error - - Get(key []byte) ([]byte, error) - - Put(key []byte, value []byte) error - Delete(key []byte) error - - NewIterator() IIterator - - NewWriteBatch() IWriteBatch - - NewSnapshot() (ISnapshot, error) - - Begin() (Tx, error) -} - -type ISnapshot interface { - Get(key []byte) ([]byte, error) - NewIterator() IIterator - Close() -} - -type IIterator interface { - Close() error - - First() - Last() - Seek(key []byte) - - Next() - Prev() - - Valid() bool - - Key() []byte - Value() []byte -} - -type IWriteBatch interface { - Put(key []byte, value []byte) - Delete(key []byte) - Commit() error - Rollback() error -} - -type Tx interface { - Get(key []byte) ([]byte, error) - Put(key []byte, value []byte) error - Delete(key []byte) error - - NewIterator() IIterator - NewWriteBatch() IWriteBatch - - Commit() error - Rollback() error -} diff --git a/vendor/gitea.com/lunny/nodb/store/driver/store.go b/vendor/gitea.com/lunny/nodb/store/driver/store.go deleted file mode 100644 index a64d22615..000000000 --- a/vendor/gitea.com/lunny/nodb/store/driver/store.go +++ /dev/null @@ -1,46 +0,0 @@ -package driver - -import ( - "fmt" - - "gitea.com/lunny/nodb/config" -) - -type Store interface { - String() string - Open(path string, cfg *config.Config) (IDB, error) - Repair(path string, cfg *config.Config) error -} - -var dbs = map[string]Store{} - -func Register(s Store) { - name := s.String() - if _, ok := dbs[name]; ok { - panic(fmt.Errorf("store %s is registered", s)) - } - - dbs[name] = s -} - -func ListStores() []string { - s := []string{} - for k, _ := range dbs { - s = append(s, k) - } - - return s -} - -func GetStore(cfg *config.Config) (Store, error) { - if len(cfg.DBName) == 0 { - cfg.DBName = config.DefaultDBName - } - - s, ok := dbs[cfg.DBName] - if !ok { - return nil, fmt.Errorf("store %s is not registered", cfg.DBName) - } - - return s, nil -} diff --git a/vendor/gitea.com/lunny/nodb/store/goleveldb/batch.go b/vendor/gitea.com/lunny/nodb/store/goleveldb/batch.go deleted file mode 100644 index b17e85e75..000000000 --- a/vendor/gitea.com/lunny/nodb/store/goleveldb/batch.go +++ /dev/null @@ -1,27 +0,0 @@ -package goleveldb - -import ( - "github.com/syndtr/goleveldb/leveldb" -) - -type WriteBatch struct { - db *DB - wbatch *leveldb.Batch -} - -func (w *WriteBatch) Put(key, value []byte) { - w.wbatch.Put(key, value) -} - -func (w *WriteBatch) Delete(key []byte) { - w.wbatch.Delete(key) -} - -func (w *WriteBatch) Commit() error { - return w.db.db.Write(w.wbatch, nil) -} - -func (w *WriteBatch) Rollback() error { - w.wbatch.Reset() - return nil -} diff --git a/vendor/gitea.com/lunny/nodb/store/goleveldb/const.go b/vendor/gitea.com/lunny/nodb/store/goleveldb/const.go deleted file mode 100644 index 2fffa7c82..000000000 --- a/vendor/gitea.com/lunny/nodb/store/goleveldb/const.go +++ /dev/null @@ -1,4 +0,0 @@ -package goleveldb - -const DBName = "goleveldb" -const MemDBName = "memory" diff --git a/vendor/gitea.com/lunny/nodb/store/goleveldb/db.go b/vendor/gitea.com/lunny/nodb/store/goleveldb/db.go deleted file mode 100644 index a3c98e9e1..000000000 --- a/vendor/gitea.com/lunny/nodb/store/goleveldb/db.go +++ /dev/null @@ -1,187 +0,0 @@ -package goleveldb - -import ( - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/cache" - "github.com/syndtr/goleveldb/leveldb/filter" - "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/syndtr/goleveldb/leveldb/storage" - - "gitea.com/lunny/nodb/config" - "gitea.com/lunny/nodb/store/driver" - - "os" -) - -const defaultFilterBits int = 10 - -type Store struct { -} - -func (s Store) String() string { - return DBName -} - -type MemStore struct { -} - -func (s MemStore) String() string { - return MemDBName -} - -type DB struct { - path string - - cfg *config.LevelDBConfig - - db *leveldb.DB - - opts *opt.Options - - iteratorOpts *opt.ReadOptions - - cache cache.Cache - - filter filter.Filter -} - -func (s Store) Open(path string, cfg *config.Config) (driver.IDB, error) { - if err := os.MkdirAll(path, os.ModePerm); err != nil { - return nil, err - } - - db := new(DB) - db.path = path - db.cfg = &cfg.LevelDB - - db.initOpts() - - var err error - db.db, err = leveldb.OpenFile(db.path, db.opts) - - if err != nil { - return nil, err - } - - return db, nil -} - -func (s Store) Repair(path string, cfg *config.Config) error { - db, err := leveldb.RecoverFile(path, newOptions(&cfg.LevelDB)) - if err != nil { - return err - } - - db.Close() - return nil -} - -func (s MemStore) Open(path string, cfg *config.Config) (driver.IDB, error) { - db := new(DB) - db.path = path - db.cfg = &cfg.LevelDB - - db.initOpts() - - var err error - db.db, err = leveldb.Open(storage.NewMemStorage(), db.opts) - if err != nil { - return nil, err - } - - return db, nil -} - -func (s MemStore) Repair(path string, cfg *config.Config) error { - return nil -} - -func (db *DB) initOpts() { - db.opts = newOptions(db.cfg) - - db.iteratorOpts = &opt.ReadOptions{} - db.iteratorOpts.DontFillCache = true -} - -func newOptions(cfg *config.LevelDBConfig) *opt.Options { - opts := &opt.Options{} - opts.ErrorIfMissing = false - - cfg.Adjust() - - //opts.BlockCacher = cache.NewLRU(cfg.CacheSize) - opts.BlockCacheCapacity = cfg.CacheSize - - //we must use bloomfilter - opts.Filter = filter.NewBloomFilter(defaultFilterBits) - - if !cfg.Compression { - opts.Compression = opt.NoCompression - } else { - opts.Compression = opt.SnappyCompression - } - - opts.BlockSize = cfg.BlockSize - opts.WriteBuffer = cfg.WriteBufferSize - - return opts -} - -func (db *DB) Close() error { - return db.db.Close() -} - -func (db *DB) Put(key, value []byte) error { - return db.db.Put(key, value, nil) -} - -func (db *DB) Get(key []byte) ([]byte, error) { - v, err := db.db.Get(key, nil) - if err == leveldb.ErrNotFound { - return nil, nil - } - return v, nil -} - -func (db *DB) Delete(key []byte) error { - return db.db.Delete(key, nil) -} - -func (db *DB) NewWriteBatch() driver.IWriteBatch { - wb := &WriteBatch{ - db: db, - wbatch: new(leveldb.Batch), - } - return wb -} - -func (db *DB) NewIterator() driver.IIterator { - it := &Iterator{ - db.db.NewIterator(nil, db.iteratorOpts), - } - - return it -} - -func (db *DB) Begin() (driver.Tx, error) { - return nil, driver.ErrTxSupport -} - -func (db *DB) NewSnapshot() (driver.ISnapshot, error) { - snapshot, err := db.db.GetSnapshot() - if err != nil { - return nil, err - } - - s := &Snapshot{ - db: db, - snp: snapshot, - } - - return s, nil -} - -func init() { - driver.Register(Store{}) - driver.Register(MemStore{}) -} diff --git a/vendor/gitea.com/lunny/nodb/store/goleveldb/iterator.go b/vendor/gitea.com/lunny/nodb/store/goleveldb/iterator.go deleted file mode 100644 index c1fd8b557..000000000 --- a/vendor/gitea.com/lunny/nodb/store/goleveldb/iterator.go +++ /dev/null @@ -1,49 +0,0 @@ -package goleveldb - -import ( - "github.com/syndtr/goleveldb/leveldb/iterator" -) - -type Iterator struct { - it iterator.Iterator -} - -func (it *Iterator) Key() []byte { - return it.it.Key() -} - -func (it *Iterator) Value() []byte { - return it.it.Value() -} - -func (it *Iterator) Close() error { - if it.it != nil { - it.it.Release() - it.it = nil - } - return nil -} - -func (it *Iterator) Valid() bool { - return it.it.Valid() -} - -func (it *Iterator) Next() { - it.it.Next() -} - -func (it *Iterator) Prev() { - it.it.Prev() -} - -func (it *Iterator) First() { - it.it.First() -} - -func (it *Iterator) Last() { - it.it.Last() -} - -func (it *Iterator) Seek(key []byte) { - it.it.Seek(key) -} diff --git a/vendor/gitea.com/lunny/nodb/store/goleveldb/snapshot.go b/vendor/gitea.com/lunny/nodb/store/goleveldb/snapshot.go deleted file mode 100644 index ddf40f7d1..000000000 --- a/vendor/gitea.com/lunny/nodb/store/goleveldb/snapshot.go +++ /dev/null @@ -1,26 +0,0 @@ -package goleveldb - -import ( - "gitea.com/lunny/nodb/store/driver" - "github.com/syndtr/goleveldb/leveldb" -) - -type Snapshot struct { - db *DB - snp *leveldb.Snapshot -} - -func (s *Snapshot) Get(key []byte) ([]byte, error) { - return s.snp.Get(key, s.db.iteratorOpts) -} - -func (s *Snapshot) NewIterator() driver.IIterator { - it := &Iterator{ - s.snp.NewIterator(nil, s.db.iteratorOpts), - } - return it -} - -func (s *Snapshot) Close() { - s.snp.Release() -} diff --git a/vendor/gitea.com/lunny/nodb/store/iterator.go b/vendor/gitea.com/lunny/nodb/store/iterator.go deleted file mode 100644 index df0311863..000000000 --- a/vendor/gitea.com/lunny/nodb/store/iterator.go +++ /dev/null @@ -1,327 +0,0 @@ -package store - -import ( - "bytes" - - "gitea.com/lunny/nodb/store/driver" -) - -const ( - IteratorForward uint8 = 0 - IteratorBackward uint8 = 1 -) - -const ( - RangeClose uint8 = 0x00 - RangeLOpen uint8 = 0x01 - RangeROpen uint8 = 0x10 - RangeOpen uint8 = 0x11 -) - -// min must less or equal than max -// -// range type: -// -// close: [min, max] -// open: (min, max) -// lopen: (min, max] -// ropen: [min, max) -// -type Range struct { - Min []byte - Max []byte - - Type uint8 -} - -type Limit struct { - Offset int - Count int -} - -type Iterator struct { - it driver.IIterator -} - -// Returns a copy of key. -func (it *Iterator) Key() []byte { - k := it.it.Key() - if k == nil { - return nil - } - - return append([]byte{}, k...) -} - -// Returns a copy of value. -func (it *Iterator) Value() []byte { - v := it.it.Value() - if v == nil { - return nil - } - - return append([]byte{}, v...) -} - -// Returns a reference of key. -// you must be careful that it will be changed after next iterate. -func (it *Iterator) RawKey() []byte { - return it.it.Key() -} - -// Returns a reference of value. -// you must be careful that it will be changed after next iterate. -func (it *Iterator) RawValue() []byte { - return it.it.Value() -} - -// Copy key to b, if b len is small or nil, returns a new one. -func (it *Iterator) BufKey(b []byte) []byte { - k := it.RawKey() - if k == nil { - return nil - } - if b == nil { - b = []byte{} - } - - b = b[0:0] - return append(b, k...) -} - -// Copy value to b, if b len is small or nil, returns a new one. -func (it *Iterator) BufValue(b []byte) []byte { - v := it.RawValue() - if v == nil { - return nil - } - - if b == nil { - b = []byte{} - } - - b = b[0:0] - return append(b, v...) -} - -func (it *Iterator) Close() { - if it.it != nil { - it.it.Close() - it.it = nil - } -} - -func (it *Iterator) Valid() bool { - return it.it.Valid() -} - -func (it *Iterator) Next() { - it.it.Next() -} - -func (it *Iterator) Prev() { - it.it.Prev() -} - -func (it *Iterator) SeekToFirst() { - it.it.First() -} - -func (it *Iterator) SeekToLast() { - it.it.Last() -} - -func (it *Iterator) Seek(key []byte) { - it.it.Seek(key) -} - -// Finds by key, if not found, nil returns. -func (it *Iterator) Find(key []byte) []byte { - it.Seek(key) - if it.Valid() { - k := it.RawKey() - if k == nil { - return nil - } else if bytes.Equal(k, key) { - return it.Value() - } - } - - return nil -} - -// Finds by key, if not found, nil returns, else a reference of value returns. -// you must be careful that it will be changed after next iterate. -func (it *Iterator) RawFind(key []byte) []byte { - it.Seek(key) - if it.Valid() { - k := it.RawKey() - if k == nil { - return nil - } else if bytes.Equal(k, key) { - return it.RawValue() - } - } - - return nil -} - -type RangeLimitIterator struct { - it *Iterator - - r *Range - l *Limit - - step int - - //0 for IteratorForward, 1 for IteratorBackward - direction uint8 -} - -func (it *RangeLimitIterator) Key() []byte { - return it.it.Key() -} - -func (it *RangeLimitIterator) Value() []byte { - return it.it.Value() -} - -func (it *RangeLimitIterator) RawKey() []byte { - return it.it.RawKey() -} - -func (it *RangeLimitIterator) RawValue() []byte { - return it.it.RawValue() -} - -func (it *RangeLimitIterator) BufKey(b []byte) []byte { - return it.it.BufKey(b) -} - -func (it *RangeLimitIterator) BufValue(b []byte) []byte { - return it.it.BufValue(b) -} - -func (it *RangeLimitIterator) Valid() bool { - if it.l.Offset < 0 { - return false - } else if !it.it.Valid() { - return false - } else if it.l.Count >= 0 && it.step >= it.l.Count { - return false - } - - if it.direction == IteratorForward { - if it.r.Max != nil { - r := bytes.Compare(it.it.RawKey(), it.r.Max) - if it.r.Type&RangeROpen > 0 { - return !(r >= 0) - } else { - return !(r > 0) - } - } - } else { - if it.r.Min != nil { - r := bytes.Compare(it.it.RawKey(), it.r.Min) - if it.r.Type&RangeLOpen > 0 { - return !(r <= 0) - } else { - return !(r < 0) - } - } - } - - return true -} - -func (it *RangeLimitIterator) Next() { - it.step++ - - if it.direction == IteratorForward { - it.it.Next() - } else { - it.it.Prev() - } -} - -func (it *RangeLimitIterator) Close() { - it.it.Close() -} - -func NewRangeLimitIterator(i *Iterator, r *Range, l *Limit) *RangeLimitIterator { - return rangeLimitIterator(i, r, l, IteratorForward) -} - -func NewRevRangeLimitIterator(i *Iterator, r *Range, l *Limit) *RangeLimitIterator { - return rangeLimitIterator(i, r, l, IteratorBackward) -} - -func NewRangeIterator(i *Iterator, r *Range) *RangeLimitIterator { - return rangeLimitIterator(i, r, &Limit{0, -1}, IteratorForward) -} - -func NewRevRangeIterator(i *Iterator, r *Range) *RangeLimitIterator { - return rangeLimitIterator(i, r, &Limit{0, -1}, IteratorBackward) -} - -func rangeLimitIterator(i *Iterator, r *Range, l *Limit, direction uint8) *RangeLimitIterator { - it := new(RangeLimitIterator) - - it.it = i - - it.r = r - it.l = l - it.direction = direction - - it.step = 0 - - if l.Offset < 0 { - return it - } - - if direction == IteratorForward { - if r.Min == nil { - it.it.SeekToFirst() - } else { - it.it.Seek(r.Min) - - if r.Type&RangeLOpen > 0 { - if it.it.Valid() && bytes.Equal(it.it.RawKey(), r.Min) { - it.it.Next() - } - } - } - } else { - if r.Max == nil { - it.it.SeekToLast() - } else { - it.it.Seek(r.Max) - - if !it.it.Valid() { - it.it.SeekToLast() - } else { - if !bytes.Equal(it.it.RawKey(), r.Max) { - it.it.Prev() - } - } - - if r.Type&RangeROpen > 0 { - if it.it.Valid() && bytes.Equal(it.it.RawKey(), r.Max) { - it.it.Prev() - } - } - } - } - - for i := 0; i < l.Offset; i++ { - if it.it.Valid() { - if it.direction == IteratorForward { - it.it.Next() - } else { - it.it.Prev() - } - } - } - - return it -} diff --git a/vendor/gitea.com/lunny/nodb/store/snapshot.go b/vendor/gitea.com/lunny/nodb/store/snapshot.go deleted file mode 100644 index 0d804e87b..000000000 --- a/vendor/gitea.com/lunny/nodb/store/snapshot.go +++ /dev/null @@ -1,16 +0,0 @@ -package store - -import ( - "gitea.com/lunny/nodb/store/driver" -) - -type Snapshot struct { - driver.ISnapshot -} - -func (s *Snapshot) NewIterator() *Iterator { - it := new(Iterator) - it.it = s.ISnapshot.NewIterator() - - return it -} diff --git a/vendor/gitea.com/lunny/nodb/store/store.go b/vendor/gitea.com/lunny/nodb/store/store.go deleted file mode 100644 index 687e40daf..000000000 --- a/vendor/gitea.com/lunny/nodb/store/store.go +++ /dev/null @@ -1,51 +0,0 @@ -package store - -import ( - "fmt" - "os" - "path" - "gitea.com/lunny/nodb/config" - "gitea.com/lunny/nodb/store/driver" - - _ "gitea.com/lunny/nodb/store/goleveldb" -) - -func getStorePath(cfg *config.Config) string { - return path.Join(cfg.DataDir, fmt.Sprintf("%s_data", cfg.DBName)) -} - -func Open(cfg *config.Config) (*DB, error) { - s, err := driver.GetStore(cfg) - if err != nil { - return nil, err - } - - path := getStorePath(cfg) - - if err := os.MkdirAll(path, os.ModePerm); err != nil { - return nil, err - } - - idb, err := s.Open(path, cfg) - if err != nil { - return nil, err - } - - db := &DB{idb} - - return db, nil -} - -func Repair(cfg *config.Config) error { - s, err := driver.GetStore(cfg) - if err != nil { - return err - } - - path := getStorePath(cfg) - - return s.Repair(path, cfg) -} - -func init() { -} diff --git a/vendor/gitea.com/lunny/nodb/store/tx.go b/vendor/gitea.com/lunny/nodb/store/tx.go deleted file mode 100644 index 3bcb5a939..000000000 --- a/vendor/gitea.com/lunny/nodb/store/tx.go +++ /dev/null @@ -1,42 +0,0 @@ -package store - -import ( - "gitea.com/lunny/nodb/store/driver" -) - -type Tx struct { - driver.Tx -} - -func (tx *Tx) NewIterator() *Iterator { - it := new(Iterator) - it.it = tx.Tx.NewIterator() - - return it -} - -func (tx *Tx) NewWriteBatch() WriteBatch { - return tx.Tx.NewWriteBatch() -} - -func (tx *Tx) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator { - return NewRangeLimitIterator(tx.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1}) -} - -func (tx *Tx) RevRangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator { - return NewRevRangeLimitIterator(tx.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1}) -} - -//count < 0, unlimit. -// -//offset must >= 0, if < 0, will get nothing. -func (tx *Tx) RangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator { - return NewRangeLimitIterator(tx.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count}) -} - -//count < 0, unlimit. -// -//offset must >= 0, if < 0, will get nothing. -func (tx *Tx) RevRangeLimitIterator(min []byte, max []byte, rangeType uint8, offset int, count int) *RangeLimitIterator { - return NewRevRangeLimitIterator(tx.NewIterator(), &Range{min, max, rangeType}, &Limit{offset, count}) -} diff --git a/vendor/gitea.com/lunny/nodb/store/writebatch.go b/vendor/gitea.com/lunny/nodb/store/writebatch.go deleted file mode 100644 index 9419ec51a..000000000 --- a/vendor/gitea.com/lunny/nodb/store/writebatch.go +++ /dev/null @@ -1,9 +0,0 @@ -package store - -import ( - "gitea.com/lunny/nodb/store/driver" -) - -type WriteBatch interface { - driver.IWriteBatch -} diff --git a/vendor/gitea.com/lunny/nodb/t_bit.go b/vendor/gitea.com/lunny/nodb/t_bit.go deleted file mode 100644 index 92ea83171..000000000 --- a/vendor/gitea.com/lunny/nodb/t_bit.go +++ /dev/null @@ -1,922 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "sort" - "time" - - "gitea.com/lunny/nodb/store" -) - -const ( - OPand uint8 = iota + 1 - OPor - OPxor - OPnot -) - -type BitPair struct { - Pos int32 - Val uint8 -} - -type segBitInfo struct { - Seq uint32 - Off uint32 - Val uint8 -} - -type segBitInfoArray []segBitInfo - -const ( - // byte - segByteWidth uint32 = 9 - segByteSize uint32 = 1 << segByteWidth - - // bit - segBitWidth uint32 = segByteWidth + 3 - segBitSize uint32 = segByteSize << 3 - - maxByteSize uint32 = 8 << 20 - maxSegCount uint32 = maxByteSize / segByteSize - - minSeq uint32 = 0 - maxSeq uint32 = uint32((maxByteSize << 3) - 1) -) - -var bitsInByte = [256]int32{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, - 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, - 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, - 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, - 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, - 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, - 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, - 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, - 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, - 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, - 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8} - -var fillBits = [...]uint8{1, 3, 7, 15, 31, 63, 127, 255} - -var emptySegment []byte = make([]byte, segByteSize, segByteSize) - -var fillSegment []byte = func() []byte { - data := make([]byte, segByteSize, segByteSize) - for i := uint32(0); i < segByteSize; i++ { - data[i] = 0xff - } - return data -}() - -var errBinKey = errors.New("invalid bin key") -var errOffset = errors.New("invalid offset") -var errDuplicatePos = errors.New("duplicate bit pos") - -func getBit(sz []byte, offset uint32) uint8 { - index := offset >> 3 - if index >= uint32(len(sz)) { - return 0 // error("overflow") - } - - offset -= index << 3 - return sz[index] >> offset & 1 -} - -func setBit(sz []byte, offset uint32, val uint8) bool { - if val != 1 && val != 0 { - return false // error("invalid val") - } - - index := offset >> 3 - if index >= uint32(len(sz)) { - return false // error("overflow") - } - - offset -= index << 3 - if sz[index]>>offset&1 != val { - sz[index] ^= (1 << offset) - } - return true -} - -func (datas segBitInfoArray) Len() int { - return len(datas) -} - -func (datas segBitInfoArray) Less(i, j int) bool { - res := (datas)[i].Seq < (datas)[j].Seq - if !res && (datas)[i].Seq == (datas)[j].Seq { - res = (datas)[i].Off < (datas)[j].Off - } - return res -} - -func (datas segBitInfoArray) Swap(i, j int) { - datas[i], datas[j] = datas[j], datas[i] -} - -func (db *DB) bEncodeMetaKey(key []byte) []byte { - mk := make([]byte, len(key)+2) - mk[0] = db.index - mk[1] = BitMetaType - - copy(mk[2:], key) - return mk -} - -func (db *DB) bDecodeMetaKey(bkey []byte) ([]byte, error) { - if len(bkey) < 2 || bkey[0] != db.index || bkey[1] != BitMetaType { - return nil, errBinKey - } - - return bkey[2:], nil -} - -func (db *DB) bEncodeBinKey(key []byte, seq uint32) []byte { - bk := make([]byte, len(key)+8) - - pos := 0 - bk[pos] = db.index - pos++ - bk[pos] = BitType - pos++ - - binary.BigEndian.PutUint16(bk[pos:], uint16(len(key))) - pos += 2 - - copy(bk[pos:], key) - pos += len(key) - - binary.BigEndian.PutUint32(bk[pos:], seq) - - return bk -} - -func (db *DB) bDecodeBinKey(bkey []byte) (key []byte, seq uint32, err error) { - if len(bkey) < 8 || bkey[0] != db.index { - err = errBinKey - return - } - - keyLen := binary.BigEndian.Uint16(bkey[2:4]) - if int(keyLen+8) != len(bkey) { - err = errBinKey - return - } - - key = bkey[4 : 4+keyLen] - seq = uint32(binary.BigEndian.Uint32(bkey[4+keyLen:])) - return -} - -func (db *DB) bCapByteSize(seq uint32, off uint32) uint32 { - var offByteSize uint32 = (off >> 3) + 1 - if offByteSize > segByteSize { - offByteSize = segByteSize - } - - return seq<= 0 { - offset += int32((uint32(tailSeq)<> segBitWidth - off &= (segBitSize - 1) - return -} - -func (db *DB) bGetMeta(key []byte) (tailSeq int32, tailOff int32, err error) { - var v []byte - - mk := db.bEncodeMetaKey(key) - v, err = db.bucket.Get(mk) - if err != nil { - return - } - - if v != nil { - tailSeq = int32(binary.LittleEndian.Uint32(v[0:4])) - tailOff = int32(binary.LittleEndian.Uint32(v[4:8])) - } else { - tailSeq = -1 - tailOff = -1 - } - return -} - -func (db *DB) bSetMeta(t *batch, key []byte, tailSeq uint32, tailOff uint32) { - ek := db.bEncodeMetaKey(key) - - buf := make([]byte, 8) - binary.LittleEndian.PutUint32(buf[0:4], tailSeq) - binary.LittleEndian.PutUint32(buf[4:8], tailOff) - - t.Put(ek, buf) - return -} - -func (db *DB) bUpdateMeta(t *batch, key []byte, seq uint32, off uint32) (tailSeq uint32, tailOff uint32, err error) { - var tseq, toff int32 - var update bool = false - - if tseq, toff, err = db.bGetMeta(key); err != nil { - return - } else if tseq < 0 { - update = true - } else { - tailSeq = uint32(MaxInt32(tseq, 0)) - tailOff = uint32(MaxInt32(toff, 0)) - update = (seq > tailSeq || (seq == tailSeq && off > tailOff)) - } - - if update { - db.bSetMeta(t, key, seq, off) - tailSeq = seq - tailOff = off - } - return -} - -func (db *DB) bDelete(t *batch, key []byte) (drop int64) { - mk := db.bEncodeMetaKey(key) - t.Delete(mk) - - minKey := db.bEncodeBinKey(key, minSeq) - maxKey := db.bEncodeBinKey(key, maxSeq) - it := db.bucket.RangeIterator(minKey, maxKey, store.RangeClose) - for ; it.Valid(); it.Next() { - t.Delete(it.RawKey()) - drop++ - } - it.Close() - - return drop -} - -func (db *DB) bGetSegment(key []byte, seq uint32) ([]byte, []byte, error) { - bk := db.bEncodeBinKey(key, seq) - segment, err := db.bucket.Get(bk) - if err != nil { - return bk, nil, err - } - return bk, segment, nil -} - -func (db *DB) bAllocateSegment(key []byte, seq uint32) ([]byte, []byte, error) { - bk, segment, err := db.bGetSegment(key, seq) - if err == nil && segment == nil { - segment = make([]byte, segByteSize, segByteSize) - } - return bk, segment, err -} - -func (db *DB) bIterator(key []byte) *store.RangeLimitIterator { - sk := db.bEncodeBinKey(key, minSeq) - ek := db.bEncodeBinKey(key, maxSeq) - return db.bucket.RangeIterator(sk, ek, store.RangeClose) -} - -func (db *DB) bSegAnd(a []byte, b []byte, res *[]byte) { - if a == nil || b == nil { - *res = nil - return - } - - data := *res - if data == nil { - data = make([]byte, segByteSize, segByteSize) - *res = data - } - - for i := uint32(0); i < segByteSize; i++ { - data[i] = a[i] & b[i] - } - return -} - -func (db *DB) bSegOr(a []byte, b []byte, res *[]byte) { - if a == nil || b == nil { - if a == nil && b == nil { - *res = nil - } else if a == nil { - *res = b - } else { - *res = a - } - return - } - - data := *res - if data == nil { - data = make([]byte, segByteSize, segByteSize) - *res = data - } - - for i := uint32(0); i < segByteSize; i++ { - data[i] = a[i] | b[i] - } - return -} - -func (db *DB) bSegXor(a []byte, b []byte, res *[]byte) { - if a == nil && b == nil { - *res = fillSegment - return - } - - if a == nil { - a = emptySegment - } - - if b == nil { - b = emptySegment - } - - data := *res - if data == nil { - data = make([]byte, segByteSize, segByteSize) - *res = data - } - - for i := uint32(0); i < segByteSize; i++ { - data[i] = a[i] ^ b[i] - } - - return -} - -func (db *DB) bExpireAt(key []byte, when int64) (int64, error) { - t := db.binBatch - t.Lock() - defer t.Unlock() - - if seq, _, err := db.bGetMeta(key); err != nil || seq < 0 { - return 0, err - } else { - db.expireAt(t, BitType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - } - return 1, nil -} - -func (db *DB) bCountByte(val byte, soff uint32, eoff uint32) int32 { - if soff > eoff { - soff, eoff = eoff, soff - } - - mask := uint8(0) - if soff > 0 { - mask |= fillBits[soff-1] - } - if eoff < 7 { - mask |= (fillBits[7] ^ fillBits[eoff]) - } - mask = fillBits[7] ^ mask - - return bitsInByte[val&mask] -} - -func (db *DB) bCountSeg(key []byte, seq uint32, soff uint32, eoff uint32) (cnt int32, err error) { - if soff >= segBitSize || soff < 0 || - eoff >= segBitSize || eoff < 0 { - return - } - - var segment []byte - if _, segment, err = db.bGetSegment(key, seq); err != nil { - return - } - - if segment == nil { - return - } - - if soff > eoff { - soff, eoff = eoff, soff - } - - headIdx := int(soff >> 3) - endIdx := int(eoff >> 3) - sByteOff := soff - ((soff >> 3) << 3) - eByteOff := eoff - ((eoff >> 3) << 3) - - if headIdx == endIdx { - cnt = db.bCountByte(segment[headIdx], sByteOff, eByteOff) - } else { - cnt = db.bCountByte(segment[headIdx], sByteOff, 7) + - db.bCountByte(segment[endIdx], 0, eByteOff) - } - - // sum up following bytes - for idx, end := headIdx+1, endIdx-1; idx <= end; idx += 1 { - cnt += bitsInByte[segment[idx]] - if idx == end { - break - } - } - - return -} - -func (db *DB) BGet(key []byte) (data []byte, err error) { - if err = checkKeySize(key); err != nil { - return - } - - var ts, to int32 - if ts, to, err = db.bGetMeta(key); err != nil || ts < 0 { - return - } - - var tailSeq, tailOff = uint32(ts), uint32(to) - var capByteSize uint32 = db.bCapByteSize(tailSeq, tailOff) - data = make([]byte, capByteSize, capByteSize) - - minKey := db.bEncodeBinKey(key, minSeq) - maxKey := db.bEncodeBinKey(key, tailSeq) - it := db.bucket.RangeIterator(minKey, maxKey, store.RangeClose) - - var seq, s, e uint32 - for ; it.Valid(); it.Next() { - if _, seq, err = db.bDecodeBinKey(it.RawKey()); err != nil { - data = nil - break - } - - s = seq << segByteWidth - e = MinUInt32(s+segByteSize, capByteSize) - copy(data[s:e], it.RawValue()) - } - it.Close() - - return -} - -func (db *DB) BDelete(key []byte) (drop int64, err error) { - if err = checkKeySize(key); err != nil { - return - } - - t := db.binBatch - t.Lock() - defer t.Unlock() - - drop = db.bDelete(t, key) - db.rmExpire(t, BitType, key) - - err = t.Commit() - return -} - -func (db *DB) BSetBit(key []byte, offset int32, val uint8) (ori uint8, err error) { - if err = checkKeySize(key); err != nil { - return - } - - // todo : check offset - var seq, off uint32 - if seq, off, err = db.bParseOffset(key, offset); err != nil { - return 0, err - } - - var bk, segment []byte - if bk, segment, err = db.bAllocateSegment(key, seq); err != nil { - return 0, err - } - - if segment != nil { - ori = getBit(segment, off) - if setBit(segment, off, val) { - t := db.binBatch - t.Lock() - defer t.Unlock() - - t.Put(bk, segment) - if _, _, e := db.bUpdateMeta(t, key, seq, off); e != nil { - err = e - return - } - - err = t.Commit() - } - } - - return -} - -func (db *DB) BMSetBit(key []byte, args ...BitPair) (place int64, err error) { - if err = checkKeySize(key); err != nil { - return - } - - // (ps : so as to aviod wasting memory copy while calling db.Get() and batch.Put(), - // here we sequence the params by pos, so that we can merge the execution of - // diff pos setting which targets on the same segment respectively. ) - - // #1 : sequence request data - var argCnt = len(args) - var bitInfos segBitInfoArray = make(segBitInfoArray, argCnt) - var seq, off uint32 - - for i, info := range args { - if seq, off, err = db.bParseOffset(key, info.Pos); err != nil { - return - } - - bitInfos[i].Seq = seq - bitInfos[i].Off = off - bitInfos[i].Val = info.Val - } - - sort.Sort(bitInfos) - - for i := 1; i < argCnt; i++ { - if bitInfos[i].Seq == bitInfos[i-1].Seq && bitInfos[i].Off == bitInfos[i-1].Off { - return 0, errDuplicatePos - } - } - - // #2 : execute bit set in order - t := db.binBatch - t.Lock() - defer t.Unlock() - - var curBinKey, curSeg []byte - var curSeq, maxSeq, maxOff uint32 - - for _, info := range bitInfos { - if curSeg != nil && info.Seq != curSeq { - t.Put(curBinKey, curSeg) - curSeg = nil - } - - if curSeg == nil { - curSeq = info.Seq - if curBinKey, curSeg, err = db.bAllocateSegment(key, info.Seq); err != nil { - return - } - - if curSeg == nil { - continue - } - } - - if setBit(curSeg, info.Off, info.Val) { - maxSeq = info.Seq - maxOff = info.Off - place++ - } - } - - if curSeg != nil { - t.Put(curBinKey, curSeg) - } - - // finally, update meta - if place > 0 { - if _, _, err = db.bUpdateMeta(t, key, maxSeq, maxOff); err != nil { - return - } - - err = t.Commit() - } - - return -} - -func (db *DB) BGetBit(key []byte, offset int32) (uint8, error) { - if seq, off, err := db.bParseOffset(key, offset); err != nil { - return 0, err - } else { - _, segment, err := db.bGetSegment(key, seq) - if err != nil { - return 0, err - } - - if segment == nil { - return 0, nil - } else { - return getBit(segment, off), nil - } - } -} - -// func (db *DB) BGetRange(key []byte, start int32, end int32) ([]byte, error) { -// section := make([]byte) - -// return -// } - -func (db *DB) BCount(key []byte, start int32, end int32) (cnt int32, err error) { - var sseq, soff uint32 - if sseq, soff, err = db.bParseOffset(key, start); err != nil { - return - } - - var eseq, eoff uint32 - if eseq, eoff, err = db.bParseOffset(key, end); err != nil { - return - } - - if sseq > eseq || (sseq == eseq && soff > eoff) { - sseq, eseq = eseq, sseq - soff, eoff = eoff, soff - } - - var segCnt int32 - if eseq == sseq { - if segCnt, err = db.bCountSeg(key, sseq, soff, eoff); err != nil { - return 0, err - } - - cnt = segCnt - - } else { - if segCnt, err = db.bCountSeg(key, sseq, soff, segBitSize-1); err != nil { - return 0, err - } else { - cnt += segCnt - } - - if segCnt, err = db.bCountSeg(key, eseq, 0, eoff); err != nil { - return 0, err - } else { - cnt += segCnt - } - } - - // middle segs - var segment []byte - skey := db.bEncodeBinKey(key, sseq) - ekey := db.bEncodeBinKey(key, eseq) - - it := db.bucket.RangeIterator(skey, ekey, store.RangeOpen) - for ; it.Valid(); it.Next() { - segment = it.RawValue() - for _, bt := range segment { - cnt += bitsInByte[bt] - } - } - it.Close() - - return -} - -func (db *DB) BTail(key []byte) (int32, error) { - // effective length of data, the highest bit-pos set in history - tailSeq, tailOff, err := db.bGetMeta(key) - if err != nil { - return 0, err - } - - tail := int32(-1) - if tailSeq >= 0 { - tail = int32(uint32(tailSeq)< maxDstSeq || (seq == maxDstSeq && off > maxDstOff) { - maxDstSeq = seq - maxDstOff = off - } - } - - if (op == OPnot && validKeyNum != 1) || - (op != OPnot && validKeyNum < 2) { - return // with not enough existing source key - } - - var srcIdx int - for srcIdx = 0; srcIdx < keyNum; srcIdx++ { - if srckeys[srcIdx] != nil { - break - } - } - - // init - data - var segments = make([][]byte, maxDstSeq+1) - - if op == OPnot { - // ps : - // ( ~num == num ^ 0x11111111 ) - // we init the result segments with all bit set, - // then we can calculate through the way of 'xor'. - - // ahead segments bin format : 1111 ... 1111 - for i := uint32(0); i < maxDstSeq; i++ { - segments[i] = fillSegment - } - - // last segment bin format : 1111..1100..0000 - var tailSeg = make([]byte, segByteSize, segByteSize) - var fillByte = fillBits[7] - var tailSegLen = db.bCapByteSize(uint32(0), maxDstOff) - for i := uint32(0); i < tailSegLen-1; i++ { - tailSeg[i] = fillByte - } - tailSeg[tailSegLen-1] = fillBits[maxDstOff-(tailSegLen-1)<<3] - segments[maxDstSeq] = tailSeg - - } else { - // ps : init segments by data corresponding to the 1st valid source key - it := db.bIterator(srckeys[srcIdx]) - for ; it.Valid(); it.Next() { - if _, seq, err = db.bDecodeBinKey(it.RawKey()); err != nil { - // to do ... - it.Close() - return - } - segments[seq] = it.Value() - } - it.Close() - srcIdx++ - } - - // operation with following keys - var res []byte - for i := srcIdx; i < keyNum; i++ { - if srckeys[i] == nil { - continue - } - - it := db.bIterator(srckeys[i]) - for idx, end := uint32(0), false; !end; it.Next() { - end = !it.Valid() - if !end { - if _, seq, err = db.bDecodeBinKey(it.RawKey()); err != nil { - // to do ... - it.Close() - return - } - } else { - seq = maxDstSeq + 1 - } - - // todo : - // operation 'and' can be optimize here : - // if seq > max_segments_idx, this loop can be break, - // which can avoid cost from Key() and bDecodeBinKey() - - for ; idx < seq; idx++ { - res = nil - exeOp(segments[idx], nil, &res) - segments[idx] = res - } - - if !end { - res = it.Value() - exeOp(segments[seq], res, &res) - segments[seq] = res - idx++ - } - } - it.Close() - } - - // clear the old data in case - db.bDelete(t, dstkey) - db.rmExpire(t, BitType, dstkey) - - // set data - db.bSetMeta(t, dstkey, maxDstSeq, maxDstOff) - - var bk []byte - for seq, segt := range segments { - if segt != nil { - bk = db.bEncodeBinKey(dstkey, uint32(seq)) - t.Put(bk, segt) - } - } - - err = t.Commit() - if err == nil { - // blen = int32(db.bCapByteSize(maxDstOff, maxDstOff)) - blen = int32(maxDstSeq< MaxKeySize || len(key) == 0 { - return errKeySize - } else if len(field) > MaxHashFieldSize || len(field) == 0 { - return errHashFieldSize - } - return nil -} - -func (db *DB) hEncodeSizeKey(key []byte) []byte { - buf := make([]byte, len(key)+2) - - buf[0] = db.index - buf[1] = HSizeType - - copy(buf[2:], key) - return buf -} - -func (db *DB) hDecodeSizeKey(ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != HSizeType { - return nil, errHSizeKey - } - - return ek[2:], nil -} - -func (db *DB) hEncodeHashKey(key []byte, field []byte) []byte { - buf := make([]byte, len(key)+len(field)+1+1+2+1) - - pos := 0 - buf[pos] = db.index - pos++ - buf[pos] = HashType - pos++ - - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - - copy(buf[pos:], key) - pos += len(key) - - buf[pos] = hashStartSep - pos++ - copy(buf[pos:], field) - - return buf -} - -func (db *DB) hDecodeHashKey(ek []byte) ([]byte, []byte, error) { - if len(ek) < 5 || ek[0] != db.index || ek[1] != HashType { - return nil, nil, errHashKey - } - - pos := 2 - keyLen := int(binary.BigEndian.Uint16(ek[pos:])) - pos += 2 - - if keyLen+5 > len(ek) { - return nil, nil, errHashKey - } - - key := ek[pos : pos+keyLen] - pos += keyLen - - if ek[pos] != hashStartSep { - return nil, nil, errHashKey - } - - pos++ - field := ek[pos:] - return key, field, nil -} - -func (db *DB) hEncodeStartKey(key []byte) []byte { - return db.hEncodeHashKey(key, nil) -} - -func (db *DB) hEncodeStopKey(key []byte) []byte { - k := db.hEncodeHashKey(key, nil) - - k[len(k)-1] = hashStopSep - - return k -} - -func (db *DB) hSetItem(key []byte, field []byte, value []byte) (int64, error) { - t := db.hashBatch - - ek := db.hEncodeHashKey(key, field) - - var n int64 = 1 - if v, _ := db.bucket.Get(ek); v != nil { - n = 0 - } else { - if _, err := db.hIncrSize(key, 1); err != nil { - return 0, err - } - } - - t.Put(ek, value) - return n, nil -} - -// ps : here just focus on deleting the hash data, -// any other likes expire is ignore. -func (db *DB) hDelete(t *batch, key []byte) int64 { - sk := db.hEncodeSizeKey(key) - start := db.hEncodeStartKey(key) - stop := db.hEncodeStopKey(key) - - var num int64 = 0 - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - t.Delete(it.Key()) - num++ - } - it.Close() - - t.Delete(sk) - return num -} - -func (db *DB) hExpireAt(key []byte, when int64) (int64, error) { - t := db.hashBatch - t.Lock() - defer t.Unlock() - - if hlen, err := db.HLen(key); err != nil || hlen == 0 { - return 0, err - } else { - db.expireAt(t, HashType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - } - return 1, nil -} - -func (db *DB) HLen(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - return Int64(db.bucket.Get(db.hEncodeSizeKey(key))) -} - -func (db *DB) HSet(key []byte, field []byte, value []byte) (int64, error) { - if err := checkHashKFSize(key, field); err != nil { - return 0, err - } else if err := checkValueSize(value); err != nil { - return 0, err - } - - t := db.hashBatch - t.Lock() - defer t.Unlock() - - n, err := db.hSetItem(key, field, value) - if err != nil { - return 0, err - } - - //todo add binlog - - err = t.Commit() - return n, err -} - -func (db *DB) HGet(key []byte, field []byte) ([]byte, error) { - if err := checkHashKFSize(key, field); err != nil { - return nil, err - } - - return db.bucket.Get(db.hEncodeHashKey(key, field)) -} - -func (db *DB) HMset(key []byte, args ...FVPair) error { - t := db.hashBatch - t.Lock() - defer t.Unlock() - - var err error - var ek []byte - var num int64 = 0 - for i := 0; i < len(args); i++ { - if err := checkHashKFSize(key, args[i].Field); err != nil { - return err - } else if err := checkValueSize(args[i].Value); err != nil { - return err - } - - ek = db.hEncodeHashKey(key, args[i].Field) - - if v, err := db.bucket.Get(ek); err != nil { - return err - } else if v == nil { - num++ - } - - t.Put(ek, args[i].Value) - } - - if _, err = db.hIncrSize(key, num); err != nil { - return err - } - - //todo add binglog - err = t.Commit() - return err -} - -func (db *DB) HMget(key []byte, args ...[]byte) ([][]byte, error) { - var ek []byte - - it := db.bucket.NewIterator() - defer it.Close() - - r := make([][]byte, len(args)) - for i := 0; i < len(args); i++ { - if err := checkHashKFSize(key, args[i]); err != nil { - return nil, err - } - - ek = db.hEncodeHashKey(key, args[i]) - - r[i] = it.Find(ek) - } - - return r, nil -} - -func (db *DB) HDel(key []byte, args ...[]byte) (int64, error) { - t := db.hashBatch - - var ek []byte - var v []byte - var err error - - t.Lock() - defer t.Unlock() - - it := db.bucket.NewIterator() - defer it.Close() - - var num int64 = 0 - for i := 0; i < len(args); i++ { - if err := checkHashKFSize(key, args[i]); err != nil { - return 0, err - } - - ek = db.hEncodeHashKey(key, args[i]) - - v = it.RawFind(ek) - if v == nil { - continue - } else { - num++ - t.Delete(ek) - } - } - - if _, err = db.hIncrSize(key, -num); err != nil { - return 0, err - } - - err = t.Commit() - - return num, err -} - -func (db *DB) hIncrSize(key []byte, delta int64) (int64, error) { - t := db.hashBatch - sk := db.hEncodeSizeKey(key) - - var err error - var size int64 = 0 - if size, err = Int64(db.bucket.Get(sk)); err != nil { - return 0, err - } else { - size += delta - if size <= 0 { - size = 0 - t.Delete(sk) - db.rmExpire(t, HashType, key) - } else { - t.Put(sk, PutInt64(size)) - } - } - - return size, nil -} - -func (db *DB) HIncrBy(key []byte, field []byte, delta int64) (int64, error) { - if err := checkHashKFSize(key, field); err != nil { - return 0, err - } - - t := db.hashBatch - var ek []byte - var err error - - t.Lock() - defer t.Unlock() - - ek = db.hEncodeHashKey(key, field) - - var n int64 = 0 - if n, err = StrInt64(db.bucket.Get(ek)); err != nil { - return 0, err - } - - n += delta - - _, err = db.hSetItem(key, field, StrPutInt64(n)) - if err != nil { - return 0, err - } - - err = t.Commit() - - return n, err -} - -func (db *DB) HGetAll(key []byte) ([]FVPair, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - start := db.hEncodeStartKey(key) - stop := db.hEncodeStopKey(key) - - v := make([]FVPair, 0, 16) - - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - _, f, err := db.hDecodeHashKey(it.Key()) - if err != nil { - return nil, err - } - - v = append(v, FVPair{Field: f, Value: it.Value()}) - } - - it.Close() - - return v, nil -} - -func (db *DB) HKeys(key []byte) ([][]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - start := db.hEncodeStartKey(key) - stop := db.hEncodeStopKey(key) - - v := make([][]byte, 0, 16) - - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - _, f, err := db.hDecodeHashKey(it.Key()) - if err != nil { - return nil, err - } - v = append(v, f) - } - - it.Close() - - return v, nil -} - -func (db *DB) HValues(key []byte) ([][]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - start := db.hEncodeStartKey(key) - stop := db.hEncodeStopKey(key) - - v := make([][]byte, 0, 16) - - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - _, _, err := db.hDecodeHashKey(it.Key()) - if err != nil { - return nil, err - } - - v = append(v, it.Value()) - } - - it.Close() - - return v, nil -} - -func (db *DB) HClear(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.hashBatch - t.Lock() - defer t.Unlock() - - num := db.hDelete(t, key) - db.rmExpire(t, HashType, key) - - err := t.Commit() - return num, err -} - -func (db *DB) HMclear(keys ...[]byte) (int64, error) { - t := db.hashBatch - t.Lock() - defer t.Unlock() - - for _, key := range keys { - if err := checkKeySize(key); err != nil { - return 0, err - } - - db.hDelete(t, key) - db.rmExpire(t, HashType, key) - } - - err := t.Commit() - return int64(len(keys)), err -} - -func (db *DB) hFlush() (drop int64, err error) { - t := db.hashBatch - - t.Lock() - defer t.Unlock() - - return db.flushType(t, HashType) -} - -func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(HSizeType, key, count, inclusive, match) -} - -func (db *DB) HExpire(key []byte, duration int64) (int64, error) { - if duration <= 0 { - return 0, errExpireValue - } - - return db.hExpireAt(key, time.Now().Unix()+duration) -} - -func (db *DB) HExpireAt(key []byte, when int64) (int64, error) { - if when <= time.Now().Unix() { - return 0, errExpireValue - } - - return db.hExpireAt(key, when) -} - -func (db *DB) HTTL(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return -1, err - } - - return db.ttl(HashType, key) -} - -func (db *DB) HPersist(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.hashBatch - t.Lock() - defer t.Unlock() - - n, err := db.rmExpire(t, HashType, key) - if err != nil { - return 0, err - } - - err = t.Commit() - return n, err -} diff --git a/vendor/gitea.com/lunny/nodb/t_kv.go b/vendor/gitea.com/lunny/nodb/t_kv.go deleted file mode 100644 index 82a12f702..000000000 --- a/vendor/gitea.com/lunny/nodb/t_kv.go +++ /dev/null @@ -1,387 +0,0 @@ -package nodb - -import ( - "errors" - "time" -) - -type KVPair struct { - Key []byte - Value []byte -} - -var errKVKey = errors.New("invalid encode kv key") - -func checkKeySize(key []byte) error { - if len(key) > MaxKeySize || len(key) == 0 { - return errKeySize - } - return nil -} - -func checkValueSize(value []byte) error { - if len(value) > MaxValueSize { - return errValueSize - } - - return nil -} - -func (db *DB) encodeKVKey(key []byte) []byte { - ek := make([]byte, len(key)+2) - ek[0] = db.index - ek[1] = KVType - copy(ek[2:], key) - return ek -} - -func (db *DB) decodeKVKey(ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != KVType { - return nil, errKVKey - } - - return ek[2:], nil -} - -func (db *DB) encodeKVMinKey() []byte { - ek := db.encodeKVKey(nil) - return ek -} - -func (db *DB) encodeKVMaxKey() []byte { - ek := db.encodeKVKey(nil) - ek[len(ek)-1] = KVType + 1 - return ek -} - -func (db *DB) incr(key []byte, delta int64) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - var err error - key = db.encodeKVKey(key) - - t := db.kvBatch - - t.Lock() - defer t.Unlock() - - var n int64 - n, err = StrInt64(db.bucket.Get(key)) - if err != nil { - return 0, err - } - - n += delta - - t.Put(key, StrPutInt64(n)) - - //todo binlog - - err = t.Commit() - return n, err -} - -// ps : here just focus on deleting the key-value data, -// any other likes expire is ignore. -func (db *DB) delete(t *batch, key []byte) int64 { - key = db.encodeKVKey(key) - t.Delete(key) - return 1 -} - -func (db *DB) setExpireAt(key []byte, when int64) (int64, error) { - t := db.kvBatch - t.Lock() - defer t.Unlock() - - if exist, err := db.Exists(key); err != nil || exist == 0 { - return 0, err - } else { - db.expireAt(t, KVType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - } - return 1, nil -} - -func (db *DB) Decr(key []byte) (int64, error) { - return db.incr(key, -1) -} - -func (db *DB) DecrBy(key []byte, decrement int64) (int64, error) { - return db.incr(key, -decrement) -} - -func (db *DB) Del(keys ...[]byte) (int64, error) { - if len(keys) == 0 { - return 0, nil - } - - codedKeys := make([][]byte, len(keys)) - for i, k := range keys { - codedKeys[i] = db.encodeKVKey(k) - } - - t := db.kvBatch - t.Lock() - defer t.Unlock() - - for i, k := range keys { - t.Delete(codedKeys[i]) - db.rmExpire(t, KVType, k) - } - - err := t.Commit() - return int64(len(keys)), err -} - -func (db *DB) Exists(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - var err error - key = db.encodeKVKey(key) - - var v []byte - v, err = db.bucket.Get(key) - if v != nil && err == nil { - return 1, nil - } - - return 0, err -} - -func (db *DB) Get(key []byte) ([]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - key = db.encodeKVKey(key) - - return db.bucket.Get(key) -} - -func (db *DB) GetSet(key []byte, value []byte) ([]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } else if err := checkValueSize(value); err != nil { - return nil, err - } - - key = db.encodeKVKey(key) - - t := db.kvBatch - - t.Lock() - defer t.Unlock() - - oldValue, err := db.bucket.Get(key) - if err != nil { - return nil, err - } - - t.Put(key, value) - //todo, binlog - - err = t.Commit() - - return oldValue, err -} - -func (db *DB) Incr(key []byte) (int64, error) { - return db.incr(key, 1) -} - -func (db *DB) IncrBy(key []byte, increment int64) (int64, error) { - return db.incr(key, increment) -} - -func (db *DB) MGet(keys ...[]byte) ([][]byte, error) { - values := make([][]byte, len(keys)) - - it := db.bucket.NewIterator() - defer it.Close() - - for i := range keys { - if err := checkKeySize(keys[i]); err != nil { - return nil, err - } - - values[i] = it.Find(db.encodeKVKey(keys[i])) - } - - return values, nil -} - -func (db *DB) MSet(args ...KVPair) error { - if len(args) == 0 { - return nil - } - - t := db.kvBatch - - var err error - var key []byte - var value []byte - - t.Lock() - defer t.Unlock() - - for i := 0; i < len(args); i++ { - if err := checkKeySize(args[i].Key); err != nil { - return err - } else if err := checkValueSize(args[i].Value); err != nil { - return err - } - - key = db.encodeKVKey(args[i].Key) - - value = args[i].Value - - t.Put(key, value) - - //todo binlog - } - - err = t.Commit() - return err -} - -func (db *DB) Set(key []byte, value []byte) error { - if err := checkKeySize(key); err != nil { - return err - } else if err := checkValueSize(value); err != nil { - return err - } - - var err error - key = db.encodeKVKey(key) - - t := db.kvBatch - - t.Lock() - defer t.Unlock() - - t.Put(key, value) - - err = t.Commit() - - return err -} - -func (db *DB) SetNX(key []byte, value []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } else if err := checkValueSize(value); err != nil { - return 0, err - } - - var err error - key = db.encodeKVKey(key) - - var n int64 = 1 - - t := db.kvBatch - - t.Lock() - defer t.Unlock() - - if v, err := db.bucket.Get(key); err != nil { - return 0, err - } else if v != nil { - n = 0 - } else { - t.Put(key, value) - - //todo binlog - - err = t.Commit() - } - - return n, err -} - -func (db *DB) flush() (drop int64, err error) { - t := db.kvBatch - t.Lock() - defer t.Unlock() - return db.flushType(t, KVType) -} - -//if inclusive is true, scan range [key, inf) else (key, inf) -func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(KVType, key, count, inclusive, match) -} - -func (db *DB) Expire(key []byte, duration int64) (int64, error) { - if duration <= 0 { - return 0, errExpireValue - } - - return db.setExpireAt(key, time.Now().Unix()+duration) -} - -func (db *DB) ExpireAt(key []byte, when int64) (int64, error) { - if when <= time.Now().Unix() { - return 0, errExpireValue - } - - return db.setExpireAt(key, when) -} - -func (db *DB) TTL(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return -1, err - } - - return db.ttl(KVType, key) -} - -func (db *DB) Persist(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.kvBatch - t.Lock() - defer t.Unlock() - n, err := db.rmExpire(t, KVType, key) - if err != nil { - return 0, err - } - - err = t.Commit() - return n, err -} - -func (db *DB) Lock() { - t := db.kvBatch - t.Lock() -} - -func (db *DB) Remove(key []byte) bool { - if len(key) == 0 { - return false - } - t := db.kvBatch - t.Delete(db.encodeKVKey(key)) - _, err := db.rmExpire(t, KVType, key) - if err != nil { - return false - } - return true -} - -func (db *DB) Commit() error { - t := db.kvBatch - return t.Commit() -} - -func (db *DB) Unlock() { - t := db.kvBatch - t.Unlock() -} diff --git a/vendor/gitea.com/lunny/nodb/t_list.go b/vendor/gitea.com/lunny/nodb/t_list.go deleted file mode 100644 index 6e66604d6..000000000 --- a/vendor/gitea.com/lunny/nodb/t_list.go +++ /dev/null @@ -1,492 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "time" - - "gitea.com/lunny/nodb/store" -) - -const ( - listHeadSeq int32 = 1 - listTailSeq int32 = 2 - - listMinSeq int32 = 1000 - listMaxSeq int32 = 1<<31 - 1000 - listInitialSeq int32 = listMinSeq + (listMaxSeq-listMinSeq)/2 -) - -var errLMetaKey = errors.New("invalid lmeta key") -var errListKey = errors.New("invalid list key") -var errListSeq = errors.New("invalid list sequence, overflow") - -func (db *DB) lEncodeMetaKey(key []byte) []byte { - buf := make([]byte, len(key)+2) - buf[0] = db.index - buf[1] = LMetaType - - copy(buf[2:], key) - return buf -} - -func (db *DB) lDecodeMetaKey(ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != LMetaType { - return nil, errLMetaKey - } - - return ek[2:], nil -} - -func (db *DB) lEncodeListKey(key []byte, seq int32) []byte { - buf := make([]byte, len(key)+8) - - pos := 0 - buf[pos] = db.index - pos++ - buf[pos] = ListType - pos++ - - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - - copy(buf[pos:], key) - pos += len(key) - - binary.BigEndian.PutUint32(buf[pos:], uint32(seq)) - - return buf -} - -func (db *DB) lDecodeListKey(ek []byte) (key []byte, seq int32, err error) { - if len(ek) < 8 || ek[0] != db.index || ek[1] != ListType { - err = errListKey - return - } - - keyLen := int(binary.BigEndian.Uint16(ek[2:])) - if keyLen+8 != len(ek) { - err = errListKey - return - } - - key = ek[4 : 4+keyLen] - seq = int32(binary.BigEndian.Uint32(ek[4+keyLen:])) - return -} - -func (db *DB) lpush(key []byte, whereSeq int32, args ...[]byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - var headSeq int32 - var tailSeq int32 - var size int32 - var err error - - t := db.listBatch - t.Lock() - defer t.Unlock() - - metaKey := db.lEncodeMetaKey(key) - headSeq, tailSeq, size, err = db.lGetMeta(nil, metaKey) - if err != nil { - return 0, err - } - - var pushCnt int = len(args) - if pushCnt == 0 { - return int64(size), nil - } - - var seq int32 = headSeq - var delta int32 = -1 - if whereSeq == listTailSeq { - seq = tailSeq - delta = 1 - } - - // append elements - if size > 0 { - seq += delta - } - - for i := 0; i < pushCnt; i++ { - ek := db.lEncodeListKey(key, seq+int32(i)*delta) - t.Put(ek, args[i]) - } - - seq += int32(pushCnt-1) * delta - if seq <= listMinSeq || seq >= listMaxSeq { - return 0, errListSeq - } - - // set meta info - if whereSeq == listHeadSeq { - headSeq = seq - } else { - tailSeq = seq - } - - db.lSetMeta(metaKey, headSeq, tailSeq) - - err = t.Commit() - return int64(size) + int64(pushCnt), err -} - -func (db *DB) lpop(key []byte, whereSeq int32) ([]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - t := db.listBatch - t.Lock() - defer t.Unlock() - - var headSeq int32 - var tailSeq int32 - var err error - - metaKey := db.lEncodeMetaKey(key) - headSeq, tailSeq, _, err = db.lGetMeta(nil, metaKey) - if err != nil { - return nil, err - } - - var value []byte - - var seq int32 = headSeq - if whereSeq == listTailSeq { - seq = tailSeq - } - - itemKey := db.lEncodeListKey(key, seq) - value, err = db.bucket.Get(itemKey) - if err != nil { - return nil, err - } - - if whereSeq == listHeadSeq { - headSeq += 1 - } else { - tailSeq -= 1 - } - - t.Delete(itemKey) - size := db.lSetMeta(metaKey, headSeq, tailSeq) - if size == 0 { - db.rmExpire(t, HashType, key) - } - - err = t.Commit() - return value, err -} - -// ps : here just focus on deleting the list data, -// any other likes expire is ignore. -func (db *DB) lDelete(t *batch, key []byte) int64 { - mk := db.lEncodeMetaKey(key) - - var headSeq int32 - var tailSeq int32 - var err error - - it := db.bucket.NewIterator() - defer it.Close() - - headSeq, tailSeq, _, err = db.lGetMeta(it, mk) - if err != nil { - return 0 - } - - var num int64 = 0 - startKey := db.lEncodeListKey(key, headSeq) - stopKey := db.lEncodeListKey(key, tailSeq) - - rit := store.NewRangeIterator(it, &store.Range{startKey, stopKey, store.RangeClose}) - for ; rit.Valid(); rit.Next() { - t.Delete(rit.RawKey()) - num++ - } - - t.Delete(mk) - - return num -} - -func (db *DB) lGetMeta(it *store.Iterator, ek []byte) (headSeq int32, tailSeq int32, size int32, err error) { - var v []byte - if it != nil { - v = it.Find(ek) - } else { - v, err = db.bucket.Get(ek) - } - if err != nil { - return - } else if v == nil { - headSeq = listInitialSeq - tailSeq = listInitialSeq - size = 0 - return - } else { - headSeq = int32(binary.LittleEndian.Uint32(v[0:4])) - tailSeq = int32(binary.LittleEndian.Uint32(v[4:8])) - size = tailSeq - headSeq + 1 - } - return -} - -func (db *DB) lSetMeta(ek []byte, headSeq int32, tailSeq int32) int32 { - t := db.listBatch - - var size int32 = tailSeq - headSeq + 1 - if size < 0 { - // todo : log error + panic - } else if size == 0 { - t.Delete(ek) - } else { - buf := make([]byte, 8) - - binary.LittleEndian.PutUint32(buf[0:4], uint32(headSeq)) - binary.LittleEndian.PutUint32(buf[4:8], uint32(tailSeq)) - - t.Put(ek, buf) - } - - return size -} - -func (db *DB) lExpireAt(key []byte, when int64) (int64, error) { - t := db.listBatch - t.Lock() - defer t.Unlock() - - if llen, err := db.LLen(key); err != nil || llen == 0 { - return 0, err - } else { - db.expireAt(t, ListType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - } - return 1, nil -} - -func (db *DB) LIndex(key []byte, index int32) ([]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - var seq int32 - var headSeq int32 - var tailSeq int32 - var err error - - metaKey := db.lEncodeMetaKey(key) - - it := db.bucket.NewIterator() - defer it.Close() - - headSeq, tailSeq, _, err = db.lGetMeta(it, metaKey) - if err != nil { - return nil, err - } - - if index >= 0 { - seq = headSeq + index - } else { - seq = tailSeq + index + 1 - } - - sk := db.lEncodeListKey(key, seq) - v := it.Find(sk) - - return v, nil -} - -func (db *DB) LLen(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - ek := db.lEncodeMetaKey(key) - _, _, size, err := db.lGetMeta(nil, ek) - return int64(size), err -} - -func (db *DB) LPop(key []byte) ([]byte, error) { - return db.lpop(key, listHeadSeq) -} - -func (db *DB) LPush(key []byte, arg1 []byte, args ...[]byte) (int64, error) { - var argss = [][]byte{arg1} - argss = append(argss, args...) - return db.lpush(key, listHeadSeq, argss...) -} - -func (db *DB) LRange(key []byte, start int32, stop int32) ([][]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - var headSeq int32 - var llen int32 - var err error - - metaKey := db.lEncodeMetaKey(key) - - it := db.bucket.NewIterator() - defer it.Close() - - if headSeq, _, llen, err = db.lGetMeta(it, metaKey); err != nil { - return nil, err - } - - if start < 0 { - start = llen + start - } - if stop < 0 { - stop = llen + stop - } - if start < 0 { - start = 0 - } - - if start > stop || start >= llen { - return [][]byte{}, nil - } - - if stop >= llen { - stop = llen - 1 - } - - limit := (stop - start) + 1 - headSeq += start - - v := make([][]byte, 0, limit) - - startKey := db.lEncodeListKey(key, headSeq) - rit := store.NewRangeLimitIterator(it, - &store.Range{ - Min: startKey, - Max: nil, - Type: store.RangeClose}, - &store.Limit{ - Offset: 0, - Count: int(limit)}) - - for ; rit.Valid(); rit.Next() { - v = append(v, rit.Value()) - } - - return v, nil -} - -func (db *DB) RPop(key []byte) ([]byte, error) { - return db.lpop(key, listTailSeq) -} - -func (db *DB) RPush(key []byte, arg1 []byte, args ...[]byte) (int64, error) { - var argss = [][]byte{arg1} - argss = append(argss, args...) - return db.lpush(key, listTailSeq, argss...) -} - -func (db *DB) LClear(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.listBatch - t.Lock() - defer t.Unlock() - - num := db.lDelete(t, key) - db.rmExpire(t, ListType, key) - - err := t.Commit() - return num, err -} - -func (db *DB) LMclear(keys ...[]byte) (int64, error) { - t := db.listBatch - t.Lock() - defer t.Unlock() - - for _, key := range keys { - if err := checkKeySize(key); err != nil { - return 0, err - } - - db.lDelete(t, key) - db.rmExpire(t, ListType, key) - - } - - err := t.Commit() - return int64(len(keys)), err -} - -func (db *DB) lFlush() (drop int64, err error) { - t := db.listBatch - t.Lock() - defer t.Unlock() - return db.flushType(t, ListType) -} - -func (db *DB) LExpire(key []byte, duration int64) (int64, error) { - if duration <= 0 { - return 0, errExpireValue - } - - return db.lExpireAt(key, time.Now().Unix()+duration) -} - -func (db *DB) LExpireAt(key []byte, when int64) (int64, error) { - if when <= time.Now().Unix() { - return 0, errExpireValue - } - - return db.lExpireAt(key, when) -} - -func (db *DB) LTTL(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return -1, err - } - - return db.ttl(ListType, key) -} - -func (db *DB) LPersist(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.listBatch - t.Lock() - defer t.Unlock() - - n, err := db.rmExpire(t, ListType, key) - if err != nil { - return 0, err - } - - err = t.Commit() - return n, err -} - -func (db *DB) LScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(LMetaType, key, count, inclusive, match) -} - -func (db *DB) lEncodeMinKey() []byte { - return db.lEncodeMetaKey(nil) -} - -func (db *DB) lEncodeMaxKey() []byte { - ek := db.lEncodeMetaKey(nil) - ek[len(ek)-1] = LMetaType + 1 - return ek -} diff --git a/vendor/gitea.com/lunny/nodb/t_set.go b/vendor/gitea.com/lunny/nodb/t_set.go deleted file mode 100644 index 0ff33e991..000000000 --- a/vendor/gitea.com/lunny/nodb/t_set.go +++ /dev/null @@ -1,601 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "time" - - "gitea.com/lunny/nodb/store" -) - -var errSetKey = errors.New("invalid set key") -var errSSizeKey = errors.New("invalid ssize key") - -const ( - setStartSep byte = ':' - setStopSep byte = setStartSep + 1 - UnionType byte = 51 - DiffType byte = 52 - InterType byte = 53 -) - -func checkSetKMSize(key []byte, member []byte) error { - if len(key) > MaxKeySize || len(key) == 0 { - return errKeySize - } else if len(member) > MaxSetMemberSize || len(member) == 0 { - return errSetMemberSize - } - return nil -} - -func (db *DB) sEncodeSizeKey(key []byte) []byte { - buf := make([]byte, len(key)+2) - - buf[0] = db.index - buf[1] = SSizeType - - copy(buf[2:], key) - return buf -} - -func (db *DB) sDecodeSizeKey(ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != SSizeType { - return nil, errSSizeKey - } - - return ek[2:], nil -} - -func (db *DB) sEncodeSetKey(key []byte, member []byte) []byte { - buf := make([]byte, len(key)+len(member)+1+1+2+1) - - pos := 0 - buf[pos] = db.index - pos++ - buf[pos] = SetType - pos++ - - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - - copy(buf[pos:], key) - pos += len(key) - - buf[pos] = setStartSep - pos++ - copy(buf[pos:], member) - - return buf -} - -func (db *DB) sDecodeSetKey(ek []byte) ([]byte, []byte, error) { - if len(ek) < 5 || ek[0] != db.index || ek[1] != SetType { - return nil, nil, errSetKey - } - - pos := 2 - keyLen := int(binary.BigEndian.Uint16(ek[pos:])) - pos += 2 - - if keyLen+5 > len(ek) { - return nil, nil, errSetKey - } - - key := ek[pos : pos+keyLen] - pos += keyLen - - if ek[pos] != hashStartSep { - return nil, nil, errSetKey - } - - pos++ - member := ek[pos:] - return key, member, nil -} - -func (db *DB) sEncodeStartKey(key []byte) []byte { - return db.sEncodeSetKey(key, nil) -} - -func (db *DB) sEncodeStopKey(key []byte) []byte { - k := db.sEncodeSetKey(key, nil) - - k[len(k)-1] = setStopSep - - return k -} - -func (db *DB) sFlush() (drop int64, err error) { - - t := db.setBatch - t.Lock() - defer t.Unlock() - - return db.flushType(t, SetType) -} - -func (db *DB) sDelete(t *batch, key []byte) int64 { - sk := db.sEncodeSizeKey(key) - start := db.sEncodeStartKey(key) - stop := db.sEncodeStopKey(key) - - var num int64 = 0 - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - t.Delete(it.RawKey()) - num++ - } - - it.Close() - - t.Delete(sk) - return num -} - -func (db *DB) sIncrSize(key []byte, delta int64) (int64, error) { - t := db.setBatch - sk := db.sEncodeSizeKey(key) - - var err error - var size int64 = 0 - if size, err = Int64(db.bucket.Get(sk)); err != nil { - return 0, err - } else { - size += delta - if size <= 0 { - size = 0 - t.Delete(sk) - db.rmExpire(t, SetType, key) - } else { - t.Put(sk, PutInt64(size)) - } - } - - return size, nil -} - -func (db *DB) sExpireAt(key []byte, when int64) (int64, error) { - t := db.setBatch - t.Lock() - defer t.Unlock() - - if scnt, err := db.SCard(key); err != nil || scnt == 0 { - return 0, err - } else { - db.expireAt(t, SetType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - - } - - return 1, nil -} - -func (db *DB) sSetItem(key []byte, member []byte) (int64, error) { - t := db.setBatch - ek := db.sEncodeSetKey(key, member) - - var n int64 = 1 - if v, _ := db.bucket.Get(ek); v != nil { - n = 0 - } else { - if _, err := db.sIncrSize(key, 1); err != nil { - return 0, err - } - } - - t.Put(ek, nil) - return n, nil -} - -func (db *DB) SAdd(key []byte, args ...[]byte) (int64, error) { - t := db.setBatch - t.Lock() - defer t.Unlock() - - var err error - var ek []byte - var num int64 = 0 - for i := 0; i < len(args); i++ { - if err := checkSetKMSize(key, args[i]); err != nil { - return 0, err - } - - ek = db.sEncodeSetKey(key, args[i]) - - if v, err := db.bucket.Get(ek); err != nil { - return 0, err - } else if v == nil { - num++ - } - - t.Put(ek, nil) - } - - if _, err = db.sIncrSize(key, num); err != nil { - return 0, err - } - - err = t.Commit() - return num, err - -} - -func (db *DB) SCard(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - sk := db.sEncodeSizeKey(key) - - return Int64(db.bucket.Get(sk)) -} - -func (db *DB) sDiffGeneric(keys ...[]byte) ([][]byte, error) { - destMap := make(map[string]bool) - - members, err := db.SMembers(keys[0]) - if err != nil { - return nil, err - } - - for _, m := range members { - destMap[String(m)] = true - } - - for _, k := range keys[1:] { - members, err := db.SMembers(k) - if err != nil { - return nil, err - } - - for _, m := range members { - if _, ok := destMap[String(m)]; !ok { - continue - } else if ok { - delete(destMap, String(m)) - } - } - // O - A = O, O is zero set. - if len(destMap) == 0 { - return nil, nil - } - } - - slice := make([][]byte, len(destMap)) - idx := 0 - for k, v := range destMap { - if !v { - continue - } - slice[idx] = []byte(k) - idx++ - } - - return slice, nil -} - -func (db *DB) SDiff(keys ...[]byte) ([][]byte, error) { - v, err := db.sDiffGeneric(keys...) - return v, err -} - -func (db *DB) SDiffStore(dstKey []byte, keys ...[]byte) (int64, error) { - n, err := db.sStoreGeneric(dstKey, DiffType, keys...) - return n, err -} - -func (db *DB) sInterGeneric(keys ...[]byte) ([][]byte, error) { - destMap := make(map[string]bool) - - members, err := db.SMembers(keys[0]) - if err != nil { - return nil, err - } - - for _, m := range members { - destMap[String(m)] = true - } - - for _, key := range keys[1:] { - if err := checkKeySize(key); err != nil { - return nil, err - } - - members, err := db.SMembers(key) - if err != nil { - return nil, err - } else if len(members) == 0 { - return nil, err - } - - tempMap := make(map[string]bool) - for _, member := range members { - if err := checkKeySize(member); err != nil { - return nil, err - } - if _, ok := destMap[String(member)]; ok { - tempMap[String(member)] = true //mark this item as selected - } - } - destMap = tempMap //reduce the size of the result set - if len(destMap) == 0 { - return nil, nil - } - } - - slice := make([][]byte, len(destMap)) - idx := 0 - for k, v := range destMap { - if !v { - continue - } - - slice[idx] = []byte(k) - idx++ - } - - return slice, nil - -} - -func (db *DB) SInter(keys ...[]byte) ([][]byte, error) { - v, err := db.sInterGeneric(keys...) - return v, err - -} - -func (db *DB) SInterStore(dstKey []byte, keys ...[]byte) (int64, error) { - n, err := db.sStoreGeneric(dstKey, InterType, keys...) - return n, err -} - -func (db *DB) SIsMember(key []byte, member []byte) (int64, error) { - ek := db.sEncodeSetKey(key, member) - - var n int64 = 1 - if v, err := db.bucket.Get(ek); err != nil { - return 0, err - } else if v == nil { - n = 0 - } - return n, nil -} - -func (db *DB) SMembers(key []byte) ([][]byte, error) { - if err := checkKeySize(key); err != nil { - return nil, err - } - - start := db.sEncodeStartKey(key) - stop := db.sEncodeStopKey(key) - - v := make([][]byte, 0, 16) - - it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - _, m, err := db.sDecodeSetKey(it.Key()) - if err != nil { - return nil, err - } - - v = append(v, m) - } - - it.Close() - - return v, nil -} - -func (db *DB) SRem(key []byte, args ...[]byte) (int64, error) { - t := db.setBatch - t.Lock() - defer t.Unlock() - - var ek []byte - var v []byte - var err error - - it := db.bucket.NewIterator() - defer it.Close() - - var num int64 = 0 - for i := 0; i < len(args); i++ { - if err := checkSetKMSize(key, args[i]); err != nil { - return 0, err - } - - ek = db.sEncodeSetKey(key, args[i]) - - v = it.RawFind(ek) - if v == nil { - continue - } else { - num++ - t.Delete(ek) - } - } - - if _, err = db.sIncrSize(key, -num); err != nil { - return 0, err - } - - err = t.Commit() - return num, err - -} - -func (db *DB) sUnionGeneric(keys ...[]byte) ([][]byte, error) { - dstMap := make(map[string]bool) - - for _, key := range keys { - if err := checkKeySize(key); err != nil { - return nil, err - } - - members, err := db.SMembers(key) - if err != nil { - return nil, err - } - - for _, member := range members { - dstMap[String(member)] = true - } - } - - slice := make([][]byte, len(dstMap)) - idx := 0 - for k, v := range dstMap { - if !v { - continue - } - slice[idx] = []byte(k) - idx++ - } - - return slice, nil -} - -func (db *DB) SUnion(keys ...[]byte) ([][]byte, error) { - v, err := db.sUnionGeneric(keys...) - return v, err -} - -func (db *DB) SUnionStore(dstKey []byte, keys ...[]byte) (int64, error) { - n, err := db.sStoreGeneric(dstKey, UnionType, keys...) - return n, err -} - -func (db *DB) sStoreGeneric(dstKey []byte, optType byte, keys ...[]byte) (int64, error) { - if err := checkKeySize(dstKey); err != nil { - return 0, err - } - - t := db.setBatch - t.Lock() - defer t.Unlock() - - db.sDelete(t, dstKey) - - var err error - var ek []byte - var v [][]byte - - switch optType { - case UnionType: - v, err = db.sUnionGeneric(keys...) - case DiffType: - v, err = db.sDiffGeneric(keys...) - case InterType: - v, err = db.sInterGeneric(keys...) - } - - if err != nil { - return 0, err - } - - for _, m := range v { - if err := checkSetKMSize(dstKey, m); err != nil { - return 0, err - } - - ek = db.sEncodeSetKey(dstKey, m) - - if _, err := db.bucket.Get(ek); err != nil { - return 0, err - } - - t.Put(ek, nil) - } - - var num = int64(len(v)) - sk := db.sEncodeSizeKey(dstKey) - t.Put(sk, PutInt64(num)) - - if err = t.Commit(); err != nil { - return 0, err - } - return num, nil -} - -func (db *DB) SClear(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.setBatch - t.Lock() - defer t.Unlock() - - num := db.sDelete(t, key) - db.rmExpire(t, SetType, key) - - err := t.Commit() - return num, err -} - -func (db *DB) SMclear(keys ...[]byte) (int64, error) { - t := db.setBatch - t.Lock() - defer t.Unlock() - - for _, key := range keys { - if err := checkKeySize(key); err != nil { - return 0, err - } - - db.sDelete(t, key) - db.rmExpire(t, SetType, key) - } - - err := t.Commit() - return int64(len(keys)), err -} - -func (db *DB) SExpire(key []byte, duration int64) (int64, error) { - if duration <= 0 { - return 0, errExpireValue - } - - return db.sExpireAt(key, time.Now().Unix()+duration) - -} - -func (db *DB) SExpireAt(key []byte, when int64) (int64, error) { - if when <= time.Now().Unix() { - return 0, errExpireValue - } - - return db.sExpireAt(key, when) - -} - -func (db *DB) STTL(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return -1, err - } - - return db.ttl(SetType, key) -} - -func (db *DB) SPersist(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.setBatch - t.Lock() - defer t.Unlock() - - n, err := db.rmExpire(t, SetType, key) - if err != nil { - return 0, err - } - err = t.Commit() - return n, err -} - -func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(SSizeType, key, count, inclusive, match) -} diff --git a/vendor/gitea.com/lunny/nodb/t_ttl.go b/vendor/gitea.com/lunny/nodb/t_ttl.go deleted file mode 100644 index f0007dc80..000000000 --- a/vendor/gitea.com/lunny/nodb/t_ttl.go +++ /dev/null @@ -1,195 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "time" - - "gitea.com/lunny/nodb/store" -) - -var ( - errExpMetaKey = errors.New("invalid expire meta key") - errExpTimeKey = errors.New("invalid expire time key") -) - -type retireCallback func(*batch, []byte) int64 - -type elimination struct { - db *DB - exp2Tx []*batch - exp2Retire []retireCallback -} - -var errExpType = errors.New("invalid expire type") - -func (db *DB) expEncodeTimeKey(dataType byte, key []byte, when int64) []byte { - buf := make([]byte, len(key)+11) - - buf[0] = db.index - buf[1] = ExpTimeType - buf[2] = dataType - pos := 3 - - binary.BigEndian.PutUint64(buf[pos:], uint64(when)) - pos += 8 - - copy(buf[pos:], key) - - return buf -} - -func (db *DB) expEncodeMetaKey(dataType byte, key []byte) []byte { - buf := make([]byte, len(key)+3) - - buf[0] = db.index - buf[1] = ExpMetaType - buf[2] = dataType - pos := 3 - - copy(buf[pos:], key) - - return buf -} - -func (db *DB) expDecodeMetaKey(mk []byte) (byte, []byte, error) { - if len(mk) <= 3 || mk[0] != db.index || mk[1] != ExpMetaType { - return 0, nil, errExpMetaKey - } - - return mk[2], mk[3:], nil -} - -func (db *DB) expDecodeTimeKey(tk []byte) (byte, []byte, int64, error) { - if len(tk) < 11 || tk[0] != db.index || tk[1] != ExpTimeType { - return 0, nil, 0, errExpTimeKey - } - - return tk[2], tk[11:], int64(binary.BigEndian.Uint64(tk[3:])), nil -} - -func (db *DB) expire(t *batch, dataType byte, key []byte, duration int64) { - db.expireAt(t, dataType, key, time.Now().Unix()+duration) -} - -func (db *DB) expireAt(t *batch, dataType byte, key []byte, when int64) { - mk := db.expEncodeMetaKey(dataType, key) - tk := db.expEncodeTimeKey(dataType, key, when) - - t.Put(tk, mk) - t.Put(mk, PutInt64(when)) -} - -func (db *DB) ttl(dataType byte, key []byte) (t int64, err error) { - mk := db.expEncodeMetaKey(dataType, key) - - if t, err = Int64(db.bucket.Get(mk)); err != nil || t == 0 { - t = -1 - } else { - t -= time.Now().Unix() - if t <= 0 { - t = -1 - } - // if t == -1 : to remove ???? - } - - return t, err -} - -func (db *DB) rmExpire(t *batch, dataType byte, key []byte) (int64, error) { - mk := db.expEncodeMetaKey(dataType, key) - if v, err := db.bucket.Get(mk); err != nil { - return 0, err - } else if v == nil { - return 0, nil - } else if when, err2 := Int64(v, nil); err2 != nil { - return 0, err2 - } else { - tk := db.expEncodeTimeKey(dataType, key, when) - t.Delete(mk) - t.Delete(tk) - return 1, nil - } -} - -func (db *DB) expFlush(t *batch, dataType byte) (err error) { - minKey := make([]byte, 3) - minKey[0] = db.index - minKey[1] = ExpTimeType - minKey[2] = dataType - - maxKey := make([]byte, 3) - maxKey[0] = db.index - maxKey[1] = ExpMetaType - maxKey[2] = dataType + 1 - - _, err = db.flushRegion(t, minKey, maxKey) - err = t.Commit() - return -} - -////////////////////////////////////////////////////////// -// -////////////////////////////////////////////////////////// - -func newEliminator(db *DB) *elimination { - eli := new(elimination) - eli.db = db - eli.exp2Tx = make([]*batch, maxDataType) - eli.exp2Retire = make([]retireCallback, maxDataType) - return eli -} - -func (eli *elimination) regRetireContext(dataType byte, t *batch, onRetire retireCallback) { - - // todo .. need to ensure exist - mapExpMetaType[expType] - - eli.exp2Tx[dataType] = t - eli.exp2Retire[dataType] = onRetire -} - -// call by outside ... (from *db to another *db) -func (eli *elimination) active() { - now := time.Now().Unix() - db := eli.db - dbGet := db.bucket.Get - - minKey := db.expEncodeTimeKey(NoneType, nil, 0) - maxKey := db.expEncodeTimeKey(maxDataType, nil, now) - - it := db.bucket.RangeLimitIterator(minKey, maxKey, store.RangeROpen, 0, -1) - for ; it.Valid(); it.Next() { - tk := it.RawKey() - mk := it.RawValue() - - dt, k, _, err := db.expDecodeTimeKey(tk) - if err != nil { - continue - } - - t := eli.exp2Tx[dt] - onRetire := eli.exp2Retire[dt] - if tk == nil || onRetire == nil { - continue - } - - t.Lock() - - if exp, err := Int64(dbGet(mk)); err == nil { - // check expire again - if exp <= now { - onRetire(t, k) - t.Delete(tk) - t.Delete(mk) - - t.Commit() - } - - } - - t.Unlock() - } - it.Close() - - return -} diff --git a/vendor/gitea.com/lunny/nodb/t_zset.go b/vendor/gitea.com/lunny/nodb/t_zset.go deleted file mode 100644 index 91e30049e..000000000 --- a/vendor/gitea.com/lunny/nodb/t_zset.go +++ /dev/null @@ -1,943 +0,0 @@ -package nodb - -import ( - "bytes" - "encoding/binary" - "errors" - "time" - - "gitea.com/lunny/nodb/store" -) - -const ( - MinScore int64 = -1<<63 + 1 - MaxScore int64 = 1<<63 - 1 - InvalidScore int64 = -1 << 63 - - AggregateSum byte = 0 - AggregateMin byte = 1 - AggregateMax byte = 2 -) - -type ScorePair struct { - Score int64 - Member []byte -} - -var errZSizeKey = errors.New("invalid zsize key") -var errZSetKey = errors.New("invalid zset key") -var errZScoreKey = errors.New("invalid zscore key") -var errScoreOverflow = errors.New("zset score overflow") -var errInvalidAggregate = errors.New("invalid aggregate") -var errInvalidWeightNum = errors.New("invalid weight number") -var errInvalidSrcKeyNum = errors.New("invalid src key number") - -const ( - zsetNScoreSep byte = '<' - zsetPScoreSep byte = zsetNScoreSep + 1 - zsetStopScoreSep byte = zsetPScoreSep + 1 - - zsetStartMemSep byte = ':' - zsetStopMemSep byte = zsetStartMemSep + 1 -) - -func checkZSetKMSize(key []byte, member []byte) error { - if len(key) > MaxKeySize || len(key) == 0 { - return errKeySize - } else if len(member) > MaxZSetMemberSize || len(member) == 0 { - return errZSetMemberSize - } - return nil -} - -func (db *DB) zEncodeSizeKey(key []byte) []byte { - buf := make([]byte, len(key)+2) - buf[0] = db.index - buf[1] = ZSizeType - - copy(buf[2:], key) - return buf -} - -func (db *DB) zDecodeSizeKey(ek []byte) ([]byte, error) { - if len(ek) < 2 || ek[0] != db.index || ek[1] != ZSizeType { - return nil, errZSizeKey - } - - return ek[2:], nil -} - -func (db *DB) zEncodeSetKey(key []byte, member []byte) []byte { - buf := make([]byte, len(key)+len(member)+5) - - pos := 0 - buf[pos] = db.index - pos++ - - buf[pos] = ZSetType - pos++ - - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - - copy(buf[pos:], key) - pos += len(key) - - buf[pos] = zsetStartMemSep - pos++ - - copy(buf[pos:], member) - - return buf -} - -func (db *DB) zDecodeSetKey(ek []byte) ([]byte, []byte, error) { - if len(ek) < 5 || ek[0] != db.index || ek[1] != ZSetType { - return nil, nil, errZSetKey - } - - keyLen := int(binary.BigEndian.Uint16(ek[2:])) - if keyLen+5 > len(ek) { - return nil, nil, errZSetKey - } - - key := ek[4 : 4+keyLen] - - if ek[4+keyLen] != zsetStartMemSep { - return nil, nil, errZSetKey - } - - member := ek[5+keyLen:] - return key, member, nil -} - -func (db *DB) zEncodeStartSetKey(key []byte) []byte { - k := db.zEncodeSetKey(key, nil) - return k -} - -func (db *DB) zEncodeStopSetKey(key []byte) []byte { - k := db.zEncodeSetKey(key, nil) - k[len(k)-1] = zsetStartMemSep + 1 - return k -} - -func (db *DB) zEncodeScoreKey(key []byte, member []byte, score int64) []byte { - buf := make([]byte, len(key)+len(member)+14) - - pos := 0 - buf[pos] = db.index - pos++ - - buf[pos] = ZScoreType - pos++ - - binary.BigEndian.PutUint16(buf[pos:], uint16(len(key))) - pos += 2 - - copy(buf[pos:], key) - pos += len(key) - - if score < 0 { - buf[pos] = zsetNScoreSep - } else { - buf[pos] = zsetPScoreSep - } - - pos++ - binary.BigEndian.PutUint64(buf[pos:], uint64(score)) - pos += 8 - - buf[pos] = zsetStartMemSep - pos++ - - copy(buf[pos:], member) - return buf -} - -func (db *DB) zEncodeStartScoreKey(key []byte, score int64) []byte { - return db.zEncodeScoreKey(key, nil, score) -} - -func (db *DB) zEncodeStopScoreKey(key []byte, score int64) []byte { - k := db.zEncodeScoreKey(key, nil, score) - k[len(k)-1] = zsetStopMemSep - return k -} - -func (db *DB) zDecodeScoreKey(ek []byte) (key []byte, member []byte, score int64, err error) { - if len(ek) < 14 || ek[0] != db.index || ek[1] != ZScoreType { - err = errZScoreKey - return - } - - keyLen := int(binary.BigEndian.Uint16(ek[2:])) - if keyLen+14 > len(ek) { - err = errZScoreKey - return - } - - key = ek[4 : 4+keyLen] - pos := 4 + keyLen - - if (ek[pos] != zsetNScoreSep) && (ek[pos] != zsetPScoreSep) { - err = errZScoreKey - return - } - pos++ - - score = int64(binary.BigEndian.Uint64(ek[pos:])) - pos += 8 - - if ek[pos] != zsetStartMemSep { - err = errZScoreKey - return - } - - pos++ - - member = ek[pos:] - return -} - -func (db *DB) zSetItem(t *batch, key []byte, score int64, member []byte) (int64, error) { - if score <= MinScore || score >= MaxScore { - return 0, errScoreOverflow - } - - var exists int64 = 0 - ek := db.zEncodeSetKey(key, member) - - if v, err := db.bucket.Get(ek); err != nil { - return 0, err - } else if v != nil { - exists = 1 - - if s, err := Int64(v, err); err != nil { - return 0, err - } else { - sk := db.zEncodeScoreKey(key, member, s) - t.Delete(sk) - } - } - - t.Put(ek, PutInt64(score)) - - sk := db.zEncodeScoreKey(key, member, score) - t.Put(sk, []byte{}) - - return exists, nil -} - -func (db *DB) zDelItem(t *batch, key []byte, member []byte, skipDelScore bool) (int64, error) { - ek := db.zEncodeSetKey(key, member) - if v, err := db.bucket.Get(ek); err != nil { - return 0, err - } else if v == nil { - //not exists - return 0, nil - } else { - //exists - if !skipDelScore { - //we must del score - if s, err := Int64(v, err); err != nil { - return 0, err - } else { - sk := db.zEncodeScoreKey(key, member, s) - t.Delete(sk) - } - } - } - - t.Delete(ek) - - return 1, nil -} - -func (db *DB) zDelete(t *batch, key []byte) int64 { - delMembCnt, _ := db.zRemRange(t, key, MinScore, MaxScore, 0, -1) - // todo : log err - return delMembCnt -} - -func (db *DB) zExpireAt(key []byte, when int64) (int64, error) { - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - if zcnt, err := db.ZCard(key); err != nil || zcnt == 0 { - return 0, err - } else { - db.expireAt(t, ZSetType, key, when) - if err := t.Commit(); err != nil { - return 0, err - } - } - return 1, nil -} - -func (db *DB) ZAdd(key []byte, args ...ScorePair) (int64, error) { - if len(args) == 0 { - return 0, nil - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - var num int64 = 0 - for i := 0; i < len(args); i++ { - score := args[i].Score - member := args[i].Member - - if err := checkZSetKMSize(key, member); err != nil { - return 0, err - } - - if n, err := db.zSetItem(t, key, score, member); err != nil { - return 0, err - } else if n == 0 { - //add new - num++ - } - } - - if _, err := db.zIncrSize(t, key, num); err != nil { - return 0, err - } - - //todo add binlog - err := t.Commit() - return num, err -} - -func (db *DB) zIncrSize(t *batch, key []byte, delta int64) (int64, error) { - sk := db.zEncodeSizeKey(key) - - size, err := Int64(db.bucket.Get(sk)) - if err != nil { - return 0, err - } else { - size += delta - if size <= 0 { - size = 0 - t.Delete(sk) - db.rmExpire(t, ZSetType, key) - } else { - t.Put(sk, PutInt64(size)) - } - } - - return size, nil -} - -func (db *DB) ZCard(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - sk := db.zEncodeSizeKey(key) - return Int64(db.bucket.Get(sk)) -} - -func (db *DB) ZScore(key []byte, member []byte) (int64, error) { - if err := checkZSetKMSize(key, member); err != nil { - return InvalidScore, err - } - - var score int64 = InvalidScore - - k := db.zEncodeSetKey(key, member) - if v, err := db.bucket.Get(k); err != nil { - return InvalidScore, err - } else if v == nil { - return InvalidScore, ErrScoreMiss - } else { - if score, err = Int64(v, nil); err != nil { - return InvalidScore, err - } - } - - return score, nil -} - -func (db *DB) ZRem(key []byte, members ...[]byte) (int64, error) { - if len(members) == 0 { - return 0, nil - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - var num int64 = 0 - for i := 0; i < len(members); i++ { - if err := checkZSetKMSize(key, members[i]); err != nil { - return 0, err - } - - if n, err := db.zDelItem(t, key, members[i], false); err != nil { - return 0, err - } else if n == 1 { - num++ - } - } - - if _, err := db.zIncrSize(t, key, -num); err != nil { - return 0, err - } - - err := t.Commit() - return num, err -} - -func (db *DB) ZIncrBy(key []byte, delta int64, member []byte) (int64, error) { - if err := checkZSetKMSize(key, member); err != nil { - return InvalidScore, err - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - ek := db.zEncodeSetKey(key, member) - - var oldScore int64 = 0 - v, err := db.bucket.Get(ek) - if err != nil { - return InvalidScore, err - } else if v == nil { - db.zIncrSize(t, key, 1) - } else { - if oldScore, err = Int64(v, err); err != nil { - return InvalidScore, err - } - } - - newScore := oldScore + delta - if newScore >= MaxScore || newScore <= MinScore { - return InvalidScore, errScoreOverflow - } - - sk := db.zEncodeScoreKey(key, member, newScore) - t.Put(sk, []byte{}) - t.Put(ek, PutInt64(newScore)) - - if v != nil { - // so as to update score, we must delete the old one - oldSk := db.zEncodeScoreKey(key, member, oldScore) - t.Delete(oldSk) - } - - err = t.Commit() - return newScore, err -} - -func (db *DB) ZCount(key []byte, min int64, max int64) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - minKey := db.zEncodeStartScoreKey(key, min) - maxKey := db.zEncodeStopScoreKey(key, max) - - rangeType := store.RangeROpen - - it := db.bucket.RangeLimitIterator(minKey, maxKey, rangeType, 0, -1) - var n int64 = 0 - for ; it.Valid(); it.Next() { - n++ - } - it.Close() - - return n, nil -} - -func (db *DB) zrank(key []byte, member []byte, reverse bool) (int64, error) { - if err := checkZSetKMSize(key, member); err != nil { - return 0, err - } - - k := db.zEncodeSetKey(key, member) - - it := db.bucket.NewIterator() - defer it.Close() - - if v := it.Find(k); v == nil { - return -1, nil - } else { - if s, err := Int64(v, nil); err != nil { - return 0, err - } else { - var rit *store.RangeLimitIterator - - sk := db.zEncodeScoreKey(key, member, s) - - if !reverse { - minKey := db.zEncodeStartScoreKey(key, MinScore) - - rit = store.NewRangeIterator(it, &store.Range{minKey, sk, store.RangeClose}) - } else { - maxKey := db.zEncodeStopScoreKey(key, MaxScore) - rit = store.NewRevRangeIterator(it, &store.Range{sk, maxKey, store.RangeClose}) - } - - var lastKey []byte = nil - var n int64 = 0 - - for ; rit.Valid(); rit.Next() { - n++ - - lastKey = rit.BufKey(lastKey) - } - - if _, m, _, err := db.zDecodeScoreKey(lastKey); err == nil && bytes.Equal(m, member) { - n-- - return n, nil - } - } - } - - return -1, nil -} - -func (db *DB) zIterator(key []byte, min int64, max int64, offset int, count int, reverse bool) *store.RangeLimitIterator { - minKey := db.zEncodeStartScoreKey(key, min) - maxKey := db.zEncodeStopScoreKey(key, max) - - if !reverse { - return db.bucket.RangeLimitIterator(minKey, maxKey, store.RangeClose, offset, count) - } else { - return db.bucket.RevRangeLimitIterator(minKey, maxKey, store.RangeClose, offset, count) - } -} - -func (db *DB) zRemRange(t *batch, key []byte, min int64, max int64, offset int, count int) (int64, error) { - if len(key) > MaxKeySize { - return 0, errKeySize - } - - it := db.zIterator(key, min, max, offset, count, false) - var num int64 = 0 - for ; it.Valid(); it.Next() { - sk := it.RawKey() - _, m, _, err := db.zDecodeScoreKey(sk) - if err != nil { - continue - } - - if n, err := db.zDelItem(t, key, m, true); err != nil { - return 0, err - } else if n == 1 { - num++ - } - - t.Delete(sk) - } - it.Close() - - if _, err := db.zIncrSize(t, key, -num); err != nil { - return 0, err - } - - return num, nil -} - -func (db *DB) zRange(key []byte, min int64, max int64, offset int, count int, reverse bool) ([]ScorePair, error) { - if len(key) > MaxKeySize { - return nil, errKeySize - } - - if offset < 0 { - return []ScorePair{}, nil - } - - nv := 64 - if count > 0 { - nv = count - } - - v := make([]ScorePair, 0, nv) - - var it *store.RangeLimitIterator - - //if reverse and offset is 0, count < 0, we may use forward iterator then reverse - //because store iterator prev is slower than next - if !reverse || (offset == 0 && count < 0) { - it = db.zIterator(key, min, max, offset, count, false) - } else { - it = db.zIterator(key, min, max, offset, count, true) - } - - for ; it.Valid(); it.Next() { - _, m, s, err := db.zDecodeScoreKey(it.Key()) - //may be we will check key equal? - if err != nil { - continue - } - - v = append(v, ScorePair{Member: m, Score: s}) - } - it.Close() - - if reverse && (offset == 0 && count < 0) { - for i, j := 0, len(v)-1; i < j; i, j = i+1, j-1 { - v[i], v[j] = v[j], v[i] - } - } - - return v, nil -} - -func (db *DB) zParseLimit(key []byte, start int, stop int) (offset int, count int, err error) { - if start < 0 || stop < 0 { - //refer redis implementation - var size int64 - size, err = db.ZCard(key) - if err != nil { - return - } - - llen := int(size) - - if start < 0 { - start = llen + start - } - if stop < 0 { - stop = llen + stop - } - - if start < 0 { - start = 0 - } - - if start >= llen { - offset = -1 - return - } - } - - if start > stop { - offset = -1 - return - } - - offset = start - count = (stop - start) + 1 - return -} - -func (db *DB) ZClear(key []byte) (int64, error) { - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - rmCnt, err := db.zRemRange(t, key, MinScore, MaxScore, 0, -1) - if err == nil { - err = t.Commit() - } - - return rmCnt, err -} - -func (db *DB) ZMclear(keys ...[]byte) (int64, error) { - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - for _, key := range keys { - if _, err := db.zRemRange(t, key, MinScore, MaxScore, 0, -1); err != nil { - return 0, err - } - } - - err := t.Commit() - - return int64(len(keys)), err -} - -func (db *DB) ZRange(key []byte, start int, stop int) ([]ScorePair, error) { - return db.ZRangeGeneric(key, start, stop, false) -} - -//min and max must be inclusive -//if no limit, set offset = 0 and count = -1 -func (db *DB) ZRangeByScore(key []byte, min int64, max int64, - offset int, count int) ([]ScorePair, error) { - return db.ZRangeByScoreGeneric(key, min, max, offset, count, false) -} - -func (db *DB) ZRank(key []byte, member []byte) (int64, error) { - return db.zrank(key, member, false) -} - -func (db *DB) ZRemRangeByRank(key []byte, start int, stop int) (int64, error) { - offset, count, err := db.zParseLimit(key, start, stop) - if err != nil { - return 0, err - } - - var rmCnt int64 - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - rmCnt, err = db.zRemRange(t, key, MinScore, MaxScore, offset, count) - if err == nil { - err = t.Commit() - } - - return rmCnt, err -} - -//min and max must be inclusive -func (db *DB) ZRemRangeByScore(key []byte, min int64, max int64) (int64, error) { - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - rmCnt, err := db.zRemRange(t, key, min, max, 0, -1) - if err == nil { - err = t.Commit() - } - - return rmCnt, err -} - -func (db *DB) ZRevRange(key []byte, start int, stop int) ([]ScorePair, error) { - return db.ZRangeGeneric(key, start, stop, true) -} - -func (db *DB) ZRevRank(key []byte, member []byte) (int64, error) { - return db.zrank(key, member, true) -} - -//min and max must be inclusive -//if no limit, set offset = 0 and count = -1 -func (db *DB) ZRevRangeByScore(key []byte, min int64, max int64, offset int, count int) ([]ScorePair, error) { - return db.ZRangeByScoreGeneric(key, min, max, offset, count, true) -} - -func (db *DB) ZRangeGeneric(key []byte, start int, stop int, reverse bool) ([]ScorePair, error) { - offset, count, err := db.zParseLimit(key, start, stop) - if err != nil { - return nil, err - } - - return db.zRange(key, MinScore, MaxScore, offset, count, reverse) -} - -//min and max must be inclusive -//if no limit, set offset = 0 and count = -1 -func (db *DB) ZRangeByScoreGeneric(key []byte, min int64, max int64, - offset int, count int, reverse bool) ([]ScorePair, error) { - - return db.zRange(key, min, max, offset, count, reverse) -} - -func (db *DB) zFlush() (drop int64, err error) { - t := db.zsetBatch - t.Lock() - defer t.Unlock() - return db.flushType(t, ZSetType) -} - -func (db *DB) ZExpire(key []byte, duration int64) (int64, error) { - if duration <= 0 { - return 0, errExpireValue - } - - return db.zExpireAt(key, time.Now().Unix()+duration) -} - -func (db *DB) ZExpireAt(key []byte, when int64) (int64, error) { - if when <= time.Now().Unix() { - return 0, errExpireValue - } - - return db.zExpireAt(key, when) -} - -func (db *DB) ZTTL(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return -1, err - } - - return db.ttl(ZSetType, key) -} - -func (db *DB) ZPersist(key []byte) (int64, error) { - if err := checkKeySize(key); err != nil { - return 0, err - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - n, err := db.rmExpire(t, ZSetType, key) - if err != nil { - return 0, err - } - - err = t.Commit() - return n, err -} - -func getAggregateFunc(aggregate byte) func(int64, int64) int64 { - switch aggregate { - case AggregateSum: - return func(a int64, b int64) int64 { - return a + b - } - case AggregateMax: - return func(a int64, b int64) int64 { - if a > b { - return a - } - return b - } - case AggregateMin: - return func(a int64, b int64) int64 { - if a > b { - return b - } - return a - } - } - return nil -} - -func (db *DB) ZUnionStore(destKey []byte, srcKeys [][]byte, weights []int64, aggregate byte) (int64, error) { - - var destMap = map[string]int64{} - aggregateFunc := getAggregateFunc(aggregate) - if aggregateFunc == nil { - return 0, errInvalidAggregate - } - if len(srcKeys) < 1 { - return 0, errInvalidSrcKeyNum - } - if weights != nil { - if len(srcKeys) != len(weights) { - return 0, errInvalidWeightNum - } - } else { - weights = make([]int64, len(srcKeys)) - for i := 0; i < len(weights); i++ { - weights[i] = 1 - } - } - - for i, key := range srcKeys { - scorePairs, err := db.ZRange(key, 0, -1) - if err != nil { - return 0, err - } - for _, pair := range scorePairs { - if score, ok := destMap[String(pair.Member)]; !ok { - destMap[String(pair.Member)] = pair.Score * weights[i] - } else { - destMap[String(pair.Member)] = aggregateFunc(score, pair.Score*weights[i]) - } - } - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - db.zDelete(t, destKey) - - for member, score := range destMap { - if err := checkZSetKMSize(destKey, []byte(member)); err != nil { - return 0, err - } - - if _, err := db.zSetItem(t, destKey, score, []byte(member)); err != nil { - return 0, err - } - } - - var num = int64(len(destMap)) - sk := db.zEncodeSizeKey(destKey) - t.Put(sk, PutInt64(num)) - - //todo add binlog - if err := t.Commit(); err != nil { - return 0, err - } - return num, nil -} - -func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, aggregate byte) (int64, error) { - - aggregateFunc := getAggregateFunc(aggregate) - if aggregateFunc == nil { - return 0, errInvalidAggregate - } - if len(srcKeys) < 1 { - return 0, errInvalidSrcKeyNum - } - if weights != nil { - if len(srcKeys) != len(weights) { - return 0, errInvalidWeightNum - } - } else { - weights = make([]int64, len(srcKeys)) - for i := 0; i < len(weights); i++ { - weights[i] = 1 - } - } - - var destMap = map[string]int64{} - scorePairs, err := db.ZRange(srcKeys[0], 0, -1) - if err != nil { - return 0, err - } - for _, pair := range scorePairs { - destMap[String(pair.Member)] = pair.Score * weights[0] - } - - for i, key := range srcKeys[1:] { - scorePairs, err := db.ZRange(key, 0, -1) - if err != nil { - return 0, err - } - tmpMap := map[string]int64{} - for _, pair := range scorePairs { - if score, ok := destMap[String(pair.Member)]; ok { - tmpMap[String(pair.Member)] = aggregateFunc(score, pair.Score*weights[i+1]) - } - } - destMap = tmpMap - } - - t := db.zsetBatch - t.Lock() - defer t.Unlock() - - db.zDelete(t, destKey) - - for member, score := range destMap { - if err := checkZSetKMSize(destKey, []byte(member)); err != nil { - return 0, err - } - if _, err := db.zSetItem(t, destKey, score, []byte(member)); err != nil { - return 0, err - } - } - - var num int64 = int64(len(destMap)) - sk := db.zEncodeSizeKey(destKey) - t.Put(sk, PutInt64(num)) - //todo add binlog - if err := t.Commit(); err != nil { - return 0, err - } - return num, nil -} - -func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { - return db.scan(ZSizeType, key, count, inclusive, match) -} diff --git a/vendor/gitea.com/lunny/nodb/tx.go b/vendor/gitea.com/lunny/nodb/tx.go deleted file mode 100644 index e56b4c0d3..000000000 --- a/vendor/gitea.com/lunny/nodb/tx.go +++ /dev/null @@ -1,113 +0,0 @@ -package nodb - -import ( - "errors" - "fmt" - - "gitea.com/lunny/nodb/store" -) - -var ( - ErrNestTx = errors.New("nest transaction not supported") - ErrTxDone = errors.New("Transaction has already been committed or rolled back") -) - -type Tx struct { - *DB - - tx *store.Tx - - logs [][]byte -} - -func (db *DB) IsTransaction() bool { - return db.status == DBInTransaction -} - -// Begin a transaction, it will block all other write operations before calling Commit or Rollback. -// You must be very careful to prevent long-time transaction. -func (db *DB) Begin() (*Tx, error) { - if db.IsTransaction() { - return nil, ErrNestTx - } - - tx := new(Tx) - - tx.DB = new(DB) - tx.DB.l = db.l - - tx.l.wLock.Lock() - - tx.DB.sdb = db.sdb - - var err error - tx.tx, err = db.sdb.Begin() - if err != nil { - tx.l.wLock.Unlock() - return nil, err - } - - tx.DB.bucket = tx.tx - - tx.DB.status = DBInTransaction - - tx.DB.index = db.index - - tx.DB.kvBatch = tx.newBatch() - tx.DB.listBatch = tx.newBatch() - tx.DB.hashBatch = tx.newBatch() - tx.DB.zsetBatch = tx.newBatch() - tx.DB.binBatch = tx.newBatch() - tx.DB.setBatch = tx.newBatch() - - return tx, nil -} - -func (tx *Tx) Commit() error { - if tx.tx == nil { - return ErrTxDone - } - - tx.l.commitLock.Lock() - err := tx.tx.Commit() - tx.tx = nil - - if len(tx.logs) > 0 { - tx.l.binlog.Log(tx.logs...) - } - - tx.l.commitLock.Unlock() - - tx.l.wLock.Unlock() - - tx.DB.bucket = nil - - return err -} - -func (tx *Tx) Rollback() error { - if tx.tx == nil { - return ErrTxDone - } - - err := tx.tx.Rollback() - tx.tx = nil - - tx.l.wLock.Unlock() - tx.DB.bucket = nil - - return err -} - -func (tx *Tx) newBatch() *batch { - return tx.l.newBatch(tx.tx.NewWriteBatch(), &txBatchLocker{}, tx) -} - -func (tx *Tx) Select(index int) error { - if index < 0 || index >= int(MaxDBNumber) { - return fmt.Errorf("invalid db index %d", index) - } - - tx.DB.index = uint8(index) - return nil -} diff --git a/vendor/gitea.com/lunny/nodb/util.go b/vendor/gitea.com/lunny/nodb/util.go deleted file mode 100644 index d5949a96e..000000000 --- a/vendor/gitea.com/lunny/nodb/util.go +++ /dev/null @@ -1,113 +0,0 @@ -package nodb - -import ( - "encoding/binary" - "errors" - "reflect" - "strconv" - "unsafe" -) - -var errIntNumber = errors.New("invalid integer") - -// no copy to change slice to string -// use your own risk -func String(b []byte) (s string) { - pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) - pstring.Data = pbytes.Data - pstring.Len = pbytes.Len - return -} - -// no copy to change string to slice -// use your own risk -func Slice(s string) (b []byte) { - pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - pstring := (*reflect.StringHeader)(unsafe.Pointer(&s)) - pbytes.Data = pstring.Data - pbytes.Len = pstring.Len - pbytes.Cap = pstring.Len - return -} - -func Int64(v []byte, err error) (int64, error) { - if err != nil { - return 0, err - } else if v == nil || len(v) == 0 { - return 0, nil - } else if len(v) != 8 { - return 0, errIntNumber - } - - return int64(binary.LittleEndian.Uint64(v)), nil -} - -func PutInt64(v int64) []byte { - var b []byte - pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - pbytes.Data = uintptr(unsafe.Pointer(&v)) - pbytes.Len = 8 - pbytes.Cap = 8 - return b -} - -func StrInt64(v []byte, err error) (int64, error) { - if err != nil { - return 0, err - } else if v == nil { - return 0, nil - } else { - return strconv.ParseInt(String(v), 10, 64) - } -} - -func StrInt32(v []byte, err error) (int32, error) { - if err != nil { - return 0, err - } else if v == nil { - return 0, nil - } else { - res, err := strconv.ParseInt(String(v), 10, 32) - return int32(res), err - } -} - -func StrInt8(v []byte, err error) (int8, error) { - if err != nil { - return 0, err - } else if v == nil { - return 0, nil - } else { - res, err := strconv.ParseInt(String(v), 10, 8) - return int8(res), err - } -} - -func StrPutInt64(v int64) []byte { - return strconv.AppendInt(nil, v, 10) -} - -func MinUInt32(a uint32, b uint32) uint32 { - if a > b { - return b - } else { - return a - } -} - -func MaxUInt32(a uint32, b uint32) uint32 { - if a > b { - return a - } else { - return b - } -} - -func MaxInt32(a int32, b int32) int32 { - if a > b { - return a - } else { - return b - } -} diff --git a/vendor/gitea.com/macaron/binding/README.md b/vendor/gitea.com/macaron/binding/README.md deleted file mode 100644 index aeaa57193..000000000 --- a/vendor/gitea.com/macaron/binding/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# 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/go.sum b/vendor/gitea.com/macaron/binding/go.sum deleted file mode 100644 index 56302b6a5..000000000 --- a/vendor/gitea.com/macaron/binding/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -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/captcha/go.mod b/vendor/gitea.com/macaron/captcha/go.mod deleted file mode 100644 index 6980787b9..000000000 --- a/vendor/gitea.com/macaron/captcha/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -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/cors/.drone.yml b/vendor/gitea.com/macaron/cors/.drone.yml deleted file mode 100644 index 39499f444..000000000 --- a/vendor/gitea.com/macaron/cors/.drone.yml +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index f1c181ec9..000000000 --- a/vendor/gitea.com/macaron/cors/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/gitea.com/macaron/cors/README.md b/vendor/gitea.com/macaron/cors/README.md deleted file mode 100644 index 5ef70e357..000000000 --- a/vendor/gitea.com/macaron/cors/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# 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 deleted file mode 100644 index 2d0613f2b..000000000 --- a/vendor/gitea.com/macaron/cors/cors.go +++ /dev/null @@ -1,169 +0,0 @@ -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.Resp.WriteHeader(200) // return response - return - } - } -} - -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 deleted file mode 100644 index 418aab88d..000000000 --- a/vendor/gitea.com/macaron/cors/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index e3bcd933d..000000000 --- a/vendor/gitea.com/macaron/cors/go.sum +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 1db7ea30e..000000000 --- a/vendor/gitea.com/macaron/csrf/.drone.yml +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 8405e89a0..000000000 --- a/vendor/gitea.com/macaron/csrf/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index 3295bd5f9..000000000 --- a/vendor/gitea.com/macaron/csrf/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# 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/go.mod b/vendor/gitea.com/macaron/csrf/go.mod deleted file mode 100644 index 946cb6aff..000000000 --- a/vendor/gitea.com/macaron/csrf/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index 7919c9f8c..000000000 --- a/vendor/gitea.com/macaron/csrf/go.sum +++ /dev/null @@ -1,83 +0,0 @@ -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/gzip/.drone.yml b/vendor/gitea.com/macaron/gzip/.drone.yml deleted file mode 100644 index 087a19664..000000000 --- a/vendor/gitea.com/macaron/gzip/.drone.yml +++ /dev/null @@ -1,24 +0,0 @@ -kind: pipeline -name: go1-14 - -steps: -- name: test - image: golang:1.14 - environment: - GOPROXY: https://goproxy.cn - commands: - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - ---- -kind: pipeline -name: go1-15 - -steps: -- name: test - image: golang:1.15 - 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/gzip/README.md b/vendor/gitea.com/macaron/gzip/README.md deleted file mode 100644 index e09c4a9d8..000000000 --- a/vendor/gitea.com/macaron/gzip/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# gzip - -Middleware gzip provides gzip comparess middleware for [Macaron](https://gitea.com/macaron/macaron). - -### Installation - - go get gitea.com/macaron/gzip - -## Getting Help - -- [API Reference](https://godoc.org/gitea.com/macaron/gzip) - -## Credits - -This package is a modified version of [go-macaron gzip](github.com/go-macaron/gzip). - -## 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/gzip/go.mod b/vendor/gitea.com/macaron/gzip/go.mod deleted file mode 100644 index 877ee7e37..000000000 --- a/vendor/gitea.com/macaron/gzip/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module gitea.com/macaron/gzip - -go 1.12 - -require ( - gitea.com/macaron/macaron v1.5.0 - github.com/klauspost/compress v1.9.2 - github.com/stretchr/testify v1.4.0 - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect - gopkg.in/ini.v1 v1.60.1 // indirect -) diff --git a/vendor/gitea.com/macaron/gzip/go.sum b/vendor/gitea.com/macaron/gzip/go.sum deleted file mode 100644 index b34055da3..000000000 --- a/vendor/gitea.com/macaron/gzip/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= -gitea.com/macaron/macaron v1.5.0 h1:TvWEcHw1/zaHlo0GTuKEukLh3A99+QsU2mjBrXLXjVQ= -gitea.com/macaron/macaron v1.5.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY= -github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -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/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/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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= -github.com/unknwon/com v1.0.1/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/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -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/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/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= -gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.60.1 h1:P5y5shSkb0CFe44qEeMBgn8JLow09MP17jlJHanke5g= -gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -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/gzip/gzip.go b/vendor/gitea.com/macaron/gzip/gzip.go deleted file mode 100644 index cd93e03f6..000000000 --- a/vendor/gitea.com/macaron/gzip/gzip.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package gzip - -import ( - "bufio" - "errors" - "fmt" - "io" - "net" - "net/http" - "regexp" - "strconv" - "strings" - "sync" - - "gitea.com/macaron/macaron" - "github.com/klauspost/compress/gzip" -) - -const ( - acceptEncodingHeader = "Accept-Encoding" - contentEncodingHeader = "Content-Encoding" - contentLengthHeader = "Content-Length" - contentTypeHeader = "Content-Type" - rangeHeader = "Range" - varyHeader = "Vary" -) - -const ( - // MinSize is the minimum size of content we will compress - MinSize = 1400 -) - -// noopClosers are io.Writers with a shim to prevent early closure -type noopCloser struct { - io.Writer -} - -func (noopCloser) Close() error { return nil } - -// WriterPool is a gzip writer pool to reduce workload on creation of -// gzip writers -type WriterPool struct { - pool sync.Pool - compressionLevel int -} - -// NewWriterPool creates a new pool -func NewWriterPool(compressionLevel int) *WriterPool { - return &WriterPool{pool: sync.Pool{ - // New will return nil, we'll manage the creation of new - // writers in the middleware - New: func() interface{} { return nil }, - }, - compressionLevel: compressionLevel} -} - -// Get a writer from the pool - or create one if not available -func (wp *WriterPool) Get(rw macaron.ResponseWriter) *gzip.Writer { - ret := wp.pool.Get() - if ret == nil { - ret, _ = gzip.NewWriterLevel(rw, wp.compressionLevel) - } else { - ret.(*gzip.Writer).Reset(rw) - } - return ret.(*gzip.Writer) -} - -// Put returns a writer to the pool -func (wp *WriterPool) Put(w *gzip.Writer) { - wp.pool.Put(w) -} - -var writerPool WriterPool - -// Options represents the configuration for the gzip middleware -type Options struct { - CompressionLevel int -} - -func validateCompressionLevel(level int) bool { - return level == gzip.DefaultCompression || - level == gzip.ConstantCompression || - (level >= gzip.BestSpeed && level <= gzip.BestCompression) -} - -func validate(options []Options) Options { - // Default to level 4 compression (Best results seem to be between 4 and 6) - opt := Options{CompressionLevel: 4} - if len(options) > 0 { - opt = options[0] - } - if !validateCompressionLevel(opt.CompressionLevel) { - opt.CompressionLevel = 4 - } - return opt -} - -// Middleware creates a macaron.Handler to proxy the response -func Middleware(options ...Options) macaron.Handler { - opt := validate(options) - writerPool = *NewWriterPool(opt.CompressionLevel) - regex := regexp.MustCompile(`bytes=(\d+)\-.*`) - - return func(ctx *macaron.Context) { - // If the client won't accept gzip or x-gzip don't compress - if !strings.Contains(ctx.Req.Header.Get(acceptEncodingHeader), "gzip") && - !strings.Contains(ctx.Req.Header.Get(acceptEncodingHeader), "x-gzip") { - return - } - - // If the client is asking for a specific range of bytes - don't compress - if rangeHdr := ctx.Req.Header.Get(rangeHeader); rangeHdr != "" { - - match := regex.FindStringSubmatch(rangeHdr) - if len(match) > 1 { - return - } - } - - // OK we should proxy the response writer - // We are still not necessarily going to compress... - proxyWriter := &ProxyResponseWriter{ - internal: ctx.Resp, - } - defer proxyWriter.Close() - - ctx.Resp = proxyWriter - ctx.MapTo(proxyWriter, (*http.ResponseWriter)(nil)) - - // Check if render middleware has been registered, - // if yes, we need to modify ResponseWriter for it as well. - if _, ok := ctx.Render.(*macaron.DummyRender); !ok { - ctx.Render.SetResponseWriter(proxyWriter) - } - - ctx.Next() - ctx.Resp = proxyWriter.internal - } -} - -// ProxyResponseWriter is a wrapped macaron ResponseWriter that may compress its contents -type ProxyResponseWriter struct { - writer io.WriteCloser - internal macaron.ResponseWriter - stopped bool - - code int - buf []byte -} - -// Header returns the header map -func (proxy *ProxyResponseWriter) Header() http.Header { - return proxy.internal.Header() -} - -// Status returns the status code of the response or 0 if the response has not been written. -func (proxy *ProxyResponseWriter) Status() int { - if proxy.code != 0 { - return proxy.code - } - return proxy.internal.Status() -} - -// Written returns whether or not the ResponseWriter has been written. -func (proxy *ProxyResponseWriter) Written() bool { - if proxy.code != 0 { - return true - } - return proxy.internal.Written() -} - -// Size returns the size of the response body. -func (proxy *ProxyResponseWriter) Size() int { - return proxy.internal.Size() -} - -// 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. -func (proxy *ProxyResponseWriter) Before(before macaron.BeforeFunc) { - proxy.internal.Before(before) -} - -// Write appends data to the proxied gzip writer. -func (proxy *ProxyResponseWriter) Write(b []byte) (int, error) { - // if writer is initialized, use the writer - if proxy.writer != nil { - return proxy.writer.Write(b) - } - - proxy.buf = append(proxy.buf, b...) - - var ( - contentLength, _ = strconv.Atoi(proxy.Header().Get(contentLengthHeader)) - contentType = proxy.Header().Get(contentTypeHeader) - contentEncoding = proxy.Header().Get(contentEncodingHeader) - ) - - // OK if an encoding hasn't been chosen, and content length > 1400 - // and content type isn't a compressed type - if contentEncoding == "" && - (contentLength == 0 || contentLength >= MinSize) && - (contentType == "" || !compressedContentType(contentType)) { - // If current buffer is less than the min size and a Content-Length isn't set, then wait - if len(proxy.buf) < MinSize && contentLength == 0 { - return len(b), nil - } - - // If the Content-Length is larger than minSize or the current buffer is larger than minSize, then continue. - if contentLength >= MinSize || len(proxy.buf) >= MinSize { - // if we don't know the content type, infer it - if contentType == "" { - contentType = http.DetectContentType(proxy.buf) - proxy.Header().Set(contentTypeHeader, contentType) - } - // If the Content-Type is not compressed - Compress! - if !compressedContentType(contentType) { - if err := proxy.startGzip(); err != nil { - return 0, err - } - return len(b), nil - } - } - } - // If we got here, we should not GZIP this response. - if err := proxy.startPlain(); err != nil { - return 0, err - } - return len(b), nil -} - -func (proxy *ProxyResponseWriter) startGzip() error { - // Set the content-encoding and vary headers. - proxy.Header().Set(contentEncodingHeader, "gzip") - proxy.Header().Set(varyHeader, acceptEncodingHeader) - - // if the Content-Length is already set, then calls to Write on gzip - // will fail to set the Content-Length header since its already set - // See: https://github.com/golang/go/issues/14975. - proxy.Header().Del(contentLengthHeader) - - // Write the header to gzip response. - if proxy.code != 0 { - proxy.internal.WriteHeader(proxy.code) - // Ensure that no other WriteHeader's happen - proxy.code = 0 - } - - // Initialize and flush the buffer into the gzip response if there are any bytes. - // If there aren't any, we shouldn't initialize it yet because on Close it will - // write the gzip header even if nothing was ever written. - if len(proxy.buf) > 0 { - // Initialize the GZIP response. - proxy.writer = writerPool.Get(proxy.internal) - - return proxy.writeBuf() - } - return nil -} - -func (proxy *ProxyResponseWriter) startPlain() error { - if proxy.code != 0 { - proxy.internal.WriteHeader(proxy.code) - proxy.code = 0 - } - proxy.stopped = true - proxy.writer = noopCloser{proxy.internal} - return proxy.writeBuf() -} - -func (proxy *ProxyResponseWriter) writeBuf() error { - if proxy.buf == nil { - return nil - } - - n, err := proxy.writer.Write(proxy.buf) - - // This should never happen (per io.Writer docs), but if the write didn't - // accept the entire buffer but returned no specific error, we have no clue - // what's going on, so abort just to be safe. - if err == nil && n < len(proxy.buf) { - err = io.ErrShortWrite - } - proxy.buf = nil - return err -} - -// WriteHeader will ensure that we have setup the writer before we write the header -func (proxy *ProxyResponseWriter) WriteHeader(code int) { - if proxy.code == 0 { - proxy.code = code - } -} - -// Close the writer -func (proxy *ProxyResponseWriter) Close() error { - if proxy.stopped { - return nil - } - - if proxy.writer == nil { - err := proxy.startPlain() - if err != nil { - return fmt.Errorf("GzipMiddleware: write to regular responseWriter at close gets error: %q", err.Error()) - } - } - - err := proxy.writer.Close() - - if poolWriter, ok := proxy.writer.(*gzip.Writer); ok { - writerPool.Put(poolWriter) - } - - proxy.writer = nil - proxy.stopped = true - return err -} - -// Flush the writer -func (proxy *ProxyResponseWriter) Flush() { - if proxy.writer == nil { - return - } - - if gw, ok := proxy.writer.(*gzip.Writer); ok { - gw.Flush() - } - - proxy.internal.Flush() -} - -// Push implements http.Pusher for HTTP/2 Push purposes -func (proxy *ProxyResponseWriter) Push(target string, opts *http.PushOptions) error { - pusher, ok := proxy.internal.(http.Pusher) - if !ok { - return errors.New("the ResponseWriter doesn't support the Pusher interface") - } - return pusher.Push(target, opts) -} - -// Hijack implements http.Hijacker. If the underlying ResponseWriter is a -// Hijacker, its Hijack method is returned. Otherwise an error is returned. -func (proxy *ProxyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hijacker, ok := proxy.internal.(http.Hijacker) - if !ok { - return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") - } - return hijacker.Hijack() -} - -// verify Hijacker interface implementation -var _ http.Hijacker = &ProxyResponseWriter{} - -func compressedContentType(contentType string) bool { - switch contentType { - case "application/zip": - return true - case "application/x-gzip": - return true - case "application/gzip": - return true - default: - return false - } -} diff --git a/vendor/gitea.com/macaron/i18n/.drone.yml b/vendor/gitea.com/macaron/i18n/.drone.yml deleted file mode 100644 index 72eebbb49..000000000 --- a/vendor/gitea.com/macaron/i18n/.drone.yml +++ /dev/null @@ -1,24 +0,0 @@ -kind: pipeline -name: golang-1-14 - -steps: -- name: test - image: golang:1.14 - environment: - GOPROXY: https://goproxy.cn - commands: - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - ---- -kind: pipeline -name: golang-1-15 - -steps: -- name: test - image: golang:1.15 - 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/.gitignore b/vendor/gitea.com/macaron/i18n/.gitignore deleted file mode 100644 index a09c56df5..000000000 --- a/vendor/gitea.com/macaron/i18n/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.idea diff --git a/vendor/gitea.com/macaron/i18n/LICENSE b/vendor/gitea.com/macaron/i18n/LICENSE deleted file mode 100644 index 8405e89a0..000000000 --- a/vendor/gitea.com/macaron/i18n/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index bff89d9b6..000000000 --- a/vendor/gitea.com/macaron/i18n/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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 deleted file mode 100644 index 59336d341..000000000 --- a/vendor/gitea.com/macaron/i18n/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module gitea.com/macaron/i18n - -go 1.11 - -require ( - gitea.com/macaron/macaron v1.5.0 - github.com/stretchr/testify v1.4.0 - 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 deleted file mode 100644 index 73db18b25..000000000 --- a/vendor/gitea.com/macaron/i18n/go.sum +++ /dev/null @@ -1,52 +0,0 @@ -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= -gitea.com/macaron/macaron v1.5.0 h1:TvWEcHw1/zaHlo0GTuKEukLh3A99+QsU2mjBrXLXjVQ= -gitea.com/macaron/macaron v1.5.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -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/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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -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/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= -github.com/unknwon/com v1.0.1/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/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/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 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag= -gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -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/i18n/i18n.go b/vendor/gitea.com/macaron/i18n/i18n.go deleted file mode 100644 index 3624aaee5..000000000 --- a/vendor/gitea.com/macaron/i18n/i18n.go +++ /dev/null @@ -1,242 +0,0 @@ -// 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 provides an Internationalization and Localization middleware for Macaron applications. -package i18n - -import ( - "fmt" - "log" - "os" - "path" - "strings" - - "gitea.com/macaron/macaron" - "github.com/unknwon/i18n" - "golang.org/x/text/language" -) - -const _VERSION = "0.4.0" - -func Version() string { - return _VERSION -} - -// isFile returns true if given path is a file, -// or returns false when it's a directory or does not exist. -func isFile(filePath string) bool { - f, e := os.Stat(filePath) - if e != nil { - return false - } - return !f.IsDir() -} - -// 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 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 { - log.Printf("ERROR: failed to set message file(%s) for language %s: %v", lang, opt.Names[i], 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 - // Set the Secure flag on the `lang` cookie. Default is disabled. - Secure bool - // Set the HTTP Only flag on the `lang` cookie. Default is disabled. - CookieHttpOnly bool -} - -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, opt.Secure, opt.CookieHttpOnly) - } - - 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{Locale: i18n.Locale{Lang: 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 + path.Clean(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 deleted file mode 100644 index 4db3bab53..000000000 --- a/vendor/gitea.com/macaron/inject/.drone.yml +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 37ec93a14..000000000 --- a/vendor/gitea.com/macaron/inject/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index 5ed58bae5..000000000 --- a/vendor/gitea.com/macaron/inject/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# 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 deleted file mode 100644 index 7d885b700..000000000 --- a/vendor/gitea.com/macaron/inject/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 6a9ccb166..000000000 --- a/vendor/gitea.com/macaron/inject/go.sum +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 1c1f98eaa..000000000 --- a/vendor/gitea.com/macaron/inject/inject.go +++ /dev/null @@ -1,262 +0,0 @@ -// 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 deleted file mode 100644 index 06a3e7d85..000000000 --- a/vendor/gitea.com/macaron/macaron/.drone.yml +++ /dev/null @@ -1,12 +0,0 @@ -kind: pipeline -name: default - -steps: -- name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - 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 deleted file mode 100644 index fc5aca3e4..000000000 --- a/vendor/gitea.com/macaron/macaron/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -macaron.sublime-project -macaron.sublime-workspace -.idea diff --git a/vendor/gitea.com/macaron/macaron/LICENSE b/vendor/gitea.com/macaron/macaron/LICENSE deleted file mode 100644 index c8a16eb2e..000000000 --- a/vendor/gitea.com/macaron/macaron/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index 34830c8b1..000000000 --- a/vendor/gitea.com/macaron/macaron/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Macaron - -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/go-macaron/macaron/Go?logo=github&style=for-the-badge)](https://github.com/go-macaron/macaron/actions?query=workflow%3AGo) -[![codecov](https://img.shields.io/codecov/c/github/go-macaron/macaron/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-macaron/macaron) -[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/gopkg.in/macaron.v1?tab=doc) -[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/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 deleted file mode 100644 index cf545eb46..000000000 --- a/vendor/gitea.com/macaron/macaron/context.go +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright 2014 The Macaron Authors -// Copyright 2020 The Gitea 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 -} - -// Body returns a RequestBody for the 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) - -// Invoke implements inject.FastInvoker - in the context of a func(ctx *Context) this simply calls the function -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 (ctx *Context) handler() Handler { - if ctx.index < len(ctx.handlers) { - return ctx.handlers[ctx.index] - } - if ctx.index == len(ctx.handlers) { - return ctx.action - } - panic("invalid index for context handler") -} - -// Next runs the next handler in the context chain -func (ctx *Context) Next() { - ctx.index++ - ctx.run() -} - -// Written returns whether the context response has been written to -func (ctx *Context) Written() bool { - return ctx.Resp.Written() -} - -func (ctx *Context) run() { - for ctx.index <= len(ctx.handlers) { - vals, err := ctx.Invoke(ctx.handler()) - if err != nil { - panic(err) - } - ctx.index++ - - // if the handler returned something, write it to the http response - if len(vals) > 0 { - ev := ctx.GetVal(reflect.TypeOf(ReturnHandler(nil))) - handleReturn := ev.Interface().(ReturnHandler) - handleReturn(ctx, vals) - } - - if ctx.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...) -} - -// Redirect sends a redirect response -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) -} - -// MaxMemory is the 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] -} - -// AllParams returns all params. -func (ctx *Context) AllParams() Params { - return ctx.params -} - -// 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) - case func(*http.Cookie): - v(&cookie) - } - } - - cookie.Path = "/" - if len(others) > 1 { - if v, ok := others[1].(string); ok && len(v) > 0 { - cookie.Path = v - } else if v, ok := others[1].(func(*http.Cookie)); ok { - v(&cookie) - } - } - - if len(others) > 2 { - if v, ok := others[2].(string); ok && len(v) > 0 { - cookie.Domain = v - } else if v, ok := others[1].(func(*http.Cookie)); ok { - v(&cookie) - } - } - - if len(others) > 3 { - switch v := others[3].(type) { - case bool: - cookie.Secure = v - case func(*http.Cookie): - v(&cookie) - default: - if others[3] != nil { - cookie.Secure = true - } - } - } - - if len(others) > 4 { - if v, ok := others[4].(bool); ok && v { - cookie.HttpOnly = true - } else if v, ok := others[1].(func(*http.Cookie)); ok { - v(&cookie) - } - } - - if len(others) > 5 { - if v, ok := others[5].(time.Time); ok { - cookie.Expires = v - cookie.RawExpires = v.Format(time.UnixDate) - } else if v, ok := others[1].(func(*http.Cookie)); ok { - v(&cookie) - } - } - - if len(others) > 6 { - for _, other := range others[6:] { - if v, ok := other.(func(*http.Cookie)); ok { - v(&cookie) - } - } - } - - 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 deleted file mode 100644 index 4741454ed..000000000 --- a/vendor/gitea.com/macaron/macaron/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module gitea.com/macaron/macaron - -go 1.11 - -require ( - gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a - github.com/smartystreets/assertions v1.0.1 // indirect - github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 - github.com/unknwon/com v1.0.1 - 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 deleted file mode 100644 index bd41a7915..000000000 --- a/vendor/gitea.com/macaron/macaron/go.sum +++ /dev/null @@ -1,31 +0,0 @@ -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/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 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/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 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= -github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= -github.com/unknwon/com v1.0.1/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 deleted file mode 100644 index 34178d78a..000000000 --- a/vendor/gitea.com/macaron/macaron/logger.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 deleted file mode 100644 index 5326093dc..000000000 --- a/vendor/gitea.com/macaron/macaron/macaron.go +++ /dev/null @@ -1,327 +0,0 @@ -// 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. - // You must set this value yourself - 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")) -} - -// 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 deleted file mode 100644 index 399759769a8d7f5549d550baccdd2ea5716e2bac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88924 zcmd?P1CwRlwk^8SuC#62&aAX;+jeH9ZL`ugDs9`gZCkItv(LR}zy0q01#d>oh`CyC zt&ff|VvUJ#d08`n;k@ehD#V0BQB>|9G;ArzTMU< zy&5u7C-uwhhPg{mKQzF`FrfR@*rkX7k!skao5x>P;apy7EFGD@cCT*Z`6zwzj(EPS z+>%{PXA4GUYiwIzRMK~%0iH-Sf8*A7C941$Y`_H$cPr(t2>^FsfQGHid%O^9-GqTX zSaO)iiwkQfpd2eY>J#?&oB+GA?hwxB^zo~ARqkSRet%*KRAv*A!{U-+DdC9;*dM9W z#-Gm!-}^nB7L%j>G?_U)=(oWqDhek_P(k75?ZG_$>Tz_&&_TY7yS^A~ReX(8ER*Ng@f$vjc7*M&v>=hv z(GzT}u_I|d4Q9c1kaaIGQXGVHE;Nfj9Ro0pJ{*o8vpvWb0U*dv904Q+giajTLICuS zAm7U0Dag12$V6z9;236~UvxkB86BB?A>6Kyt+VZ!647fo+%@-_> zLo0+&|G9*KtqJKNBq+~;0x~ER9*2-1@H#_Qia`}vo+l*F`#|iB)dr~(_?8PXL%;(5 z3FIAwm{Lt#(@;;8{ZLjN6myl?^)lKY(eAu(cFi=i+uy23D)aNgcKNo z1eq08hs3@W{u!rE0)$9t6;D(MTqcqc_oEQIHCjC0;Q$0YG|wOn18oR%KU5=X*HG19 z$xxDEG0iAVRT}+>%@l#z|23e$cn)QH_ngY z9?U%)d!H5zZ6;gRYS3o*<*0VQ^M1HpvRn5Ksx6f30Qg>~8^d3OUC_QbzIfZQ_(R>~ zaZs9|RsryYDFh_eB+3X_kjIdpL5O`U3jU>X&O{*y9s}BjlyxyHf*le#WU`4U5+Nk$ ziX@cj4gSg!mP9&aykwn(iKAS`VvZOZ0=jb5Bsm0nr0@y+@<|kAC~}fGB0pDUIVD>K zJOn(1iWK2WZ5Pzb)7r8$d2gorGmA6IKOs7L!(l3DVMXKXXICb$M08a7~V+r$Skx?G(DP28YS9u8hi~(4KQ_^ zhO^qY{6;SkGxqxz}h&7;PuGTLp|?n`g(44v9dw07C{>8Br0MDJwpj1at` z?xCGOo>!b*DcXa)7ZJ}-c3Y-yqxK=ly~yk4B}%rlG^Qx0;HC;-V9{^T7icjYXB~E1 za2(_5jOn8E-0kxgRMwOCHP7EBWR|0kT?cFiHdlC8KU?0Z;icf?#Uh83N)nH;reSo$ zQft)W)oPj(o(-=puS@v2_`><3`H;I@yDGftUkpBhKV9E8@0V|m-K@vdfNl=>F4xFv=`8Te|`wL2J=9%!s?*DIbqPzuvC&=c(kav z`?$M-(i(~g3k|f3$BxX6h($)DNujxkj*1P6EQ-m9c!{KnNTsUNXw)BwMrlS76!W6t zW21VJxH^_S&uvPD?qBRd?_bd|Yp2v-tT{FwmW@wW$LaNII8fnXzk3b_ikUHazr*GEG?TYoC- zcG6C1{jeUiMzy}&G_9|)Tu&v^oqaSM-;3CjBi%?i-`4O}d)=IP3cBnpdMbK%+i`_- z+t@s9yH`&))X-g>sov44I-I!6ziVz+yXaCi&~ng+w}>^4ZD1<>(1)_qp>Z9Y5#e2aO8%Mi)HI_Rp_#X|u@v4^$Y$Xs?X`XCH=l(&g-gr-`}@~J)Khv! z{g|25bo>-*=5Nj%H=nzoL&3L^;pm}UYR-IZ4F_{s)wR`IH(Mx#k*(rJ#J{G5q*EXG}_Vu4B`&BVrVBW`XI`8ybPi@|ZG2dC( zAd%p2S9D!o-?|&Tr`}tivk+zQy8IKK9G~W&Mi=|}HLygrCpqFOHVe(#wQ2!ZWh_55w`N7jiFh z$vM&dZoi;D?%c_2OpmsogYMs%xHNqJ?kS<{#WkD&02t(dP9Q)=CI$ciGHIc#?yN2& z&1Ga~LuX)YXJ|s_Ze#zK4FK@CbN#)vF>y8^aJR9xb>ed8CHgN0*Wc@ZV0t2g|Drfs z@e--a$P)PbB6DYFdoFrBdxPEJmG1}1tYCfdIYS|<-%X9IUyTPNawIr(3HgiV}`94+jfE$nOw{_$&IXy@Y0 zOGNa~K>z#r?>KGj|Mx()PXC?N-~8y^4eaR|=@{t$PaR zWdB(HCHt>p{3}MDzZK<@u`{+X^$<32HsNDpU}T|XWT0haR%T$~VrAlDVdkO#56=H& z_+M%vM-u~QJ4ac(~@1p;c zKa>BAhLMeng^`Pa?ce18;{2QXpTcm-Ia-+f?bttk;bY{X|NmhB$=6Ucak8^^`7b^@ z(|-&89sEzh|L|38Eu8uOm*hX-|5T~}kDc#-5A*NfzX|^76qk^lwVk7qy@8R*KWY3Y z^q*{b3wIN1bzut|6I-YM%AA3L;lEY?Y5G4(HU96?e>45Jl!yMG3IA^k;9sljztF$S zix2v5lm6FI<%3Q-Ui$<91OO7kg39hd7v2zlDuavP8+V@T&EBaoEWK!hA;BUF2r#9! z6l}u12`l-t^x;skzx0rz!?8ZG2Qgp_jcf=AfcpXh;FOiQXvUwqYM$@Dvs$m|>@4#0 zR|mOkubaw_r#`MY&89L9q+zhYSfI^OW+-wM1Pc5_fue!`{|U@YQV=-TzgaF`j-iMb zC4I42p~Wz#N1kyx$py2gBt!L+$w<0vqz@)>z>+hYQ9XkhOy9?Zt%ZvM1p`Y$%$MdV z?ChW*s8bD+B`1eR$r$$Eskdz>)#s6tmdQjF`AJdkGWf|r=R&+7h3wfWd2o(V?y|0{ z_bFCJ)%}s683ont0JnzlH4{6ScOK>@S}$fj<5nUd1*ED|`dO&!X3Lw^LlwwzQEE^@ zh6_zd>Q^VHBnS?Qj^-x{lS!d@|I9%#){unEw~jmDNZb#1=b$V;a3{cuB-7xDha5mU zT%1I7-A)HeO5{oUb5x`_clUTcXt*s=7ClMBYCT_>G%li@F_ec3{n=)P16SL2{+pZT zQ5LEirKQLbdR692h;w52Gw8>U~77&fCqlgDjarsu zZHhaS27U|~wRe|*)#@>A^aLx9_~RzG{Z(a$_MZ0Nq1)o>!1cY9%DnWf{1!X6x816E z0nnagKjaT5SS^)*<-pRZTce?UeRQWBTg3@M`E`W@$5k477$GViTRnHab}P142O{;$I))4IDp;(`Vr*E zjVT}>q(b}!8)?!lr3(|Zl)@KM>}l#|k;7Fwnu=|ozx<*&>SPY3S5~xBsHn4oG%ay3 z;v3q*|5d{QPvm7rXl*elY9Ilqcfi6R4And8I_)q<^qzgs;7t<64_PQfxlv=r!UrM) zu*eXdRXYE+yzD*ysFRHh2^W-gObT$lKx zeZ9nL_Hf=UxFooQk_zVm5JF%Dt0@bkG|;(_V#qXD(SO{LDitNgBf}&Fzz#S85D{c3 zrvM_K&}H%hYLiSKEfwY*B_?G9IdMJb8w;FS$eA9iqJsQi3cx9+K*pl>-k!B4iZr7M z2EGE~#Z5rk{4t`)_$;0QIUNoPIT5_+#DU6^nP9f_jnKKp{%T|vZ5OZ47n(Xvy!_h{ z4slee=1bLy2_L(S#n^H<3>$^W)99pipiIlR6NdGJK!SPd5y3=4orV|JOs zMeVPm&fo>mqQOC!Pr-^o+DJdU;^L%#!F}0_P@s5yZ=+u3)MAIcEc6{s2A+hi$}N{` z0O(VPJj#Mj4TnEnlju~%s<Z4qFJ^)X|W0cTPySi zOn>B)>I66j3~xE$Mo7vcg@YXwcOX0cL3vQ+7KqKt;{fphNI!63VtY;;QDyy~#xX9? z6aJV=fAWN>?q(GPQH+NaLVD9mXlz>aB@Wo z+8hyt(DGpc^%yb}#sCVTA(>zhR`)h0EIHbb!fKmC^dgYo$pf`;fh+1i>gqZQNqX1I z`DS9iU7&CHUiE5%RsF9;fl{TMqcbU8fy)f;ADF*b{|uBSN-5>r^D(P?4mx`tK}ZJo z87LkMh~&a1t%EX>>>?uEwAcwpW^h;A>W)k_`+HNXTxyj*)eJlD*rCL#$L(NvjtrhKn^r0y(!N*qD z4-1O8t6qK?15L{9P+-bDW0O{AYH+Dg;LoDXso!20WM>a;Rtfpes&i-h+4@OfKLugs zQUK!d;zKG2S;9PgK_0jW@KhF9RbgQE39tZQ+y`DHTe5!nHVE%LWu7I3SYfvTqP)po zd3&jm{pt!k$LkNvLvr1wk6!QtP{MmLcqZq=*uyajMyjq?mnPR9Ug83Ev|0gEqVqCWOmt7H}oUZkBS(SUtv^z#G?X) z!QY)mT)$@uVK+#u5z;+C&L)K&Qd1D?H)I0;I6!VJ+jwjnN*91=bA{R;1WFy+DJhKkc1* z-JBd1w$_y*cwMkEg6>v09~~5dYf*OmAQAgl+9f{$%9NB!{4SGoZ}Du257dH5bm3a{ zkC$Hn7*Qf9!#sU`i8Ine-o4a;j+oR4AovA8nE*cv-#~Mg0H>blv}W4$sGw%C{=8Uk zyy=X!v$4nX>&{=;mZ#h8#h~JTnDJ5cs05a>$u@n!N9h=cl6CJu#hp&CTg&Fqf7}=i z=4P1uW%{^87mUoIq=23hsN$}Z+bu?Nj(%Y9=+D^KZ+f)NT65_zrG=c}tTIh91ral% zg$swj29!rR=>q7fg1=|zQ=_0Z8QsQQbVajtUDqb0?;1fykgi_?ZhbrLb!Ukhwf`n8 z`SCIw)AhNJUX*-8i?RYyYOAi`n>U*K?xJYMLte>T#}*qqh!oxe>i};_wui#dhT^AK z8;B4|os#Gd>k_BSWS}V>H3(3wvZ<4QnfDv6cXk_WTFm)wawb*`^}svDN6q{S78xcr zRxk5Iir%fp1Qb_m0OsNk_o^652O5Kf4-t-S2X~@zUk2iDRpPmSeYNSbE-W zEk3G9rTgyUJuq?lAE~kF`qp;a6Frr}^=9hXYEqSHp)lL*`Lx7%lJ0Vxl%ahAyFV~Y zN#X*4HTt472R1kGV8B$pG_|%h8|+u1pUyrP)06WY!;N;wRoQGLd%wcKv$>R+3tUs3 za-;WD@;U7i?5&_+&7O5zytg_B(X~BBHCBUzQ_m(-oZvKCTP4E0dl5Kl>_Mr0T9_f@ zkHiQqoh%Xj;^k%8zS00-X5#}i6)Fw$6EGXf84-=*N%PK_40)l#7-6nFDpqDS5 zl(|4l&t#kMavT<0JQhd;I4<%!?Hvvciv(NKQ7Q3TK_hFwd@@?4&Vo=*XOn@UN|ozk zos@ne36N>gs1^^4M>Mjx6B~S6>PfAy!|46)PNMx_KFxENf0Q}cB)BXt;tot)tF+_n zQ?|wp{IeS{ZH$kf@ zO@`*w%8ar5`QESnvZz_tqabSX+X;ePgh0d!YFw>qj;19%uGWAWlT(p~(tqT4*~0f= z!=BVt*CUdb?$x4al~;e!VW>GMa2W6o$>=G-|}61A;BahmUZRe zWswDqy)rNd)tHh(NyRaO0+Mlv{t!*qYOc|2im2Scb(Wr5r`HrjHrlF#De7F9W%Z33)$iCsyFig+8}p zKXCQ!bmMJ$Ab&`YS>*x#R37g`8MpqG`>(WBiz+)S;`vJs4yr%*2VgUToX3f`Q0A*5 zzZa=6ay7ym*e@Si*K@M?ud7-sCsc}-#NRV2l3wt{{8C|dk~z7@S4hE9r{3pPmS~Py z!Xo?kYDb=5KPl7P1@(`6z*JK6bJavdPFnWl-6euGGy?bzVv3Q%eJ8e7-q4@M4Sdc( z4#?Un8&Fb^HDCBpW&r2!@|W#Jh^CcV%YFsqsPx)oUx_@V!GMov5y$;lAm@llXB!HaA8fOf2 zk@e5{NE2_s6igenG-t}C!piv&U;W$N1|F|(Sv~i(bowQ1lcl_2k@z&J|Czp#%1!_c zJW3$-Vvr!xVLlEyawl!_m;J*Q`t-E~a|GJbj~=n|1o`FBmz*y z0hPRZAsC|K3${q(0gKW`1DXA+i0vxGnsl$A#ys z-P$LZ62(rX z1XSgQ`FB%(>V;{cu1Fgx=CEFC#C0p3(^*;{u)Nb3 zd8L!iixXG5?YkNocjTlLrMCwkRG*1~Z+llh6oz7=+)QTXaWy!LQ#^FZi(Zq3j zs;l|$=b_Ai`GDH2X9x=ijt`R_)yNiFV0vihD1mO1+4DmpZc|r~t7PuAf_XLVH81j6 zy?L85`p2EWTaO>IIoA&?^ZGYuhc3ty`7gpFClu8-Hlbm|sk8bvb--#11(X2)J@719 zS1xOsCB@|f+tT;@y|NPiV6k5Yw-WWsr%T%`I8vVQ{PKu9&W=jVURGBRd7MRLozgxH zQ%kEhLkKDeafP8SZZe0}Vq0y-H#KEjpcC8cgA5$Zf8?hZBE82Qv8fz$00=u;0f;Me z{W&vTurPCKlGM+Y_b9_(W{-`Wyzd{gam)4iRo6j`7C1~q&!U3o1%B!sUkw`&57MY6 zW)eqX%eWE4cE6e1-064^63H;};j#tZPlf+(D!JnK-}NpGF4e}^Y4AV3g%cl(Lj;mM zh-Wbt(7EXD3BC5pbFXtX1W%dX8@;1M{*5ygdR zT&Vo;{49C4cGbD(OZl|q@J&PXM)HaZd$kd5LwbeEBHdwN$Jd` zMF{QhhAbbJe@4lblr~wPF#Md-`F?*B`TT~A5k)bbBzijQF(h!1D@>7c;$@yi^K0nS zl?O>C$o_na!Q;I=`c+Q?&v1Uo7gv0zzr=_!9x0uR{U9I#(=rQeOe&}(ii0B4AAaMx z2wQ`4ITBY(0NTw^Lv>hT^HeQmx^V6FV@Fcr z@C@Rp*!Krbr~Sk>&1)ywi^1^@Ot6uB>v+S47%H;GjzLPi*VJ`#4)H07O)_oX$f8}$ zdA%tf84p|8^~w9v38YL3&=S0RU3xu_TBn*5ZgE}CQv{lK!7?_pp(Hj)a;$Q~mpCX% zHFT324LP@zbknleMl_tS`*WKTehJk~t|G5i`JU?N2QEmI=lr4+-O277(Bg&o*tj@+ zId$rC^kn_-?=m+o@W$8t3HbFs zA}eVbX0beTs^9!eT-{Nz=qfiK$4j!~)HGl9nac&WMUyfaZl&O9!ZwQQLRr93Ml9== zOWaDkD3#h*J_gd$Viddr2v$hXo2OyfpW1^t>E7)-jQyzH*$;54 z2U|L>KCxiwTugY^+#k+p0-e7C#57Qf{UG0Po3lC)!w1TD81r;&V2~a;YRH; zdRM8%VsP`Cjt4tZLr%G1roz%PXDR97rP%hBPfg7Mo~~xfJLRhI~1i7t=EfdKj|w8J=EG&aTUe{s>VXkYsI;{d6uBNnxaNhQeT@# zD`2alcmPBc4Ljx!NKpe7g2G6#LfGw|`g}LTd%IA0ni|Y<)Aq5Cw3(}m(N!l+vSS`4 z$9`->u60>q+{py*F$v-{If#O9`a$d$F&|*aVNx=rfcT3nnuacU&plljGO6NY+v&nN zHiW}BDCdE5@HnU#1VP?l0@U(1)KscXsXla9r9NF_?iRkc@z=^8JO#Y$cf*U!aww-W z60#WI?bhJnCpa(JAyT7g=$d!GnT%G!ePK5(^zzL=5d_Y>e~@Gq8$bpw(d zu+~9^DG*Z@fP`ZqzQAjjQ36dw2o1^&C8V98EI39$8DP&Rj6%RsHyfDQT|gjEXxMHT zq%HXs3XgXmiA@rr)%#? zKusKDa*=(*#~#DCbbwflN1}4YYscS*Z6EYn4msgh0zsJ!cj>~)*v{h3Am|2RVImqo zI%%(*yokf0O!hsi@@+;W$#?ggYxr&$)LN{}hPmaJs}0f1a8Joir-Gox#ASV&&!Fo` zIu|l$!fosejr)Aye)Z^oIDsLQOGmhrOcu`vuPI)IoN0j^W%gBeTr{I)E7cm{Bidh+ znZo8;ZPn=Q($L|k=&5*%tfp<%xP-ZSm8_m~)kmG;v+(LqkG9wJqWewX8~=pRVkoCS zF5=*r70IaITON}+!?`J&7*53064I&@;>C=g1xHx1Lv3G05TI;d^{d#;BqBt1mo}so zgdXz;-_OSPkzGy)Vo!cQ)ni4HRFzAztv(xqmM`H@qwYJpp{hP%%^@gCmjN_#s0BtRUO>Q@TGwqnKjkCS> zn#3@1E%f+ox74ENXS!bu%>1IEWuBX4*l@L7hbjuk0_22O{yOc2ZwjMl9Y$hC)YYIjYQQ8bGQ425Gpe19``W5Cm5zY3Q5T$L3RnzCI=Z9`>|NlmCXM3ctin=Y@n#^ z0!}jtYLlGa6FiG1&A(`pyP1#MZr2OGcGb;VEpp`=k(w#Be+p%K>tGr+CZ%?77|x`~ zm?>cJMmxT%=;FIBJ~hSrQQ1df`+SXnsBb07E17gFULexhzR-Pr_RI(b=H8dxMZ+fN zc>3IdRR--N4k``5IiBD43QHe1s0%X_`uCgeqi;fSrG?hH7LswUFb)_T5f%%JzQtkJ zq+tr}D1z1c>!kO!EbphCs!{oGZ$^rW9Ybn3>L9Sw`50s$8Th1)g%;e~ z6QD1pA`cXv9!@pec~(1E5kj8Awp&2k&8OFipRsuVz9J7Pm|SV0O148xB>yn+*byKX z>?y)b614{{bRuMum5SY_!^_~*0bXfVIP~t$k+Fue(;!4KFGg=|CZCf&w!D8#Ecs($ z-KEHUsb-*2k5ko+=m3+OTP7TrabEL$Z{KwUGoAeo2

QJx8u(c;{O@hHqa*uT;M%@1 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 diff --git a/vendor/gitea.com/macaron/macaron/recovery.go b/vendor/gitea.com/macaron/macaron/recovery.go deleted file mode 100644 index 1d301f721..000000000 --- a/vendor/gitea.com/macaron/macaron/recovery.go +++ /dev/null @@ -1,163 +0,0 @@ -// 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 = ` -PANIC: %s - - - -

PANIC

-
%s
-
%s
- -` -) - -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 deleted file mode 100644 index 04687c4f4..000000000 --- a/vendor/gitea.com/macaron/macaron/render.go +++ /dev/null @@ -1,724 +0,0 @@ -// 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_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, _ 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 deleted file mode 100644 index eeb35f642..000000000 --- a/vendor/gitea.com/macaron/macaron/response_writer.go +++ /dev/null @@ -1,124 +0,0 @@ -// 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" - "errors" - "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 - http.Pusher - // 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, errors.New("the ResponseWriter doesn't support the Hijacker interface") - } - return hijacker.Hijack() -} - -//nolint -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() - } -} - -func (rw *responseWriter) Push(target string, opts *http.PushOptions) error { - pusher, ok := rw.ResponseWriter.(http.Pusher) - if !ok { - return errors.New("the ResponseWriter doesn't support the Pusher interface") - } - return pusher.Push(target, opts) -} diff --git a/vendor/gitea.com/macaron/macaron/return_handler.go b/vendor/gitea.com/macaron/macaron/return_handler.go deleted file mode 100644 index 10b2c2283..000000000 --- a/vendor/gitea.com/macaron/macaron/return_handler.go +++ /dev/null @@ -1,76 +0,0 @@ -// 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 deleted file mode 100644 index df593d669..000000000 --- a/vendor/gitea.com/macaron/macaron/router.go +++ /dev/null @@ -1,380 +0,0 @@ -// 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 deleted file mode 100644 index 04d91d8c9..000000000 --- a/vendor/gitea.com/macaron/macaron/static.go +++ /dev/null @@ -1,231 +0,0 @@ -// 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" - "fmt" - "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() { - redirPath := path.Clean(ctx.Req.URL.Path) - // path.Clean removes the trailing slash, so we need to add it back when - // the original path has it. - if strings.HasSuffix(ctx.Req.URL.Path, "/") { - redirPath = redirPath + "/" - } - // Redirect if missing trailing slash. - if !strings.HasSuffix(redirPath, "/") { - http.Redirect(ctx.Resp, ctx.Req.Request, redirPath+"/", 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(fmt.Sprintf("%d", fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat)) + `"` - ctx.Resp.Header().Set("ETag", tag) - if ctx.Req.Header.Get("If-None-Match") == tag { - ctx.Resp.WriteHeader(http.StatusNotModified) - return true - } - } - - 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 deleted file mode 100644 index 0ab094dd6..000000000 --- a/vendor/gitea.com/macaron/macaron/tree.go +++ /dev/null @@ -1,390 +0,0 @@ -// 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 deleted file mode 100644 index a80c696c7..000000000 --- a/vendor/gitea.com/macaron/macaron/util_go17.go +++ /dev/null @@ -1,25 +0,0 @@ -// +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 deleted file mode 100644 index d5eb1dfb2..000000000 --- a/vendor/gitea.com/macaron/macaron/util_go18.go +++ /dev/null @@ -1,24 +0,0 @@ -// +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 deleted file mode 100644 index 717ada4b8..000000000 --- a/vendor/gitea.com/macaron/session/.drone.yml +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 834722925..000000000 --- a/vendor/gitea.com/macaron/session/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -ledis/tmp.db -nodb/tmp.db -/vendor -/.idea diff --git a/vendor/gitea.com/macaron/session/LICENSE b/vendor/gitea.com/macaron/session/LICENSE deleted file mode 100644 index 8405e89a0..000000000 --- a/vendor/gitea.com/macaron/session/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index ebbbff545..000000000 --- a/vendor/gitea.com/macaron/session/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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 deleted file mode 100644 index 25a278d34..000000000 --- a/vendor/gitea.com/macaron/session/couchbase/couchbase.go +++ /dev/null @@ -1,227 +0,0 @@ -// 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/couchbase/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 deleted file mode 100644 index ce915344f..000000000 --- a/vendor/gitea.com/macaron/session/file.go +++ /dev/null @@ -1,274 +0,0 @@ -// 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 - ok := false - if com.IsFile(filename) { - modTime, err := com.FileMTime(filename) - if err != nil { - return nil, err - } - ok = (modTime + p.maxlifetime) >= time.Now().Unix() - } - if ok { - 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 deleted file mode 100644 index 93c461d47..000000000 --- a/vendor/gitea.com/macaron/session/flash.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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 deleted file mode 100644 index f44315f82..000000000 --- a/vendor/gitea.com/macaron/session/go.mod +++ /dev/null @@ -1,30 +0,0 @@ -module gitea.com/macaron/session - -go 1.11 - -require ( - gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727 - gitea.com/macaron/macaron v1.5.0 - github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 - github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 - github.com/couchbase/gomemcached v0.1.0 // indirect - github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 // indirect - 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/golang/snappy v0.0.2 // indirect - github.com/lib/pq v1.2.0 - github.com/pkg/errors v0.8.1 // indirect - github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // 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/unknwon/com v1.0.1 - golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect - golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect - google.golang.org/appengine v1.6.1 // indirect - gopkg.in/ini.v1 v1.62.0 - gopkg.in/yaml.v2 v2.2.2 // indirect -) diff --git a/vendor/gitea.com/macaron/session/go.sum b/vendor/gitea.com/macaron/session/go.sum deleted file mode 100644 index cf76c4e87..000000000 --- a/vendor/gitea.com/macaron/session/go.sum +++ /dev/null @@ -1,118 +0,0 @@ -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk= -gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= -gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727 h1:ZF2Bd6rqVlwhIDhYiS0uGYcT+GaVNGjuKVJkTNqWMIs= -gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY= -gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= -gitea.com/macaron/macaron v1.5.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= -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/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 h1:uNLXQ6QO1TocD8BaN/KkRki0Xw0brCM1PKl/ZA5pgfs= -github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= -github.com/couchbase/gomemcached v0.1.0 h1:whUde87n8CScx8ckMp2En5liqAlcuG3aKy/BQeBPu84= -github.com/couchbase/gomemcached v0.1.0/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= -github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4= -github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= -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/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= -github.com/golang/snappy v0.0.2/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/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -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.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -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/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 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 v1.0.1/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/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -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/ini.v1 v1.62.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 deleted file mode 100644 index ff1209754..000000000 --- a/vendor/gitea.com/macaron/session/memcache/memcache.go +++ /dev/null @@ -1,203 +0,0 @@ -// 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 deleted file mode 100644 index 8485e986e..000000000 --- a/vendor/gitea.com/macaron/session/memcache/memcache.goconvey +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 076922575..000000000 --- a/vendor/gitea.com/macaron/session/memory.go +++ /dev/null @@ -1,223 +0,0 @@ -// 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.list = list.New() - p.data = make(map[string]*list.Element) - 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() - - // Only restore if the session is still alive. - if ok && (e.Value.(*MemStore).lastAccess.Unix()+p.maxLifetime) >= time.Now().Unix() { - 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() - if ok { - p.list.Remove(e) - delete(p.data, sid) - } - 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{}) -} diff --git a/vendor/gitea.com/macaron/session/mysql/mysql.go b/vendor/gitea.com/macaron/session/mysql/mysql.go deleted file mode 100644 index af1cd9dd1..000000000 --- a/vendor/gitea.com/macaron/session/mysql/mysql.go +++ /dev/null @@ -1,201 +0,0 @@ -// 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) { - now := time.Now().Unix() - var data []byte - expiry := now - err := p.c.QueryRow("SELECT data, expiry FROM session WHERE `key`=?", sid).Scan(&data, &expiry) - if err == sql.ErrNoRows { - _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", - sid, "", now) - } - if err != nil { - return nil, err - } - - var kv map[interface{}]interface{} - if len(data) == 0 || expiry+p.expire <= now { - 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 deleted file mode 100644 index 8485e986e..000000000 --- a/vendor/gitea.com/macaron/session/mysql/mysql.goconvey +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 08a73c549..000000000 --- a/vendor/gitea.com/macaron/session/nodb/nodb.go +++ /dev/null @@ -1,207 +0,0 @@ -// 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" - "gitea.com/lunny/nodb" - "gitea.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 deleted file mode 100644 index 8485e986e..000000000 --- a/vendor/gitea.com/macaron/session/nodb/nodb.goconvey +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index f173021d5..000000000 --- a/vendor/gitea.com/macaron/session/postgres/postgres.go +++ /dev/null @@ -1,202 +0,0 @@ -// 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) { - now := time.Now().Unix() - var data []byte - expiry := now - err := p.c.QueryRow("SELECT data, expiry FROM session WHERE key=$1", sid).Scan(&data, &expiry) - if err == sql.ErrNoRows { - _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", - sid, "", now) - } - if err != nil { - return nil, err - } - - var kv map[interface{}]interface{} - if len(data) == 0 || expiry+p.maxlifetime <= now { - 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 deleted file mode 100644 index 8485e986e..000000000 --- a/vendor/gitea.com/macaron/session/postgres/postgres.goconvey +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 93f18342d..000000000 --- a/vendor/gitea.com/macaron/session/session.go +++ /dev/null @@ -1,393 +0,0 @@ -// 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 deleted file mode 100644 index 762b978cd..000000000 --- a/vendor/gitea.com/macaron/session/utils.go +++ /dev/null @@ -1,63 +0,0 @@ -// 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 deleted file mode 100644 index 39499f444..000000000 --- a/vendor/gitea.com/macaron/toolbox/.drone.yml +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index c3265c118..000000000 --- a/vendor/gitea.com/macaron/toolbox/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -profile/ -/.idea diff --git a/vendor/gitea.com/macaron/toolbox/LICENSE b/vendor/gitea.com/macaron/toolbox/LICENSE deleted file mode 100644 index 8405e89a0..000000000 --- a/vendor/gitea.com/macaron/toolbox/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -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 deleted file mode 100644 index 75055d5f0..000000000 --- a/vendor/gitea.com/macaron/toolbox/README.md +++ /dev/null @@ -1,112 +0,0 @@ -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/healthcheck.go b/vendor/gitea.com/macaron/toolbox/healthcheck.go deleted file mode 100644 index 25b5bdfe2..000000000 --- a/vendor/gitea.com/macaron/toolbox/healthcheck.go +++ /dev/null @@ -1,83 +0,0 @@ -// 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 deleted file mode 100644 index 359a87fde..000000000 --- a/vendor/gitea.com/macaron/toolbox/profile.go +++ /dev/null @@ -1,163 +0,0 @@ -// 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(`

Available operations:

-
    -
  1. Start CPU profile
  2. -
  3. Stop CPU profile
  4. -
  5. Dump memory profile
  6. -
  7. Dump GC summary
  8. -
`, 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 deleted file mode 100644 index 47e6ab23e..000000000 --- a/vendor/gitea.com/macaron/toolbox/statistic.go +++ /dev/null @@ -1,138 +0,0 @@ -// 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 deleted file mode 100644 index 42e565e45..000000000 --- a/vendor/gitea.com/macaron/toolbox/toolbox.go +++ /dev/null @@ -1,158 +0,0 @@ -// 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(`

Toolbox Index:

-
    -
  1. Pprof Information
  2. -
  3. Profile Operations
  4. -
`, 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)) - } -} diff --git a/vendor/github.com/NYTimes/gziphandler/.gitignore b/vendor/github.com/NYTimes/gziphandler/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/vendor/github.com/NYTimes/gziphandler/.travis.yml b/vendor/github.com/NYTimes/gziphandler/.travis.yml new file mode 100644 index 000000000..94dfae362 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.x + - tip +env: + - GO111MODULE=on +install: + - go mod download +script: + - go test -race -v diff --git a/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..cdbca194c --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +--- +layout: code-of-conduct +version: v1.0 +--- + +This code of conduct outlines our expectations for participants within the **NYTimes/gziphandler** community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. + +Our open source community strives to: + +* **Be friendly and patient.** +* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. +* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. +* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. +* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. +* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. + +## Definitions + +Harassment includes, but is not limited to: + +- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation +- Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment +- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle +- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop +- Threats of violence, both physical and psychological +- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm +- Deliberate intimidation +- Stalking or following +- Harassing photography or recording, including logging online activity for harassment purposes +- Sustained disruption of discussion +- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour +- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others +- Continued one-on-one communication after requests to cease +- Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse +- Publication of non-harassing private communication + +Our open source community prioritizes marginalized people’s safety over privileged people’s comfort. We will not act on complaints regarding: + +- ‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’ +- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you” +- Refusal to explain or debate social justice concepts +- Communicating in a ‘tone’ you don’t find congenial +- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions + + +### Diversity Statement + +We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. + +Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected +characteristics above, including participants with disabilities. + +### Reporting Issues + +If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via **code@nytimes.com**. All reports will be handled with discretion. In your report please include: + +- Your contact information. +- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please +include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. +- Any additional information that may be helpful. + +After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse. + +### Attribution & Acknowledgements + +We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration: + +* [Django](https://www.djangoproject.com/conduct/reporting/) +* [Python](https://www.python.org/community/diversity/) +* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) +* [Contributor Covenant](http://contributor-covenant.org/) +* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) +* [Citizen Code of Conduct](http://citizencodeofconduct.org/) + +This Code of Conduct was based on https://github.com/todogroup/opencodeofconduct diff --git a/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md new file mode 100644 index 000000000..b89a9eb4f --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing to NYTimes/gziphandler + +This is an open source project started by handful of developers at The New York Times and open to the entire Go community. + +We really appreciate your help! + +## Filing issues + +When filing an issue, make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +## Contributing code + +Before submitting changes, please follow these guidelines: + +1. Check the open issues and pull requests for existing discussions. +2. Open an issue to discuss a new feature. +3. Write tests. +4. Make sure code follows the ['Go Code Review Comments'](https://github.com/golang/go/wiki/CodeReviewComments). +5. Make sure your changes pass `go test`. +6. Make sure the entire test suite passes locally and on Travis CI. +7. Open a Pull Request. +8. [Squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) after receiving feedback and add a [great commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +Unless otherwise noted, the gziphandler source files are distributed under the Apache 2.0-style license found in the LICENSE.md file. diff --git a/vendor/gitea.com/macaron/cors/LICENSE b/vendor/github.com/NYTimes/gziphandler/LICENSE similarity index 99% rename from vendor/gitea.com/macaron/cors/LICENSE rename to vendor/github.com/NYTimes/gziphandler/LICENSE index 261eeb9e9..df6192d36 100644 --- a/vendor/gitea.com/macaron/cors/LICENSE +++ b/vendor/github.com/NYTimes/gziphandler/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2016-2017 The New York Times Company Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/NYTimes/gziphandler/README.md b/vendor/github.com/NYTimes/gziphandler/README.md new file mode 100644 index 000000000..6259acaca --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/README.md @@ -0,0 +1,56 @@ +Gzip Handler +============ + +This is a tiny Go package which wraps HTTP handlers to transparently gzip the +response body, for clients which support it. Although it's usually simpler to +leave that to a reverse proxy (like nginx or Varnish), this package is useful +when that's undesirable. + +## Install +```bash +go get -u github.com/NYTimes/gziphandler +``` + +## Usage + +Call `GzipHandler` with any handler (an object which implements the +`http.Handler` interface), and it'll return a new handler which gzips the +response. For example: + +```go +package main + +import ( + "io" + "net/http" + "github.com/NYTimes/gziphandler" +) + +func main() { + withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + io.WriteString(w, "Hello, World") + }) + + withGz := gziphandler.GzipHandler(withoutGz) + + http.Handle("/", withGz) + http.ListenAndServe("0.0.0.0:8000", nil) +} +``` + + +## Documentation + +The docs can be found at [godoc.org][docs], as usual. + + +## License + +[Apache 2.0][license]. + + + + +[docs]: https://godoc.org/github.com/NYTimes/gziphandler +[license]: https://github.com/NYTimes/gziphandler/blob/master/LICENSE diff --git a/vendor/github.com/NYTimes/gziphandler/go.mod b/vendor/github.com/NYTimes/gziphandler/go.mod new file mode 100644 index 000000000..801901274 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/go.mod @@ -0,0 +1,5 @@ +module github.com/NYTimes/gziphandler + +go 1.11 + +require github.com/stretchr/testify v1.3.0 diff --git a/vendor/github.com/NYTimes/gziphandler/go.sum b/vendor/github.com/NYTimes/gziphandler/go.sum new file mode 100644 index 000000000..4347755af --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/go.sum @@ -0,0 +1,7 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/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= diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go new file mode 100644 index 000000000..c112bbdf8 --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip.go @@ -0,0 +1,532 @@ +package gziphandler // import "github.com/NYTimes/gziphandler" + +import ( + "bufio" + "compress/gzip" + "fmt" + "io" + "mime" + "net" + "net/http" + "strconv" + "strings" + "sync" +) + +const ( + vary = "Vary" + acceptEncoding = "Accept-Encoding" + contentEncoding = "Content-Encoding" + contentType = "Content-Type" + contentLength = "Content-Length" +) + +type codings map[string]float64 + +const ( + // DefaultQValue is the default qvalue to assign to an encoding if no explicit qvalue is set. + // This is actually kind of ambiguous in RFC 2616, so hopefully it's correct. + // The examples seem to indicate that it is. + DefaultQValue = 1.0 + + // DefaultMinSize is the default minimum size until we enable gzip compression. + // 1500 bytes is the MTU size for the internet since that is the largest size allowed at the network layer. + // If you take a file that is 1300 bytes and compress it to 800 bytes, it’s still transmitted in that same 1500 byte packet regardless, so you’ve gained nothing. + // That being the case, you should restrict the gzip compression to files with a size greater than a single packet, 1400 bytes (1.4KB) is a safe value. + DefaultMinSize = 1400 +) + +// gzipWriterPools stores a sync.Pool for each compression level for reuse of +// gzip.Writers. Use poolIndex to covert a compression level to an index into +// gzipWriterPools. +var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool + +func init() { + for i := gzip.BestSpeed; i <= gzip.BestCompression; i++ { + addLevelPool(i) + } + addLevelPool(gzip.DefaultCompression) +} + +// poolIndex maps a compression level to its index into gzipWriterPools. It +// assumes that level is a valid gzip compression level. +func poolIndex(level int) int { + // gzip.DefaultCompression == -1, so we need to treat it special. + if level == gzip.DefaultCompression { + return gzip.BestCompression - gzip.BestSpeed + 1 + } + return level - gzip.BestSpeed +} + +func addLevelPool(level int) { + gzipWriterPools[poolIndex(level)] = &sync.Pool{ + New: func() interface{} { + // NewWriterLevel only returns error on a bad level, we are guaranteeing + // that this will be a valid level so it is okay to ignore the returned + // error. + w, _ := gzip.NewWriterLevel(nil, level) + return w + }, + } +} + +// GzipResponseWriter provides an http.ResponseWriter interface, which gzips +// bytes before writing them to the underlying response. This doesn't close the +// writers, so don't forget to do that. +// It can be configured to skip response smaller than minSize. +type GzipResponseWriter struct { + http.ResponseWriter + index int // Index for gzipWriterPools. + gw *gzip.Writer + + code int // Saves the WriteHeader value. + + minSize int // Specifed the minimum response size to gzip. If the response length is bigger than this value, it is compressed. + buf []byte // Holds the first part of the write before reaching the minSize or the end of the write. + ignore bool // If true, then we immediately passthru writes to the underlying ResponseWriter. + + contentTypes []parsedContentType // Only compress if the response is one of these content-types. All are accepted if empty. +} + +type GzipResponseWriterWithCloseNotify struct { + *GzipResponseWriter +} + +func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool { + return w.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +// Write appends data to the gzip writer. +func (w *GzipResponseWriter) Write(b []byte) (int, error) { + // GZIP responseWriter is initialized. Use the GZIP responseWriter. + if w.gw != nil { + return w.gw.Write(b) + } + + // If we have already decided not to use GZIP, immediately passthrough. + if w.ignore { + return w.ResponseWriter.Write(b) + } + + // Save the write into a buffer for later use in GZIP responseWriter (if content is long enough) or at close with regular responseWriter. + // On the first write, w.buf changes from nil to a valid slice + w.buf = append(w.buf, b...) + + var ( + cl, _ = strconv.Atoi(w.Header().Get(contentLength)) + ct = w.Header().Get(contentType) + ce = w.Header().Get(contentEncoding) + ) + // Only continue if they didn't already choose an encoding or a known unhandled content length or type. + if ce == "" && (cl == 0 || cl >= w.minSize) && (ct == "" || handleContentType(w.contentTypes, ct)) { + // If the current buffer is less than minSize and a Content-Length isn't set, then wait until we have more data. + if len(w.buf) < w.minSize && cl == 0 { + return len(b), nil + } + // If the Content-Length is larger than minSize or the current buffer is larger than minSize, then continue. + if cl >= w.minSize || len(w.buf) >= w.minSize { + // If a Content-Type wasn't specified, infer it from the current buffer. + if ct == "" { + ct = http.DetectContentType(w.buf) + w.Header().Set(contentType, ct) + } + // If the Content-Type is acceptable to GZIP, initialize the GZIP writer. + if handleContentType(w.contentTypes, ct) { + if err := w.startGzip(); err != nil { + return 0, err + } + return len(b), nil + } + } + } + // If we got here, we should not GZIP this response. + if err := w.startPlain(); err != nil { + return 0, err + } + return len(b), nil +} + +// startGzip initializes a GZIP writer and writes the buffer. +func (w *GzipResponseWriter) startGzip() error { + // Set the GZIP header. + w.Header().Set(contentEncoding, "gzip") + + // if the Content-Length is already set, then calls to Write on gzip + // will fail to set the Content-Length header since its already set + // See: https://github.com/golang/go/issues/14975. + w.Header().Del(contentLength) + + // Write the header to gzip response. + if w.code != 0 { + w.ResponseWriter.WriteHeader(w.code) + // Ensure that no other WriteHeader's happen + w.code = 0 + } + + // Initialize and flush the buffer into the gzip response if there are any bytes. + // If there aren't any, we shouldn't initialize it yet because on Close it will + // write the gzip header even if nothing was ever written. + if len(w.buf) > 0 { + // Initialize the GZIP response. + w.init() + n, err := w.gw.Write(w.buf) + + // This should never happen (per io.Writer docs), but if the write didn't + // accept the entire buffer but returned no specific error, we have no clue + // what's going on, so abort just to be safe. + if err == nil && n < len(w.buf) { + err = io.ErrShortWrite + } + return err + } + return nil +} + +// startPlain writes to sent bytes and buffer the underlying ResponseWriter without gzip. +func (w *GzipResponseWriter) startPlain() error { + if w.code != 0 { + w.ResponseWriter.WriteHeader(w.code) + // Ensure that no other WriteHeader's happen + w.code = 0 + } + w.ignore = true + // If Write was never called then don't call Write on the underlying ResponseWriter. + if w.buf == nil { + return nil + } + n, err := w.ResponseWriter.Write(w.buf) + w.buf = nil + // This should never happen (per io.Writer docs), but if the write didn't + // accept the entire buffer but returned no specific error, we have no clue + // what's going on, so abort just to be safe. + if err == nil && n < len(w.buf) { + err = io.ErrShortWrite + } + return err +} + +// WriteHeader just saves the response code until close or GZIP effective writes. +func (w *GzipResponseWriter) WriteHeader(code int) { + if w.code == 0 { + w.code = code + } +} + +// init graps a new gzip writer from the gzipWriterPool and writes the correct +// content encoding header. +func (w *GzipResponseWriter) init() { + // Bytes written during ServeHTTP are redirected to this gzip writer + // before being written to the underlying response. + gzw := gzipWriterPools[w.index].Get().(*gzip.Writer) + gzw.Reset(w.ResponseWriter) + w.gw = gzw +} + +// Close will close the gzip.Writer and will put it back in the gzipWriterPool. +func (w *GzipResponseWriter) Close() error { + if w.ignore { + return nil + } + + if w.gw == nil { + // GZIP not triggered yet, write out regular response. + err := w.startPlain() + // Returns the error if any at write. + if err != nil { + err = fmt.Errorf("gziphandler: write to regular responseWriter at close gets error: %q", err.Error()) + } + return err + } + + err := w.gw.Close() + gzipWriterPools[w.index].Put(w.gw) + w.gw = nil + return err +} + +// Flush flushes the underlying *gzip.Writer and then the underlying +// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter +// an http.Flusher. +func (w *GzipResponseWriter) Flush() { + if w.gw == nil && !w.ignore { + // Only flush once startGzip or startPlain has been called. + // + // Flush is thus a no-op until we're certain whether a plain + // or gzipped response will be served. + return + } + + if w.gw != nil { + w.gw.Flush() + } + + if fw, ok := w.ResponseWriter.(http.Flusher); ok { + fw.Flush() + } +} + +// Hijack implements http.Hijacker. If the underlying ResponseWriter is a +// Hijacker, its Hijack method is returned. Otherwise an error is returned. +func (w *GzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if hj, ok := w.ResponseWriter.(http.Hijacker); ok { + return hj.Hijack() + } + return nil, nil, fmt.Errorf("http.Hijacker interface is not supported") +} + +// verify Hijacker interface implementation +var _ http.Hijacker = &GzipResponseWriter{} + +// MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in +// an error case it panics rather than returning an error. +func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler { + wrap, err := NewGzipLevelHandler(level) + if err != nil { + panic(err) + } + return wrap +} + +// NewGzipLevelHandler returns a wrapper function (often known as middleware) +// which can be used to wrap an HTTP handler to transparently gzip the response +// body if the client supports it (via the Accept-Encoding header). Responses will +// be encoded at the given gzip compression level. An error will be returned only +// if an invalid gzip compression level is given, so if one can ensure the level +// is valid, the returned error can be safely ignored. +func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) { + return NewGzipLevelAndMinSize(level, DefaultMinSize) +} + +// NewGzipLevelAndMinSize behave as NewGzipLevelHandler except it let the caller +// specify the minimum size before compression. +func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler, error) { + return GzipHandlerWithOpts(CompressionLevel(level), MinSize(minSize)) +} + +func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error) { + c := &config{ + level: gzip.DefaultCompression, + minSize: DefaultMinSize, + } + + for _, o := range opts { + o(c) + } + + if err := c.validate(); err != nil { + return nil, err + } + + return func(h http.Handler) http.Handler { + index := poolIndex(c.level) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add(vary, acceptEncoding) + if acceptsGzip(r) { + gw := &GzipResponseWriter{ + ResponseWriter: w, + index: index, + minSize: c.minSize, + contentTypes: c.contentTypes, + } + defer gw.Close() + + if _, ok := w.(http.CloseNotifier); ok { + gwcn := GzipResponseWriterWithCloseNotify{gw} + h.ServeHTTP(gwcn, r) + } else { + h.ServeHTTP(gw, r) + } + + } else { + h.ServeHTTP(w, r) + } + }) + }, nil +} + +// Parsed representation of one of the inputs to ContentTypes. +// See https://golang.org/pkg/mime/#ParseMediaType +type parsedContentType struct { + mediaType string + params map[string]string +} + +// equals returns whether this content type matches another content type. +func (pct parsedContentType) equals(mediaType string, params map[string]string) bool { + if pct.mediaType != mediaType { + return false + } + // if pct has no params, don't care about other's params + if len(pct.params) == 0 { + return true + } + + // if pct has any params, they must be identical to other's. + if len(pct.params) != len(params) { + return false + } + for k, v := range pct.params { + if w, ok := params[k]; !ok || v != w { + return false + } + } + return true +} + +// Used for functional configuration. +type config struct { + minSize int + level int + contentTypes []parsedContentType +} + +func (c *config) validate() error { + if c.level != gzip.DefaultCompression && (c.level < gzip.BestSpeed || c.level > gzip.BestCompression) { + return fmt.Errorf("invalid compression level requested: %d", c.level) + } + + if c.minSize < 0 { + return fmt.Errorf("minimum size must be more than zero") + } + + return nil +} + +type option func(c *config) + +func MinSize(size int) option { + return func(c *config) { + c.minSize = size + } +} + +func CompressionLevel(level int) option { + return func(c *config) { + c.level = level + } +} + +// ContentTypes specifies a list of content types to compare +// the Content-Type header to before compressing. If none +// match, the response will be returned as-is. +// +// Content types are compared in a case-insensitive, whitespace-ignored +// manner. +// +// A MIME type without any other directive will match a content type +// that has the same MIME type, regardless of that content type's other +// directives. I.e., "text/html" will match both "text/html" and +// "text/html; charset=utf-8". +// +// A MIME type with any other directive will only match a content type +// that has the same MIME type and other directives. I.e., +// "text/html; charset=utf-8" will only match "text/html; charset=utf-8". +// +// By default, responses are gzipped regardless of +// Content-Type. +func ContentTypes(types []string) option { + return func(c *config) { + c.contentTypes = []parsedContentType{} + for _, v := range types { + mediaType, params, err := mime.ParseMediaType(v) + if err == nil { + c.contentTypes = append(c.contentTypes, parsedContentType{mediaType, params}) + } + } + } +} + +// GzipHandler wraps an HTTP handler, to transparently gzip the response body if +// the client supports it (via the Accept-Encoding header). This will compress at +// the default compression level. +func GzipHandler(h http.Handler) http.Handler { + wrapper, _ := NewGzipLevelHandler(gzip.DefaultCompression) + return wrapper(h) +} + +// acceptsGzip returns true if the given HTTP request indicates that it will +// accept a gzipped response. +func acceptsGzip(r *http.Request) bool { + acceptedEncodings, _ := parseEncodings(r.Header.Get(acceptEncoding)) + return acceptedEncodings["gzip"] > 0.0 +} + +// returns true if we've been configured to compress the specific content type. +func handleContentType(contentTypes []parsedContentType, ct string) bool { + // If contentTypes is empty we handle all content types. + if len(contentTypes) == 0 { + return true + } + + mediaType, params, err := mime.ParseMediaType(ct) + if err != nil { + return false + } + + for _, c := range contentTypes { + if c.equals(mediaType, params) { + return true + } + } + + return false +} + +// parseEncodings attempts to parse a list of codings, per RFC 2616, as might +// appear in an Accept-Encoding header. It returns a map of content-codings to +// quality values, and an error containing the errors encountered. It's probably +// safe to ignore those, because silently ignoring errors is how the internet +// works. +// +// See: http://tools.ietf.org/html/rfc2616#section-14.3. +func parseEncodings(s string) (codings, error) { + c := make(codings) + var e []string + + for _, ss := range strings.Split(s, ",") { + coding, qvalue, err := parseCoding(ss) + + if err != nil { + e = append(e, err.Error()) + } else { + c[coding] = qvalue + } + } + + // TODO (adammck): Use a proper multi-error struct, so the individual errors + // can be extracted if anyone cares. + if len(e) > 0 { + return c, fmt.Errorf("errors while parsing encodings: %s", strings.Join(e, ", ")) + } + + return c, nil +} + +// parseCoding parses a single conding (content-coding with an optional qvalue), +// as might appear in an Accept-Encoding header. It attempts to forgive minor +// formatting errors. +func parseCoding(s string) (coding string, qvalue float64, err error) { + for n, part := range strings.Split(s, ";") { + part = strings.TrimSpace(part) + qvalue = DefaultQValue + + if n == 0 { + coding = strings.ToLower(part) + } else if strings.HasPrefix(part, "q=") { + qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64) + + if qvalue < 0.0 { + qvalue = 0.0 + } else if qvalue > 1.0 { + qvalue = 1.0 + } + } + } + + if coding == "" { + err = fmt.Errorf("empty content-coding") + } + + return +} diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_go18.go b/vendor/github.com/NYTimes/gziphandler/gzip_go18.go new file mode 100644 index 000000000..fa9665b7e --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip_go18.go @@ -0,0 +1,43 @@ +// +build go1.8 + +package gziphandler + +import "net/http" + +// Push initiates an HTTP/2 server push. +// Push returns ErrNotSupported if the client has disabled push or if push +// is not supported on the underlying connection. +func (w *GzipResponseWriter) Push(target string, opts *http.PushOptions) error { + pusher, ok := w.ResponseWriter.(http.Pusher) + if ok && pusher != nil { + return pusher.Push(target, setAcceptEncodingForPushOptions(opts)) + } + return http.ErrNotSupported +} + +// setAcceptEncodingForPushOptions sets "Accept-Encoding" : "gzip" for PushOptions without overriding existing headers. +func setAcceptEncodingForPushOptions(opts *http.PushOptions) *http.PushOptions { + + if opts == nil { + opts = &http.PushOptions{ + Header: http.Header{ + acceptEncoding: []string{"gzip"}, + }, + } + return opts + } + + if opts.Header == nil { + opts.Header = http.Header{ + acceptEncoding: []string{"gzip"}, + } + return opts + } + + if encoding := opts.Header.Get(acceptEncoding); encoding == "" { + opts.Header.Add(acceptEncoding, "gzip") + return opts + } + + return opts +} diff --git a/vendor/github.com/go-chi/cors/LICENSE b/vendor/github.com/go-chi/cors/LICENSE new file mode 100644 index 000000000..aee6182f9 --- /dev/null +++ b/vendor/github.com/go-chi/cors/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2014 Olivier Poitrey +Copyright (c) 2016-Present https://github.com/go-chi authors + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/go-chi/cors/README.md b/vendor/github.com/go-chi/cors/README.md new file mode 100644 index 000000000..1cd6b7f11 --- /dev/null +++ b/vendor/github.com/go-chi/cors/README.md @@ -0,0 +1,39 @@ +# CORS net/http middleware + +[go-chi/cors](https://github.com/go-chi/cors) is a fork of [github.com/rs/cors](https://github.com/rs/cors) that +provides a `net/http` compatible middleware for performing preflight CORS checks on the server side. These headers +are required for using the browser native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + +This middleware is designed to be used as a top-level middleware on the [chi](https://github.com/go-chi/chi) router. +Applying with within a `r.Group()` or using `With()` will not work without routes matching `OPTIONS` added. + +## Usage + +```go +func main() { + r := chi.NewRouter() + + // Basic CORS + // for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing + r.Use(cors.Handler(cors.Options{ + // AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts + AllowedOrigins: []string{"*"}, + // AllowOriginFunc: func(r *http.Request, origin string) bool { return true }, + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, + ExposedHeaders: []string{"Link"}, + AllowCredentials: false, + MaxAge: 300, // Maximum value not ignored by any of major browsers + })) + + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("welcome")) + }) + + http.ListenAndServe(":3000", r) +} +``` + +## Credits + +All credit for the original work of this middleware goes out to [github.com/rs](github.com/rs). diff --git a/vendor/github.com/go-chi/cors/cors.go b/vendor/github.com/go-chi/cors/cors.go new file mode 100644 index 000000000..8df81636e --- /dev/null +++ b/vendor/github.com/go-chi/cors/cors.go @@ -0,0 +1,400 @@ +// cors package is net/http handler to handle CORS related requests +// as defined by http://www.w3.org/TR/cors/ +// +// You can configure it by passing an option struct to cors.New: +// +// c := cors.New(cors.Options{ +// AllowedOrigins: []string{"foo.com"}, +// AllowedMethods: []string{"GET", "POST", "DELETE"}, +// AllowCredentials: true, +// }) +// +// Then insert the handler in the chain: +// +// handler = c.Handler(handler) +// +// See Options documentation for more options. +// +// The resulting handler is a standard net/http handler. +package cors + +import ( + "log" + "net/http" + "os" + "strconv" + "strings" +) + +// Options is a configuration container to setup the CORS middleware. +type Options struct { + // AllowedOrigins is a list of origins a cross-domain request can be executed from. + // If the special "*" value is present in the list, all origins will be allowed. + // An origin may contain a wildcard (*) to replace 0 or more characters + // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty. + // Only one wildcard can be used per origin. + // Default value is ["*"] + AllowedOrigins []string + + // AllowOriginFunc is a custom function to validate the origin. It takes the origin + // as argument and returns true if allowed or false otherwise. If this option is + // set, the content of AllowedOrigins is ignored. + AllowOriginFunc func(r *http.Request, origin string) bool + + // AllowedMethods is a list of methods the client is allowed to use with + // cross-domain requests. Default value is simple methods (HEAD, GET and POST). + AllowedMethods []string + + // AllowedHeaders is list of non simple headers the client is allowed to use with + // cross-domain requests. + // If the special "*" value is present in the list, all headers will be allowed. + // Default value is [] but "Origin" is always appended to the list. + AllowedHeaders []string + + // ExposedHeaders indicates which headers are safe to expose to the API of a CORS + // API specification + ExposedHeaders []string + + // AllowCredentials indicates whether the request can include user credentials like + // cookies, HTTP authentication or client side SSL certificates. + AllowCredentials bool + + // MaxAge indicates how long (in seconds) the results of a preflight request + // can be cached + MaxAge int + + // OptionsPassthrough instructs preflight to let other potential next handlers to + // process the OPTIONS method. Turn this on if your application handles OPTIONS. + OptionsPassthrough bool + + // Debugging flag adds additional output to debug server side CORS issues + Debug bool +} + +// Logger generic interface for logger +type Logger interface { + Printf(string, ...interface{}) +} + +// Cors http handler +type Cors struct { + // Debug logger + Log Logger + + // Normalized list of plain allowed origins + allowedOrigins []string + + // List of allowed origins containing wildcards + allowedWOrigins []wildcard + + // Optional origin validator function + allowOriginFunc func(r *http.Request, origin string) bool + + // Normalized list of allowed headers + allowedHeaders []string + + // Normalized list of allowed methods + allowedMethods []string + + // Normalized list of exposed headers + exposedHeaders []string + maxAge int + + // Set to true when allowed origins contains a "*" + allowedOriginsAll bool + + // Set to true when allowed headers contains a "*" + allowedHeadersAll bool + + allowCredentials bool + optionPassthrough bool +} + +// New creates a new Cors handler with the provided options. +func New(options Options) *Cors { + c := &Cors{ + exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), + allowOriginFunc: options.AllowOriginFunc, + allowCredentials: options.AllowCredentials, + maxAge: options.MaxAge, + optionPassthrough: options.OptionsPassthrough, + } + if options.Debug && c.Log == nil { + c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags) + } + + // Normalize options + // Note: for origins and methods matching, the spec requires a case-sensitive matching. + // As it may error prone, we chose to ignore the spec here. + + // Allowed Origins + if len(options.AllowedOrigins) == 0 { + if options.AllowOriginFunc == nil { + // Default is all origins + c.allowedOriginsAll = true + } + } else { + c.allowedOrigins = []string{} + c.allowedWOrigins = []wildcard{} + for _, origin := range options.AllowedOrigins { + // Normalize + origin = strings.ToLower(origin) + if origin == "*" { + // If "*" is present in the list, turn the whole list into a match all + c.allowedOriginsAll = true + c.allowedOrigins = nil + c.allowedWOrigins = nil + break + } else if i := strings.IndexByte(origin, '*'); i >= 0 { + // Split the origin in two: start and end string without the * + w := wildcard{origin[0:i], origin[i+1:]} + c.allowedWOrigins = append(c.allowedWOrigins, w) + } else { + c.allowedOrigins = append(c.allowedOrigins, origin) + } + } + } + + // Allowed Headers + if len(options.AllowedHeaders) == 0 { + // Use sensible defaults + c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"} + } else { + // Origin is always appended as some browsers will always request for this header at preflight + c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey) + for _, h := range options.AllowedHeaders { + if h == "*" { + c.allowedHeadersAll = true + c.allowedHeaders = nil + break + } + } + } + + // Allowed Methods + if len(options.AllowedMethods) == 0 { + // Default is spec's "simple" methods + c.allowedMethods = []string{http.MethodGet, http.MethodPost, http.MethodHead} + } else { + c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper) + } + + return c +} + +// Handler creates a new Cors handler with passed options. +func Handler(options Options) func(next http.Handler) http.Handler { + c := New(options) + return c.Handler +} + +// AllowAll create a new Cors handler with permissive configuration allowing all +// origins with all standard methods with any header and credentials. +func AllowAll() *Cors { + return New(Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{ + http.MethodHead, + http.MethodGet, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + }, + AllowedHeaders: []string{"*"}, + AllowCredentials: false, + }) +} + +// Handler apply the CORS specification on the request, and add relevant CORS headers +// as necessary. +func (c *Cors) Handler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { + c.logf("Handler: Preflight request") + c.handlePreflight(w, r) + // Preflight requests are standalone and should stop the chain as some other + // middleware may not handle OPTIONS requests correctly. One typical example + // is authentication middleware ; OPTIONS requests won't carry authentication + // headers (see #1) + if c.optionPassthrough { + next.ServeHTTP(w, r) + } else { + w.WriteHeader(http.StatusOK) + } + } else { + c.logf("Handler: Actual request") + c.handleActualRequest(w, r) + next.ServeHTTP(w, r) + } + }) +} + +// handlePreflight handles pre-flight CORS requests +func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + origin := r.Header.Get("Origin") + + if r.Method != http.MethodOptions { + c.logf("Preflight aborted: %s!=OPTIONS", r.Method) + return + } + // Always set Vary headers + // see https://github.com/rs/cors/issues/10, + // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001 + headers.Add("Vary", "Origin") + headers.Add("Vary", "Access-Control-Request-Method") + headers.Add("Vary", "Access-Control-Request-Headers") + + if origin == "" { + c.logf("Preflight aborted: empty origin") + return + } + if !c.isOriginAllowed(r, origin) { + c.logf("Preflight aborted: origin '%s' not allowed", origin) + return + } + + reqMethod := r.Header.Get("Access-Control-Request-Method") + if !c.isMethodAllowed(reqMethod) { + c.logf("Preflight aborted: method '%s' not allowed", reqMethod) + return + } + reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) + if !c.areHeadersAllowed(reqHeaders) { + c.logf("Preflight aborted: headers '%v' not allowed", reqHeaders) + return + } + if c.allowedOriginsAll { + headers.Set("Access-Control-Allow-Origin", "*") + } else { + headers.Set("Access-Control-Allow-Origin", origin) + } + // Spec says: Since the list of methods can be unbounded, simply returning the method indicated + // by Access-Control-Request-Method (if supported) can be enough + headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) + if len(reqHeaders) > 0 { + + // Spec says: Since the list of headers can be unbounded, simply returning supported headers + // from Access-Control-Request-Headers can be enough + headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", ")) + } + if c.allowCredentials { + headers.Set("Access-Control-Allow-Credentials", "true") + } + if c.maxAge > 0 { + headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge)) + } + c.logf("Preflight response headers: %v", headers) +} + +// handleActualRequest handles simple cross-origin requests, actual request or redirects +func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + origin := r.Header.Get("Origin") + + // Always set Vary, see https://github.com/rs/cors/issues/10 + headers.Add("Vary", "Origin") + if origin == "" { + c.logf("Actual request no headers added: missing origin") + return + } + if !c.isOriginAllowed(r, origin) { + c.logf("Actual request no headers added: origin '%s' not allowed", origin) + return + } + + // Note that spec does define a way to specifically disallow a simple method like GET or + // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the + // spec doesn't instruct to check the allowed methods for simple cross-origin requests. + // We think it's a nice feature to be able to have control on those methods though. + if !c.isMethodAllowed(r.Method) { + c.logf("Actual request no headers added: method '%s' not allowed", r.Method) + + return + } + if c.allowedOriginsAll { + headers.Set("Access-Control-Allow-Origin", "*") + } else { + headers.Set("Access-Control-Allow-Origin", origin) + } + if len(c.exposedHeaders) > 0 { + headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", ")) + } + if c.allowCredentials { + headers.Set("Access-Control-Allow-Credentials", "true") + } + c.logf("Actual response added headers: %v", headers) +} + +// convenience method. checks if a logger is set. +func (c *Cors) logf(format string, a ...interface{}) { + if c.Log != nil { + c.Log.Printf(format, a...) + } +} + +// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests +// on the endpoint +func (c *Cors) isOriginAllowed(r *http.Request, origin string) bool { + if c.allowOriginFunc != nil { + return c.allowOriginFunc(r, origin) + } + if c.allowedOriginsAll { + return true + } + origin = strings.ToLower(origin) + for _, o := range c.allowedOrigins { + if o == origin { + return true + } + } + for _, w := range c.allowedWOrigins { + if w.match(origin) { + return true + } + } + return false +} + +// isMethodAllowed checks if a given method can be used as part of a cross-domain request +// on the endpoint +func (c *Cors) isMethodAllowed(method string) bool { + if len(c.allowedMethods) == 0 { + // If no method allowed, always return false, even for preflight request + return false + } + method = strings.ToUpper(method) + if method == http.MethodOptions { + // Always allow preflight requests + return true + } + for _, m := range c.allowedMethods { + if m == method { + return true + } + } + return false +} + +// areHeadersAllowed checks if a given list of headers are allowed to used within +// a cross-domain request. +func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool { + if c.allowedHeadersAll || len(requestedHeaders) == 0 { + return true + } + for _, header := range requestedHeaders { + header = http.CanonicalHeaderKey(header) + found := false + for _, h := range c.allowedHeaders { + if h == header { + found = true + break + } + } + if !found { + return false + } + } + return true +} diff --git a/vendor/github.com/go-chi/cors/utils.go b/vendor/github.com/go-chi/cors/utils.go new file mode 100644 index 000000000..cd24831fc --- /dev/null +++ b/vendor/github.com/go-chi/cors/utils.go @@ -0,0 +1,70 @@ +package cors + +import "strings" + +const toLower = 'a' - 'A' + +type converter func(string) string + +type wildcard struct { + prefix string + suffix string +} + +func (w wildcard) match(s string) bool { + return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix) +} + +// convert converts a list of string using the passed converter function +func convert(s []string, c converter) []string { + out := []string{} + for _, i := range s { + out = append(out, c(i)) + } + return out +} + +// parseHeaderList tokenize + normalize a string containing a list of headers +func parseHeaderList(headerList string) []string { + l := len(headerList) + h := make([]byte, 0, l) + upper := true + // Estimate the number headers in order to allocate the right splice size + t := 0 + for i := 0; i < l; i++ { + if headerList[i] == ',' { + t++ + } + } + headers := make([]string, 0, t) + for i := 0; i < l; i++ { + b := headerList[i] + if b >= 'a' && b <= 'z' { + if upper { + h = append(h, b-toLower) + } else { + h = append(h, b) + } + } else if b >= 'A' && b <= 'Z' { + if !upper { + h = append(h, b+toLower) + } else { + h = append(h, b) + } + } else if b == '-' || (b >= '0' && b <= '9') { + h = append(h, b) + } + + if b == ' ' || b == ',' || i == l-1 { + if len(h) > 0 { + // Flush the found header + headers = append(headers, string(h)) + h = h[:0] + upper = true + } + } else { + upper = b == '-' + } + } + return headers +} diff --git a/vendor/github.com/siddontang/go-snappy/AUTHORS b/vendor/github.com/siddontang/go-snappy/AUTHORS deleted file mode 100644 index 8ddb5b7a2..000000000 --- a/vendor/github.com/siddontang/go-snappy/AUTHORS +++ /dev/null @@ -1,12 +0,0 @@ -# This is the official list of Snappy-Go authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# Please keep the list sorted. - -Google Inc. -Jan Mercl <0xjnml@gmail.com> diff --git a/vendor/github.com/siddontang/go-snappy/CONTRIBUTORS b/vendor/github.com/siddontang/go-snappy/CONTRIBUTORS deleted file mode 100644 index 50b69c80e..000000000 --- a/vendor/github.com/siddontang/go-snappy/CONTRIBUTORS +++ /dev/null @@ -1,34 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the Snappy-Go repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# http://code.google.com/legal/individual-cla-v1.0.html -# http://code.google.com/legal/corporate-cla-v1.0.html -# -# The agreement for individuals can be filled out on the web. -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name - -# Please keep the list sorted. - -Jan Mercl <0xjnml@gmail.com> -Kai Backman -Marc-Antoine Ruel -Nigel Tao -Rob Pike -Russ Cox diff --git a/vendor/github.com/siddontang/go-snappy/LICENSE b/vendor/github.com/siddontang/go-snappy/LICENSE deleted file mode 100644 index 6050c10f4..000000000 --- a/vendor/github.com/siddontang/go-snappy/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/siddontang/go-snappy/snappy/decode.go b/vendor/github.com/siddontang/go-snappy/snappy/decode.go deleted file mode 100644 index d93c1b9db..000000000 --- a/vendor/github.com/siddontang/go-snappy/snappy/decode.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package snappy - -import ( - "encoding/binary" - "errors" -) - -// ErrCorrupt reports that the input is invalid. -var ErrCorrupt = errors.New("snappy: corrupt input") - -// DecodedLen returns the length of the decoded block. -func DecodedLen(src []byte) (int, error) { - v, _, err := decodedLen(src) - return v, err -} - -// decodedLen returns the length of the decoded block and the number of bytes -// that the length header occupied. -func decodedLen(src []byte) (blockLen, headerLen int, err error) { - v, n := binary.Uvarint(src) - if n == 0 { - return 0, 0, ErrCorrupt - } - if uint64(int(v)) != v { - return 0, 0, errors.New("snappy: decoded block is too large") - } - return int(v), n, nil -} - -// Decode returns the decoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire decoded block. -// Otherwise, a newly allocated slice will be returned. -// It is valid to pass a nil dst. -func Decode(dst, src []byte) ([]byte, error) { - dLen, s, err := decodedLen(src) - if err != nil { - return nil, err - } - if len(dst) < dLen { - dst = make([]byte, dLen) - } - - var d, offset, length int - for s < len(src) { - switch src[s] & 0x03 { - case tagLiteral: - x := uint(src[s] >> 2) - switch { - case x < 60: - s += 1 - case x == 60: - s += 2 - if s > len(src) { - return nil, ErrCorrupt - } - x = uint(src[s-1]) - case x == 61: - s += 3 - if s > len(src) { - return nil, ErrCorrupt - } - x = uint(src[s-2]) | uint(src[s-1])<<8 - case x == 62: - s += 4 - if s > len(src) { - return nil, ErrCorrupt - } - x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16 - case x == 63: - s += 5 - if s > len(src) { - return nil, ErrCorrupt - } - x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24 - } - length = int(x + 1) - if length <= 0 { - return nil, errors.New("snappy: unsupported literal length") - } - if length > len(dst)-d || length > len(src)-s { - return nil, ErrCorrupt - } - copy(dst[d:], src[s:s+length]) - d += length - s += length - continue - - case tagCopy1: - s += 2 - if s > len(src) { - return nil, ErrCorrupt - } - length = 4 + int(src[s-2])>>2&0x7 - offset = int(src[s-2])&0xe0<<3 | int(src[s-1]) - - case tagCopy2: - s += 3 - if s > len(src) { - return nil, ErrCorrupt - } - length = 1 + int(src[s-3])>>2 - offset = int(src[s-2]) | int(src[s-1])<<8 - - case tagCopy4: - return nil, errors.New("snappy: unsupported COPY_4 tag") - } - - end := d + length - if offset > d || end > len(dst) { - return nil, ErrCorrupt - } - for ; d < end; d++ { - dst[d] = dst[d-offset] - } - } - if d != dLen { - return nil, ErrCorrupt - } - return dst[:d], nil -} diff --git a/vendor/github.com/siddontang/go-snappy/snappy/encode.go b/vendor/github.com/siddontang/go-snappy/snappy/encode.go deleted file mode 100644 index b2371db11..000000000 --- a/vendor/github.com/siddontang/go-snappy/snappy/encode.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package snappy - -import ( - "encoding/binary" -) - -// We limit how far copy back-references can go, the same as the C++ code. -const maxOffset = 1 << 15 - -// emitLiteral writes a literal chunk and returns the number of bytes written. -func emitLiteral(dst, lit []byte) int { - i, n := 0, uint(len(lit)-1) - switch { - case n < 60: - dst[0] = uint8(n)<<2 | tagLiteral - i = 1 - case n < 1<<8: - dst[0] = 60<<2 | tagLiteral - dst[1] = uint8(n) - i = 2 - case n < 1<<16: - dst[0] = 61<<2 | tagLiteral - dst[1] = uint8(n) - dst[2] = uint8(n >> 8) - i = 3 - case n < 1<<24: - dst[0] = 62<<2 | tagLiteral - dst[1] = uint8(n) - dst[2] = uint8(n >> 8) - dst[3] = uint8(n >> 16) - i = 4 - case int64(n) < 1<<32: - dst[0] = 63<<2 | tagLiteral - dst[1] = uint8(n) - dst[2] = uint8(n >> 8) - dst[3] = uint8(n >> 16) - dst[4] = uint8(n >> 24) - i = 5 - default: - panic("snappy: source buffer is too long") - } - if copy(dst[i:], lit) != len(lit) { - panic("snappy: destination buffer is too short") - } - return i + len(lit) -} - -// emitCopy writes a copy chunk and returns the number of bytes written. -func emitCopy(dst []byte, offset, length int) int { - i := 0 - for length > 0 { - x := length - 4 - if 0 <= x && x < 1<<3 && offset < 1<<11 { - dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1 - dst[i+1] = uint8(offset) - i += 2 - break - } - - x = length - if x > 1<<6 { - x = 1 << 6 - } - dst[i+0] = uint8(x-1)<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - i += 3 - length -= x - } - return i -} - -// Encode returns the encoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire encoded block. -// Otherwise, a newly allocated slice will be returned. -// It is valid to pass a nil dst. -func Encode(dst, src []byte) ([]byte, error) { - if n := MaxEncodedLen(len(src)); len(dst) < n { - dst = make([]byte, n) - } - - // The block starts with the varint-encoded length of the decompressed bytes. - d := binary.PutUvarint(dst, uint64(len(src))) - - // Return early if src is short. - if len(src) <= 4 { - if len(src) != 0 { - d += emitLiteral(dst[d:], src) - } - return dst[:d], nil - } - - // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. - const maxTableSize = 1 << 14 - shift, tableSize := uint(32-8), 1<<8 - for tableSize < maxTableSize && tableSize < len(src) { - shift-- - tableSize *= 2 - } - var table [maxTableSize]int - - // Iterate over the source bytes. - var ( - s int // The iterator position. - t int // The last position with the same hash as s. - lit int // The start position of any pending literal bytes. - ) - for s+3 < len(src) { - // Update the hash table. - b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] - h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 - p := &table[(h*0x1e35a7bd)>>shift] - // We need to to store values in [-1, inf) in table. To save - // some initialization time, (re)use the table's zero value - // and shift the values against this zero: add 1 on writes, - // subtract 1 on reads. - t, *p = *p-1, s+1 - // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. - if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { - s++ - continue - } - // Otherwise, we have a match. First, emit any pending literal bytes. - if lit != s { - d += emitLiteral(dst[d:], src[lit:s]) - } - // Extend the match to be as long as possible. - s0 := s - s, t = s+4, t+4 - for s < len(src) && src[s] == src[t] { - s++ - t++ - } - // Emit the copied bytes. - d += emitCopy(dst[d:], s-t, s-s0) - lit = s - } - - // Emit any final pending literal bytes and return. - if lit != len(src) { - d += emitLiteral(dst[d:], src[lit:]) - } - return dst[:d], nil -} - -// MaxEncodedLen returns the maximum length of a snappy block, given its -// uncompressed length. -func MaxEncodedLen(srcLen int) int { - // Compressed data can be defined as: - // compressed := item* literal* - // item := literal* copy - // - // The trailing literal sequence has a space blowup of at most 62/60 - // since a literal of length 60 needs one tag byte + one extra byte - // for length information. - // - // Item blowup is trickier to measure. Suppose the "copy" op copies - // 4 bytes of data. Because of a special check in the encoding code, - // we produce a 4-byte copy only if the offset is < 65536. Therefore - // the copy op takes 3 bytes to encode, and this type of item leads - // to at most the 62/60 blowup for representing literals. - // - // Suppose the "copy" op copies 5 bytes of data. If the offset is big - // enough, it will take 5 bytes to encode the copy op. Therefore the - // worst case here is a one-byte literal followed by a five-byte copy. - // That is, 6 bytes of input turn into 7 bytes of "compressed" data. - // - // This last factor dominates the blowup, so the final estimate is: - return 32 + srcLen + srcLen/6 -} diff --git a/vendor/github.com/siddontang/go-snappy/snappy/snappy.go b/vendor/github.com/siddontang/go-snappy/snappy/snappy.go deleted file mode 100644 index 2f1b790d0..000000000 --- a/vendor/github.com/siddontang/go-snappy/snappy/snappy.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package snappy implements the snappy block-based compression format. -// It aims for very high speeds and reasonable compression. -// -// The C++ snappy implementation is at http://code.google.com/p/snappy/ -package snappy - -/* -Each encoded block begins with the varint-encoded length of the decoded data, -followed by a sequence of chunks. Chunks begin and end on byte boundaries. The -first byte of each chunk is broken into its 2 least and 6 most significant bits -called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. -Zero means a literal tag. All other values mean a copy tag. - -For literal tags: - - If m < 60, the next 1 + m bytes are literal bytes. - - Otherwise, let n be the little-endian unsigned integer denoted by the next - m - 59 bytes. The next 1 + n bytes after that are literal bytes. - -For copy tags, length bytes are copied from offset bytes ago, in the style of -Lempel-Ziv compression algorithms. In particular: - - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). - The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 - of the offset. The next byte is bits 0-7 of the offset. - - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). - The length is 1 + m. The offset is the little-endian unsigned integer - denoted by the next 2 bytes. - - For l == 3, this tag is a legacy format that is no longer supported. -*/ -const ( - tagLiteral = 0x00 - tagCopy1 = 0x01 - tagCopy2 = 0x02 - tagCopy4 = 0x03 -) diff --git a/vendor/modules.txt b/vendor/modules.txt index 50a603d4b..618e397f4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,6 +7,16 @@ code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.13.1 ## explicit code.gitea.io/sdk/gitea +# gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c +## explicit +gitea.com/go-chi/binding +# gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e +## explicit +gitea.com/go-chi/cache +gitea.com/go-chi/cache/memcache +# gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e +## explicit +gitea.com/go-chi/captcha # gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee ## explicit gitea.com/go-chi/session @@ -17,55 +27,11 @@ gitea.com/go-chi/session/postgres # gitea.com/lunny/levelqueue v0.3.0 ## explicit gitea.com/lunny/levelqueue -# gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e -gitea.com/lunny/log -# gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727 -gitea.com/lunny/nodb -gitea.com/lunny/nodb/config -gitea.com/lunny/nodb/store -gitea.com/lunny/nodb/store/driver -gitea.com/lunny/nodb/store/goleveldb -# gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b -## explicit -gitea.com/macaron/binding -# gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b -## explicit -gitea.com/macaron/cache -gitea.com/macaron/cache/memcache -# gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca -## explicit -gitea.com/macaron/captcha -# gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4 -## explicit -gitea.com/macaron/cors -# gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439 -## explicit -gitea.com/macaron/csrf -# gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5 -## explicit -gitea.com/macaron/gzip -# gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60 -## explicit -gitea.com/macaron/i18n -# gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a -## explicit -gitea.com/macaron/inject -# gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804 -## explicit -gitea.com/macaron/macaron -# gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee -## explicit -gitea.com/macaron/session -gitea.com/macaron/session/couchbase -gitea.com/macaron/session/memcache -gitea.com/macaron/session/mysql -gitea.com/macaron/session/nodb -gitea.com/macaron/session/postgres -# gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 -## explicit -gitea.com/macaron/toolbox # github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c github.com/Azure/go-ntlmssp +# github.com/NYTimes/gziphandler v1.1.1 +## explicit +github.com/NYTimes/gziphandler # github.com/PuerkitoBio/goquery v1.5.1 ## explicit github.com/PuerkitoBio/goquery @@ -274,6 +240,9 @@ github.com/go-asn1-ber/asn1-ber ## explicit github.com/go-chi/chi github.com/go-chi/chi/middleware +# github.com/go-chi/cors v1.1.1 +## explicit +github.com/go-chi/cors # github.com/go-enry/go-enry/v2 v2.6.0 ## explicit github.com/go-enry/go-enry/v2 @@ -709,8 +678,6 @@ github.com/shurcooL/sanitized_anchor_name # github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 ## explicit github.com/shurcooL/vfsgen -# github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d -github.com/siddontang/go-snappy/snappy # github.com/spf13/afero v1.3.2 github.com/spf13/afero github.com/spf13/afero/mem